| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354 |
- #include <time.h>
- #include <stdio.h>
- #include <signal.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <pthread.h>
- #include <arpa/inet.h>
- #include <sys/socket.h>
- #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, "<html><body>%s</body></html>", 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;
- }
|