فهرست منبع

add: lab8 server

RegMs If 4 سال پیش
والد
کامیت
e54a37e554
7فایلهای تغییر یافته به همراه356 افزوده شده و 22 حذف شده
  1. BIN
      lab8/server
  2. 225 19
      lab8/server.c
  3. 106 0
      lab8/sockio.c
  4. 22 0
      lab8/sockio.h
  5. 1 1
      www/html/noimg.html
  6. 2 2
      www/html/test.html
  7. BIN
      www/img/logo.jpg

BIN
lab8/server


+ 225 - 19
lab8/server.c

@@ -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;

+ 106 - 0
lab8/sockio.c

@@ -0,0 +1,106 @@
+#include "sockio.h"
+
+#include <stdio.h>
+
+// get char from client socket
+int sgetc(int clnt_sock, struct buffer *recv_buf)
+{
+    if (recv_buf->ptr == recv_buf->len)
+    {
+        ssize_t n = read(clnt_sock, recv_buf->buf, BUF_SIZE);
+        if (n == -1)
+        {
+            return -1;
+        }
+        if (n == 0) // avoid confusion with '\0'
+        {
+            return -2;
+        }
+
+        recv_buf->ptr = 0;
+        recv_buf->len = n;
+    }
+
+    return (uint8_t)recv_buf->buf[recv_buf->ptr++]; // avoid confusion with '\xff'
+}
+
+// get line from client socket
+int sgetline(int clnt_sock, struct buffer *recv_buf, struct buffer *line_buf)
+{
+    int cr = 0;
+
+    for (line_buf->len = 0;;)
+    {
+        int c = sgetc(clnt_sock, recv_buf);
+        if (c < 0)
+        {
+            return c;
+        }
+
+        line_buf->buf[line_buf->len++] = c;
+
+        if (c == '\r')
+        {
+            cr = 1;
+        }
+        else if (c == '\n' && cr)
+        {
+            break;
+        }
+        else
+        {
+            cr = 0;
+        }
+    }
+
+    line_buf->buf[line_buf->len] = '\0';
+
+    return line_buf->len;
+}
+
+// flush send buffer
+int sflush(int clnt_sock, struct buffer *send_buf)
+{
+    uint16_t len = send_buf->len;
+
+    for (; send_buf->len;)
+    {
+        ssize_t n = write(clnt_sock, send_buf->buf + len - send_buf->len, send_buf->len);
+        if (n == -1)
+        {
+            return -1;
+        }
+
+        send_buf->len -= n;
+    }
+
+    return len;
+}
+
+// put char to client socket
+int sputc(int clnt_sock, struct buffer *send_buf, char c)
+{
+    if (send_buf->len == BUF_SIZE && sflush(clnt_sock, send_buf) == -1)
+    {
+        return -1;
+    }
+
+    send_buf->buf[send_buf->len++] = c;
+
+    return (uint8_t)c; // avoid confusion with '\xff'
+}
+
+// put line to client socket
+int sputline(int clnt_sock, struct buffer *send_buf, struct buffer *line_buf)
+{
+    for (char *s = line_buf->buf; s < line_buf->buf + line_buf->len; s++)
+    {
+        int c = sputc(clnt_sock, send_buf, *s);
+        if (c == -1)
+        {
+            return -1;
+        }
+    }
+
+    return line_buf->len;
+}

+ 22 - 0
lab8/sockio.h

@@ -0,0 +1,22 @@
+#pragma once
+
+#include <unistd.h>
+#include <arpa/inet.h>
+
+const uint16_t BUF_SIZE = 4096;
+
+struct buffer
+{
+    uint16_t ptr, len;
+    char buf[BUF_SIZE];
+};
+
+int sgetc(int clnt_sock, struct buffer *recv_buf);
+
+int sgetline(int clnt_sock, struct buffer *recv_buf, struct buffer *line_buf);
+
+int sflush(int clnt_sock, struct buffer *send_buf);
+
+int sputc(int clnt_sock, struct buffer *send_buf, char c);
+
+int sputline(int clnt_sock, struct buffer *send_buf, struct buffer *line_buf);

+ 1 - 1
www/html/noimg.html

@@ -6,7 +6,7 @@
 
 <body>
     <h1>This is a test</h1>
-    <form action="dopost" method="POST">
+    <form action="/dopost" method="POST">
         Login:<input name="login">
         Pass:<input name="pass">
         <input type="submit" value="login">

+ 2 - 2
www/html/test.html

@@ -6,8 +6,8 @@
 
 <body>
     <h1>This is a test</h1>
-    <img src="img/logo.jpg">
-    <form action="dopost" method="POST">
+    <img src="/img/logo.jpg">
+    <form action="/dopost" method="POST">
         Login:<input name="login">
         Pass:<input name="pass">
         <input type="submit" value="login">

BIN
www/img/logo.jpg