Created
November 27, 2018 10:08
-
-
Save yzyzsun/52ed1799b24bda08f3c0ae534356a99a to your computer and use it in GitHub Desktop.
Simple Web Server
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <string.h> | |
#include <pthread.h> | |
#include <sys/socket.h> | |
#include <arpa/inet.h> | |
#define SERV_PORT 4746 | |
#define BACKLOG 10 | |
#define DATA_LEN 100000 | |
#define METHOD_LEN 8 | |
#define TYPE_LEN 20 | |
#define PATH_LEN 100 | |
#define FIELD_LEN 100 | |
void not_found(int client) { | |
char buf[] = "HTTP/1.1 404 Not Found\r\nContent-Type: text/html\r\nContent-Length: 9\r\n\r\nNot Found"; | |
send(client, buf, strlen(buf), 0); | |
} | |
void not_implemented(int client) { | |
char buf[] = "HTTP/1.1 501 Not Implemented\r\nContent-Type: text/html\r\nContent-Length: 15\r\n\r\nNot Implemented"; | |
send(client, buf, strlen(buf), 0); | |
} | |
void *worker(void *arg) { | |
int client = *(int *)arg; | |
for (;;) { | |
char request[DATA_LEN]; | |
int len = recv(client, request, sizeof(request), 0); | |
request[len] = 0; | |
if (len <= 0) { | |
shutdown(client, SHUT_RDWR); | |
return 0; | |
} | |
char method[METHOD_LEN]; | |
char path[PATH_LEN] = "public"; | |
sscanf(request, "%s%s", method, path + strlen(path)); | |
if (path[strlen(path) - 1] == '/') strcat(path, "index.html"); | |
printf("%s %s\n", method, path); | |
if (strcmp(method, "GET") == 0) { | |
FILE *fp = fopen(path, "r"); | |
if (fp) { | |
char buf[DATA_LEN] = "HTTP/1.1 200 OK\r\n"; | |
if (strcmp(path + strlen(path) - 4, ".jpg") == 0) { | |
strcat(buf, "Content-Type: image/jpeg\r\n"); | |
} else { | |
strcat(buf, "Content-Type: text/html\r\n"); | |
} | |
fseek(fp, 0, SEEK_END); | |
long fsize = ftell(fp); | |
rewind(fp); | |
sprintf(buf + strlen(buf), "Content-Length: %ld\r\n\r\n", fsize); | |
int offset = strlen(buf); | |
fread(buf + offset, 1, fsize, fp); | |
send(client, buf, offset + fsize, 0); | |
fclose(fp); | |
} else { | |
not_found(client); | |
} | |
} else if (strcmp(method, "POST") == 0) { | |
if (strcmp(path, "public/dopost") == 0) { | |
char buf[DATA_LEN] = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n"; | |
char *body = strstr(request, "\r\n\r\n"); | |
body += 4; | |
char login[FIELD_LEN], pass[FIELD_LEN]; | |
sscanf(body, "login=%[^&]&pass=%s", login, pass); | |
printf("Login attempt using %s:%s\n", login, pass); | |
char *response; | |
if (strcmp(login, "3140104746") == 0 && strcmp(pass, "4746") == 0) { | |
response = "Login succeeded"; | |
} else { | |
response = "Login failed"; | |
} | |
sprintf(buf + strlen(buf), "Content-Length: %lu\r\n\r\n%s", strlen(response), response); | |
send(client, buf, strlen(buf), 0); | |
} else { | |
not_found(client); | |
} | |
} else { | |
not_implemented(client); | |
} | |
} | |
} | |
int main(int argc, char *argv[]) { | |
int sock = socket(PF_INET, SOCK_STREAM, 0); | |
struct sockaddr_in servaddr; | |
memset(&servaddr, 0, sizeof(servaddr)); | |
servaddr.sin_family = AF_INET; | |
servaddr.sin_addr.s_addr = INADDR_ANY; | |
servaddr.sin_port = htons(SERV_PORT); | |
bind(sock, (const struct sockaddr *)&servaddr, sizeof(servaddr)); | |
listen(sock, BACKLOG); | |
printf("HTTP Server running on port %d\n", SERV_PORT); | |
for (;;) { | |
int client = accept(sock, NULL, NULL); | |
if (client < 0) continue; | |
struct sockaddr_in connaddr; | |
socklen_t connaddr_len = sizeof(connaddr); | |
getpeername(client, (struct sockaddr *)&connaddr, &connaddr_len); | |
printf("Connection from %s:%hu\n", | |
inet_ntoa(connaddr.sin_addr), ntohs(connaddr.sin_port)); | |
pthread_t tid; | |
pthread_create(&tid, NULL, worker, &client); | |
} | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <pthread.h> | |
#include <sys/socket.h> | |
#include <arpa/inet.h> | |
#include "tcp_common.h" | |
void *worker(void *arg) { | |
int sockfd = *(int *)arg; | |
for (;;) { | |
char reply[DATA_LEN]; | |
ssize_t length = recv(sockfd, reply, sizeof(reply), 0); | |
if (length <= 0) { | |
puts("Lose connection to server"); | |
shutdown(sockfd, SHUT_RDWR); | |
return 0; | |
} | |
while (reply[strlen(reply) - 1] == '\n') reply[strlen(reply) - 1] = 0; | |
switch (reply[0]) { | |
case TYPE_MSG: { | |
int clino, offset; | |
sscanf(reply + 1, "%d\n%n", &clino, &offset); | |
offset++; | |
printf("--- Message From No.%d ---\n", clino); | |
puts(reply + offset); | |
puts("--- Message End ---"); | |
break; | |
} | |
case TYPE_SUC: | |
puts("--- Server Reply Begin ---"); | |
puts(reply + 1); | |
puts("--- Server Reply End ---"); | |
break; | |
case TYPE_ERR: | |
puts("--- Server Error Begin ---"); | |
puts(reply + 1); | |
puts("--- Server Error End ---"); | |
break; | |
} | |
} | |
} | |
int request(int sockfd, char type, char *msg) { | |
char data[strlen(msg) + 2]; | |
data[0] = type; | |
data[1] = 0; | |
strcat(data, msg); | |
return send(sockfd, data, sizeof(data), 0); | |
} | |
int main(int argc, char *argv[]) { | |
int sockfd = socket(PF_INET, SOCK_STREAM, 0); | |
struct sockaddr_in servaddr; | |
memset(&servaddr, 0, sizeof(servaddr)); | |
for (;;) { | |
if (servaddr.sin_family) { | |
printf("You have connected to %s:%hd. Please select one option from the list below:\n", inet_ntoa(servaddr.sin_addr), ntohs(servaddr.sin_port)); | |
puts("1. Disconnect from the server."); | |
puts("2. Retrieve the server time."); | |
puts("3. Retrieve the server name."); | |
puts("4. Retrieve the list of clients."); | |
puts("5. Send a message."); | |
puts("6. Quit."); | |
int number; | |
scanf("%d", &number); | |
switch (number) { | |
case 1: | |
shutdown(sockfd, SHUT_RDWR); | |
sockfd = socket(PF_INET, SOCK_STREAM, 0); | |
memset(&servaddr, 0, sizeof(servaddr)); | |
break; | |
case 2: | |
request(sockfd, TYPE_TIME, ""); | |
break; | |
case 3: | |
request(sockfd, TYPE_NAME, ""); | |
break; | |
case 4: | |
request(sockfd, TYPE_LIST, ""); | |
break; | |
case 5: { | |
while (getchar() != '\n'); | |
char msg[DATA_LEN]; | |
puts("Enter the number of client to which you want to send the message:"); | |
fgets(msg, DATA_LEN, stdin); | |
puts("Enter the message to send:"); | |
fgets(msg + strlen(msg), DATA_LEN - strlen(msg), stdin); | |
request(sockfd, TYPE_SEND, msg); | |
break; | |
} | |
default: | |
shutdown(sockfd, SHUT_RDWR); | |
return 0; | |
} | |
} else { | |
puts("You have not connected to a server. Please select one option from the list below:"); | |
puts("1. Connect."); | |
puts("2. Quit."); | |
int number; | |
scanf("%d", &number); | |
switch (number) { | |
case 1: | |
puts("Enter the IP address and port number: (e.g. 127.0.0.1:80)"); | |
char ipaddr[IPADDR_LEN]; | |
in_port_t port; | |
scanf("\n%[^:]:%hu", ipaddr, &port); | |
servaddr.sin_family = PF_INET; | |
inet_aton(ipaddr, &servaddr.sin_addr); | |
servaddr.sin_port = htons(port); | |
int ret = connect(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)); | |
if (ret == 0) { | |
pthread_t tid; | |
pthread_create(&tid, NULL, worker, &sockfd); | |
} else { | |
memset(&servaddr, 0, sizeof(servaddr)); | |
puts("Connection failed"); | |
} | |
break; | |
default: | |
return 0; | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#define TYPE_TIME 100 | |
#define TYPE_NAME 101 | |
#define TYPE_LIST 102 | |
#define TYPE_SEND 103 | |
#define TYPE_SUC 104 | |
#define TYPE_ERR 105 | |
#define TYPE_MSG 106 | |
#define IPADDR_LEN 16 | |
#define DATA_LEN 1024 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdbool.h> | |
#include <string.h> | |
#include <time.h> | |
#include <pthread.h> | |
#include <sys/utsname.h> | |
#include <sys/socket.h> | |
#include <arpa/inet.h> | |
#include "tcp_common.h" | |
#define SERV_PORT 4746 | |
#define BACKLOG 10 | |
struct connection { | |
pthread_t tid; | |
int sock; | |
char ipaddr[IPADDR_LEN]; | |
in_port_t port; | |
bool connected; | |
} conn[BACKLOG]; | |
int conn_count = 0; | |
void *worker(void *arg) { | |
int index = *(int *)arg; | |
int connfd = conn[index].sock; | |
for (;;) { | |
char request[DATA_LEN]; | |
int length = recv(connfd, request, sizeof(request), 0); | |
if (length <= 0) { | |
conn[index].connected = false; | |
shutdown(connfd, SHUT_RDWR); | |
return 0; | |
} | |
char reply[DATA_LEN]; | |
char type = request[0]; | |
switch (type) { | |
case TYPE_TIME: { | |
reply[0] = TYPE_SUC; | |
time_t t = time(NULL); | |
strcpy(reply + 1, ctime(&t)); | |
break; | |
} | |
case TYPE_NAME: { | |
reply[0] = TYPE_SUC; | |
struct utsname sysinfo; | |
uname(&sysinfo); | |
strcpy(reply + 1, sysinfo.nodename); | |
break; | |
} | |
case TYPE_LIST: { | |
reply[0] = TYPE_SUC; | |
int offset; | |
sprintf(reply + 1, "No.\tIP address\tPort\tConnected\n%n", &offset); | |
offset++; | |
for (int i = 0; i < conn_count; i++) { | |
int count; | |
sprintf(reply + offset, "%d\t%s\t%hu\t%d\n%n", | |
i, conn[i].ipaddr, conn[i].port, conn[i].connected, &count); | |
offset += count; | |
} | |
break; | |
} | |
case TYPE_SEND: { | |
int clino, offset; | |
sscanf(request + 1, "%d\n%n", &clino, &offset); | |
offset++; | |
if (clino < conn_count) { | |
char msg[DATA_LEN]; | |
msg[0] = TYPE_MSG; | |
sprintf(msg + 1, "%d\n%s", index, request + offset); | |
ssize_t ret = send(conn[clino].sock, msg, strlen(msg) + 1, 0); | |
if (ret > 0) { | |
reply[0] = TYPE_SUC; | |
strcpy(reply + 1, "Sent successfully"); | |
} else { | |
reply[0] = TYPE_ERR; | |
strcpy(reply + 1, "Sending failed"); | |
} | |
} else { | |
reply[0] = TYPE_ERR; | |
strcpy(reply + 1, "Client number not found"); | |
} | |
break; | |
} | |
} | |
send(connfd, reply, strlen(reply) + 1, 0); | |
} | |
} | |
int main(int argc, char *argv[]) { | |
int sockfd = socket(PF_INET, SOCK_STREAM, 0); | |
struct sockaddr_in servaddr; | |
memset(&servaddr, 0, sizeof(servaddr)); | |
servaddr.sin_family = AF_INET; | |
servaddr.sin_addr.s_addr = INADDR_ANY; | |
servaddr.sin_port = htons(SERV_PORT); | |
bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)); | |
listen(sockfd, BACKLOG); | |
for (;;) { | |
int connfd = accept(sockfd, NULL, NULL); | |
if (connfd < 0) continue; | |
struct sockaddr_in connaddr; | |
socklen_t connaddr_len = sizeof(connaddr); | |
getpeername(connfd, (struct sockaddr *)&connaddr, &connaddr_len); | |
int index = conn_count; | |
conn[index].sock = connfd; | |
strcpy(conn[index].ipaddr, inet_ntoa(connaddr.sin_addr)); | |
conn[index].port = ntohs(connaddr.sin_port); | |
conn[index].connected = true; | |
printf("Connection from %s:%hu\n", conn[index].ipaddr, conn[index].port); | |
pthread_create(&conn[index].tid, NULL, worker, &index); | |
conn_count++; | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment