文件服务器架构

image.png

职责定义

  • 主线程

    • 命令行输入,管理员可控制服务端工作状态
  • 监听线程

    • 服务端启动后对客户端连接进行监听的工作环境
  • 通讯线程

    • 客户端连接成功后与服务端的数据交互环境

主线程

int main(int argc, char *argv)
{
    if (argc >=2) {
        DIR *dir = opendir(argv[1]);
        if (dir != NULL) {
            close(dir);
            Run(argv[1]);
        }
    } 

    printf("Can not launch File Server, need a valid director as root.\n");

    return 0;
}
if (*line) {
    char *cmd = FormatByChar(line, ' ');  // 此处的作用主要为去除前后空格
    int done = 0;

    for (i=0; i<DIM(g_handler); ++i) {
        if (strcmp(g_handler[i].cmd, cmd) == 0) {
            g_handler[i].handler(cmd);
            done = 1;
            break;
        }
    }
    
    if (!done) {
        printf("\'%s\' can NOT be parser!\n", cmd);
    }

    free(cmd);
}

交互流程一

image.png

交互流程二

image.png

监护线程

static void *Server_Thread(void *arg)
{
    while (TcpServer_IsValid(arg)) {
        TcpClient *client = TcpServer_Accept(arg);

        if (client &&(g_status != PAUSE)) {
            pthread_t tid = 0;
            pthread_create(&tid, NULL, Process_Thread, client);
        } else {
            TcpClient_Del(client);
        }
    }

    printf("%s - Thread Exit : %p\n", __FCUNTION__, arg);
    g_status = STOP;
}

请求响应流程

image.png

通讯流程

static void DoResp(TcpClient *client)
{
    int len = TcpClient_Available(client);
    if (len > 0) {
        char *buf = malloc(len + 1);
        TcpClient_RecvRaw(client, buf, len);
        buf[len] = 0;
        char resp[255] = {0};
        // do process to create response
        TcpClient_SendRaw(client, resp, strlen(resp));
        TcpClient_Del(client);
        free(buff);
    }
}

编程实验:实现请求响应

main.c

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <time.h>
#include <fcntl.h>
#include <string.h>

#include "tcp_server.h"
#include "tcp_client.h" 
#include "utility.h"

#define BUF_SIZE 512

typedef struct {
    const char *cmd;
    void (*handler)(const char *);
}Handler;

enum {STOP, RUN, PAUSE};

static const volatile char *g_root = NULL;
static volatile int g_status = STOP;

static void DoResp(TcpClient *client)
{
    int len = TcpClient_Available(client);

    if (len > 0) {
        char *buf = malloc(len + 1);
        len = TcpClient_RecvRaw(client, buf, len);
        if (len > 0) {
            buf[len] = 0;
            printf("%s - Request : %s\n", __FUNCTION__, buf);
    
            char resp[255] = {0};
            sscanf(buf, "GET %s HTTP\n", resp);  // 提取文件路径 !!
            printf("%s - Req String: %s\n", __FUNCTION__, resp);

            char* format = "HTTP/1.1 200 OK\r\n"
                "Server:D.T. Http Server\r\n"
                "Content-Length:%d\r\n"
                "Content-Type:text/html\r\n"
                "Connection:close\r\n"
                "\r\n"
                "%s";
                       
            sprintf(resp, format, strlen((const char*)g_root), g_root);
            
            TcpClient_SendRaw(client, resp, strlen(resp));
            
            TcpClient_Del(client);
        }
        free(buf);
    }
}

static void *Process_Thread(void *arg)
{
    while (TcpClient_IsValid(arg)) {
        DoResp(arg);
    }

    printf("%s - Thread Exit : %p\n", __FUNCTION__, arg);

    return arg;
}

static void *Server_Thread(void *arg)
{
    while (TcpServer_IsValid(arg)) {
        TcpClient *client = TcpServer_Accept(arg);
        if (client && (g_status != PAUSE)) {
            pthread_t tid = 0;
            pthread_create(&tid, NULL, Process_Thread, client);
        } else {
            TcpClient_Del(client);
        }
    }

    g_status = STOP;

    return arg;
}

static void Start_Handler(const char *arg)
{
    int err = 0;

    if (g_status == STOP) {
        TcpServer *server = TcpServer_New();
        TcpServer_Start(server, 9000, 100);

        if (TcpServer_IsValid(server)) {
            pthread_t tid = 0;
            err = pthread_create(&tid, NULL, Server_Thread, server);
        } else {
            err = 1;
        }
    }

    if (!err) {
        g_status = RUN;

        printf("Server is OK!\n");
    } else {
        g_status = STOP;

        printf("Server is failed!\n");
    }
}

static void Pause_Handler(const char *arg)
{
    if (g_status == RUN) {
        g_status = PAUSE;

        printf("Server is paused!\n");
    } else {
        printf("Server is NOT started!\n");
    }
}

static void Exit_Handler(const char *arg)
{
    exit(0);
}

static Handler g_handler[] = {
    {"start", Start_Handler},
    {"pause", Pause_Handler},
    {"exit",  Exit_Handler}
};

static void Run(const char *root)
{
    printf("File Server Demo @ D.T.Software\n");

    g_root = root;

    while (1) {
        char line[BUF_SIZE] = {0};
        int i = 0;

        printf("D.T.Software @ Input >>> ");

        fgets(line, sizeof(line) - 1, stdin);

        line[strlen(line) - 1] = 0;

        if (*line) {
            char *cmd = FormatByChar(line, ' ');
            int done = 0;

            for (i-0; i<DIM(g_handler); ++i) {
                if (strcmp (g_handler[i].cmd, cmd) == 0) {
                    g_handler[i].handler(cmd);
                    done = 1;
                    break;
                }
            }

            if (!done) {
                printf("'\%s\' can NOT be parsed!\n", cmd);
            }

            free(cmd);
        }
    }
}

int main(int argc, char *argv[])
{
    if (argc >= 2) {
        DIR *dir = opendir(argv[1]);

        if (dir != NULL) {
            closedir(dir);
            Run(argv[1]);
        }
    }

    printf("Can not launch File Server, need a valid directory as roots.\n");

    return 0;
}
输出:
tiansong@tiansong:~/Desktop/network/web$ ./a.out ./
File Server Demo @ D.T.Software
D.T.Software @ Input >>> start
Server is OK!
D.T.Software @ Input >>> DoResp - Request : GET / HTTP/1.1
Host: 192.168.112.165:9000
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Edg/113.0.1774.50
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6


DoResp - Req String: /1/2/3  // 提取到的文件路径 !!
Process_Thread - Thread Exit : 0x7f77ac000c90
DoResp - Request : GET /favicon.ico HTTP/1.1
Host: 192.168.112.165:9000
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Edg/113.0.1774.50
Accept: image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8
Referer: http://192.168.112.165:9000/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6


DoResp - Req String: /favicon.ico
Process_Thread - Thread Exit : 0x7f77ac000b20
浏览器请求

image.png

思考

favivon.ico 是什么?如何处理?
如何创建浏览器中的文件展示页面?

TianSong
734 声望138 粉丝

阿里山神木的种子在3000年前已经埋下,今天不过是看到当年注定的结果,为了未来的自己,今天就埋下一颗好种子吧