|
|
@@ -8,52 +8,247 @@
|
|
|
#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 uint16_t BUF_SIZE = 256;
|
|
|
+
|
|
|
+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
|
|
|
-struct buffer
|
|
|
-{
|
|
|
- uint16_t ptr, len;
|
|
|
- char buf[BUF_SIZE];
|
|
|
-} clnt_buf[MAX_CLIENT_NUM]; // client buffers
|
|
|
|
|
|
-// get char from client socket
|
|
|
-int sgetc(uint16_t clnt_id)
|
|
|
+// test string prefix
|
|
|
+int strprefix(const char *s, const char *prefix)
|
|
|
{
|
|
|
- struct buffer *b = &clnt_buf[clnt_id];
|
|
|
-
|
|
|
- if (b->ptr == b->len)
|
|
|
+ for (; *s && *prefix; s++, prefix++)
|
|
|
{
|
|
|
- if (b->len == BUF_SIZE)
|
|
|
+ if (*s != *prefix)
|
|
|
{
|
|
|
- b->ptr = b->len = 0;
|
|
|
+ return 0;
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- ssize_t n = read(clnt_socks[clnt_id], b->buf, BUF_SIZE - b->len);
|
|
|
- if (n == -1)
|
|
|
+ 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 -1;
|
|
|
+ return 0;
|
|
|
}
|
|
|
- if (n == 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 -2;
|
|
|
+ return 0;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- return b->buf[b->ptr++];
|
|
|
+ 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);
|
|
|
+}
|
|
|
+
|
|
|
+// 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';
|
|
|
+ }
|
|
|
+
|
|
|
+ printf("Client %hu: method = %s, url = %s, protocol = %s.\n", clnt_id, method, url, protocol);
|
|
|
+
|
|
|
+ // request headers
|
|
|
+ int 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: content length = %d.\n", clnt_id, 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);
|
|
|
@@ -63,6 +258,7 @@ void *conn(void *id)
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
+// close sockets before exit
|
|
|
void stop_server(int sig)
|
|
|
{
|
|
|
for (uint16_t i = 0; i < MAX_CLIENT_NUM; i++)
|
|
|
@@ -83,8 +279,11 @@ void stop_server(int sig)
|
|
|
|
|
|
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)
|
|
|
{
|
|
|
@@ -92,6 +291,7 @@ int main()
|
|
|
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;
|
|
|
@@ -103,17 +303,20 @@ int main()
|
|
|
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);
|
|
|
@@ -123,6 +326,7 @@ int main()
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
+ // allocate client id
|
|
|
for (uint16_t i = 0; i < MAX_CLIENT_NUM; i++)
|
|
|
{
|
|
|
if (clnt_socks[i] == -1)
|
|
|
@@ -130,6 +334,7 @@ int main()
|
|
|
clnt_socks[i] = clnt_sock;
|
|
|
clnt_addrs[i] = clnt_addr;
|
|
|
|
|
|
+ // create thread
|
|
|
pthread_t pid;
|
|
|
if (pthread_create(&pid, NULL, conn, &i) != 0)
|
|
|
{
|
|
|
@@ -137,6 +342,7 @@ int main()
|
|
|
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;
|