#include #include #include #include #include #include #include #include #include #include "sockio.h" const uint16_t SERVER_PORT = 1092; const uint16_t MAX_CLIENT_NUM = 20; const char WWW_PATH[] = "../www"; const char USERNAME[] = "3190101092"; const char PASSWORD[] = "1092"; int serv_sock; // server socket int clnt_socks[MAX_CLIENT_NUM]; // client sockets struct sockaddr_in clnt_addrs[MAX_CLIENT_NUM]; // client addresses // test string prefix int strprefix(const char *s, const char *prefix) { for (; *s && *prefix; s++, prefix++) { if (*s != *prefix) { return 0; } } if (*prefix) { return 0; } return 1; } // test string suffix int strsuffix(const char *s, const char *suffix) { int s_len = strlen(s), suffix_len = strlen(suffix); if (s_len < suffix_len) { return 0; } for (s += s_len - suffix_len; *suffix; s++, suffix++) { if (*s != *suffix) { return 0; } } return 1; } // test object key-value pair int strmatch(const char *s, const char *key, const char *value) { int key_len = strlen(key); const char *p = strstr(s, key); if (p == NULL || p[key_len] != '=') { return 0; } for (p += key_len + 1; *p && *p != '&' && *value; p++, value++) { if (*p != *value) { return 0; } } if (*p && *p != '&' || *value) { return 0; } return 1; } // get content type from filename const char *getcontenttype(const char *s) { if (strsuffix(s, ".html")) { return "text/html"; } else if (strsuffix(s, ".jpg")) { return "image/jpeg"; } return "text/plain"; } // send http header void send_header(int clnt_sock, struct buffer *send_buf, struct buffer *line_buf, const char *protocol, const char *status, const char *content_type, size_t content_length) { line_buf->len = sprintf(line_buf->buf, "%s %s\r\n" "Content-Type: %s; charset=utf-8\r\n" "Content-Length: %lu\r\n" "\r\n", protocol, status, content_type, content_length); sputline(clnt_sock, send_buf, line_buf); printf("\tresponse: protocol = %s, status = %s, content type = %s, content length = %lu.\n", protocol, status, content_type, content_length); } // handle connection void *conn(void *id) { uint16_t clnt_id = *(uint16_t *)id; int clnt_sock = clnt_socks[clnt_id]; struct buffer recv_buf, send_buf, line_buf, body_buf; memset(&recv_buf, 0, sizeof(recv_buf)); memset(&send_buf, 0, sizeof(send_buf)); memset(&line_buf, 0, sizeof(line_buf)); memset(&body_buf, 0, sizeof(body_buf)); char method[32], url[1024], protocol[32]; for (;;) { // request line int n = sgetline(clnt_sock, &recv_buf, &line_buf); if (n < 0) { break; } { char *s = line_buf.buf, *p = method; for (; *s != ' '; s++, p++) { *p = *s; } *p = '\0'; s++, p = url; for (; *s != ' '; s++, p++) { *p = *s; } *p = '\0'; s++, p = protocol; for (; *s != '\r'; s++, p++) { *p = *s; } *p = '\0'; } // request headers size_t content_length = 0; for (;;) { n = sgetline(clnt_sock, &recv_buf, &line_buf); if (n <= 2) { break; } if (strprefix(line_buf.buf, "Content-Length: ")) { for (char *s = line_buf.buf + strlen("Content-Length: "); *s != '\r'; s++) { content_length = content_length * 10 + *s - '0'; } } } if (n < 0) { break; } printf("Client %hu request: method = %s, url = %s, protocol = %s, content length = %lu.\n", clnt_id, method, url, protocol, content_length); // request body char *request_body = (char *)malloc(content_length + 1); for (char *s = request_body; s < request_body + content_length; s++) { *s = sgetc(clnt_sock, &recv_buf); } request_body[content_length] = '\0'; if (strcmp(method, "GET") == 0) { FILE *fp = fopen(url + 1, "rb"); if (fp == NULL) { body_buf.len = sprintf(body_buf.buf, "The requested file %s was not found on this server", url); send_header(clnt_sock, &send_buf, &line_buf, protocol, "404 Not Found", "text/plain", body_buf.len); sputline(clnt_sock, &send_buf, &body_buf); } else { fseek(fp, 0, SEEK_END); send_header(clnt_sock, &send_buf, &line_buf, protocol, "200 OK", getcontenttype(url), ftell(fp)); fseek(fp, 0, SEEK_SET); for (;;) { body_buf.len = fread(body_buf.buf, 1, BUF_SIZE, fp); if (body_buf.len == 0) { break; } sputline(clnt_sock, &send_buf, &body_buf); } } } else if (strcmp(method, "POST") == 0) { if (strcmp(url, "/dopost") == 0) { int login = strmatch(request_body, "login", USERNAME) && strmatch(request_body, "pass", PASSWORD); body_buf.len = sprintf(body_buf.buf, "%s", login ? "登录成功" : "登录失败"); send_header(clnt_sock, &send_buf, &line_buf, protocol, "200 OK", "text/html", body_buf.len); sputline(clnt_sock, &send_buf, &body_buf); } else { body_buf.len = sprintf(body_buf.buf, "The requested URL %s was not found on this server", url); send_header(clnt_sock, &send_buf, &line_buf, protocol, "404 Not Found", "text/plain", body_buf.len); sputline(clnt_sock, &send_buf, &body_buf); } } free(request_body); sflush(clnt_sock, &send_buf); if (strcmp(method, "POST") == 0) { break; } } close(clnt_sock); printf("Client %hu disconnected.\n", clnt_id); clnt_socks[clnt_id] = -1; return NULL; } // close sockets before exit void stop_server(int sig) { for (uint16_t i = 0; i < MAX_CLIENT_NUM; i++) { if (clnt_socks[i] != -1) { close(clnt_socks[i]); printf("Client %hu disconnected.\n", i); clnt_socks[i] = -1; } } close(serv_sock); puts("Stop listening."); exit(0); } int main() { chdir(WWW_PATH); memset(clnt_socks, -1, sizeof(clnt_socks)); // create server socket serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (serv_sock == -1) { puts("Create socket failed."); exit(1); } // bind address to server socket struct sockaddr_in serv_addr; memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(SERVER_PORT); if (bind(serv_sock, (const struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) { puts("Bind socket failed."); exit(1); } // listen on server socket if (listen(serv_sock, MAX_CLIENT_NUM) == -1) { puts("Listen socket failed."); exit(1); } puts("Start listening on port 1092..."); signal(SIGINT, stop_server); for (;;) { // accept connection struct sockaddr_in clnt_addr; socklen_t clnt_addr_size = sizeof(clnt_addr); int clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_addr, &clnt_addr_size); if (clnt_sock == -1) { puts("Create socket failed."); continue; } // allocate client id for (uint16_t i = 0; i < MAX_CLIENT_NUM; i++) { if (clnt_socks[i] == -1) { clnt_socks[i] = clnt_sock; clnt_addrs[i] = clnt_addr; // create thread pthread_t pid; if (pthread_create(&pid, NULL, conn, &i) != 0) { puts("Create thread failed."); clnt_socks[i] = -1; break; } printf("Client %hu (%s:%hu) connected. pid = %lu.\n", i, inet_ntoa(clnt_addr.sin_addr), ntohs(clnt_addr.sin_port), pid); break; } } } return 0; }