需求分析

  • 服务端启动时指定某本地文件夹作为网络共享文件夹
  • 共享文件夹中的所有文件及子文件夹均可被客户端访问
  • 使用浏览器作为客户端,在浏览器中可“看到”所有共享文件
  • 用户可在浏览器中将目标文件下载到本地
  • 服务端可暂定服务,但不影响正在下载目标文件的客户端

技术可行性分析

  • 服务端网络通讯

    • 基于 Http 协议与客户端浏览器通讯
    • 可使用 tcp socket 实现对 Http 协议的支持
  • 服务端文件操作

    • 获取文件夹中的所有文件及子文件夹
    • 返回文件列表 或 返回文件内容

系统概要设计

image.png

基础设施

  • Socket - Http 协议以 TCP 协议为底座
  • Thread - 同时对多个客户端服务(请求响应)
  • File - 文件夹信息读取,文件属性读取,文件数据读取

基础设施一 (Socket)

  • 思考:能否使用课程中的网络通讯框架作为基础设施?
  • 答:暂时不能

    • 课程中通讯框架的服务端基于 select 机制,即:

      • 服务端工作时会导致程序阻塞 (程序无法继续向下执行)
      • 每次只服务一个客户端(当服务时间过长,会影响其它服务端通讯)

文件服务器工作模型

image.png

网络通讯框架改进方案

  • 新增服务端函数

    • TcpClient *TcpServer_Accept(TcpServer *server);

      • 阻塞函数,调用后一直等待客户端连接
      • 返回与客户端通信的 TcpClient 结构体
      • 返回的 TcpClient 结构体相互独立,并且独立于服务端
TcpClient *TcpServer_Accept(TcpServer *server)
{
    TcpClient *ret = NULL;
    
    if (server) {
        Server *s = (Server*)server;
        struct sockaddr_in caddr = {0};
        socklen_t asize =sizeof(caddr); 
        int fd = accept(s->fd, (struct sockaddr*)&caddr, &asize);

        if (fd > -1) {
            ret = TcpClient_From(fd);
            if (!ret) close(fd);
        }
    }

    return ret;
}

基础设施二 (Thread)

  • 进程:应用程序的一次加载执行(系统进行资源分配的基本单位)
  • 线程:进程中的程序执行流

    • 一个进程中可以存在多个线程 (至少存在一个线程)
    • 每个线程执行不同的任务(多个线程可并行执行)
    • 同一个进程中的多个线程共享进程的系统资源

    ## Linux 多线程API

  • 头文件: #include <pthread.h>
  • 线程创建函数:
int pthread_create(pthread_t *thread, 
                   const pthread_attr_t *attr,
                   void *(start_routine)(void*),
                   void *arg);

thread: pthread_t 变量的地址,用于返回线程标识
attr: 线程的属性,可设置为 NULL,即:使用默认属性
start_routine: 线程入口函数
arg: 线程入口函数参数
  • 线程标识

    • pthread_t pthread_self(void);
    • 获取当前线程的 ID 标识

    线程等待

  • int pthread_join(pthread_t thread, void **retval);
  • 等待目标线程执行结束

多线程编程示例

void *thread_entry(void *arg)
{
    pthread_t id = pthread_self();
    int n = (long)(arg);
    int i = 0;

    for (i=0; i<n; ++i) {
        printf("tid = %ld, i = %d\n", id, i);
        sleep(1);
    }

    return arg;
}

int main()
{
    pthread_t t1 = 0;
    pthread_t t2 = 0;
    int arg1 = 5;
    int arg2 = 10;

    printf("create thread...\n");

    pthread_create(&t1, NULL, thread_entry, (void*)arg1);
    
    pthread_create(&t2, NULL, thread_entry, (void*)arg2);

    printf("t1 = %ld\n", t1);
    printf("t2 = %ld\n", t2);

    pthread_join(t1, NULL);
    pthread_join(t1, NULL);

    printf("child thread is finished...\n");
}

基础设施三 (Dir)

  • #include <sys/stat.h>
  • #include <dirent.h>
  • DIR *opendir(const char *name);
  • struct dirent *readdir(DIR *dirp);
  • int closedir(DIR *dirp);
  • int stat(const char *file_name, struct stat *buf);

文件夹编程示例

DIR *dirp = opendir(".");
if (dirp != NULL) {
    struct dirent *dp = NULL;
    while ((dp = readdir(dirp)) != NULL) {
        struct stat sb = {0};
        if (stat(dp->d_name, &sb) != -1) {
            printf("name: %s, type: %d, len: %ld, mtime: %s", 
                    dp->d_name, dp->d_type, sb.st_size, ctime(&sb.st_mtime));
        }
    }
}

基础设施四 (File)

  • #include <fcntl.h>
  • int open(const char *pathname, int flags, mode_t mode);
  • sszie_t read(int fd, void *buf, size_t count);
  • ssize_t write(int fd, const void *buf, size_t count);
  • int close(int fd);

文件编程示例

void file_copy(const char *dst, const char *src)
{
    int dfd = open(dst, O_WRONLY|O_CREAT, 0600);
    int sfd = open(src, O_RDONLY);

    if ((dfd != -1) && (sfd != -1)) {
        char buf[512] = {0};
        int len = 0;

        while ((len = read(sfd, buf, sizeof(buf))) > 0) {
            write(dfd, buf, len);
        }
    }

    close(dfd);
    close(sfd);
}

编程实验:前期技术准备

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 "tcp_server.h"
#include "tcp_client.h" 

void func1()
{
    TcpServer *s = TcpServer_New();

    TcpServer_Start(s, 9000, 100);

    if (TcpServer_IsValid(s)) {
        char buf[64] = {0};
        int len = 0;
        TcpClient *c = TcpServer_Accept(s);

        if (c) {
            len = TcpClient_RecvRaw(c, buf, sizeof(buf) - 1);
            buf[len] = 0;
            printf("recv = %s\n", buf);

            TcpClient_SendRaw(c, buf, len);
            TcpClient_Del(c);
        }
        TcpServer_Del(s);
    }
}

void *thread_entry(void *arg)
{
    pthread_t id = pthread_self();
    int n = (long)(arg);
    int i = 0;

    for (i=0; i<n; ++i) {
        printf("tid = %ld, i = %d\n", id, i);
        sleep(1);
    }

    return arg;
}

void func2()
{
    pthread_t t1 = 0;
    pthread_t t2 = 0;
    int arg1 = 5;
    int arg2 = 10;

    printf("create thread...\n");

    pthread_create(&t1, NULL, thread_entry, (void*)arg1);
    
    pthread_create(&t2, NULL, thread_entry, (void*)arg2);

    printf("t1 = %ld\n", t1);
    printf("t2 = %ld\n", t2);

    pthread_join(t1, NULL);
    pthread_join(t1, NULL);

    printf("child thread is finished...\n");
}

void func3()
{
    DIR *dirp = opendir(".");
    if (dirp != NULL) {
        struct dirent *dp = NULL;
        while ((dp = readdir(dirp)) != NULL) {
            struct stat sb = {0};
            if (stat(dp->d_name, &sb) != -1) {
                printf("name: %s, type: %d, len: %ld, mtime: %s", 
                        dp->d_name, dp->d_type, sb.st_size, ctime(&sb.st_mtime));
            }
        }
    }
}

void file_copy(const char *dst, const char *src)
{
    int dfd = open(dst, O_WRONLY|O_CREAT, 0600);
    int sfd = open(src, O_RDONLY);

    if ((dfd != -1) && (sfd != -1)) {
        char buf[512] = {0};
        int len = 0;

        while ((len = read(sfd, buf, sizeof(buf))) > 0) {
            write(dfd, buf, len);
        }
    }

    close(dfd);
    close(sfd);
}

int main(void)
{
    printf("=====================\n");

    func1();

    printf("=====================\n");

    func2();

    printf("=====================\n");

    func3();

    printf("=====================\n");

    file_copy("new.out", "a.out");

    return 0;
}
输出
=====================
recv = http://www.cmsoft.cn
=====================
create thread...
t1 = 140155835639552
t2 = 140155827246848
tid = 140155835639552, i = 0
tid = 140155827246848, i = 0
tid = 140155835639552, i = 1
tid = 140155827246848, i = 1
tid = 140155835639552, i = 2
tid = 140155827246848, i = 2
tid = 140155835639552, i = 3
tid = 140155827246848, i = 3
tid = 140155835639552, i = 4
tid = 140155827246848, i = 4
tid = 140155827246848, i = 5
child thread is finished...
=====================
name: utility.c, type: 8, len: 1875, mtime: Sun Jun  4 18:34:38 2023
name: server_task.o, type: 8, len: 6872, mtime: Sun Jun  4 18:34:38 2023
name: .., type: 4, len: 4096, mtime: Sun Jun  4 18:34:29 2023
name: tcp_server.c, type: 8, len: 4978, mtime: Sun Jun  4 19:29:28 2023
name: main.c, type: 8, len: 2353, mtime: Sun Jun  4 20:01:53 2023
name: udp_point.h, type: 8, len: 860, mtime: Sun Jun  4 18:34:38 2023
name: ., type: 4, len: 4096, mtime: Sun Jun  4 20:02:37 2023
name: tcp_client.h, type: 8, len: 932, mtime: Sun Jun  4 18:34:38 2023
name: new.out, type: 8, len: 29136, mtime: Sun Jun  4 20:02:18 2023
name: message.h, type: 8, len: 482, mtime: Sun Jun  4 18:34:38 2023
name: tcp_server.o, type: 8, len: 5864, mtime: Sun Jun  4 19:32:19 2023
name: msg_parser.o, type: 8, len: 3960, mtime: Sun Jun  4 19:32:19 2023
name: msg_parser.c, type: 8, len: 4218, mtime: Sun Jun  4 18:34:38 2023
name: a.out, type: 8, len: 29136, mtime: Sun Jun  4 20:02:37 2023
name: tcp_client.c, type: 8, len: 3825, mtime: Sun Jun  4 18:34:38 2023
name: msg_parser.h, type: 8, len: 327, mtime: Sun Jun  4 18:34:38 2023
name: utility.o, type: 8, len: 2752, mtime: Sun Jun  4 19:32:19 2023
name: tcp_server.h, type: 8, len: 770, mtime: Sun Jun  4 19:29:33 2023
name: message.o, type: 8, len: 2608, mtime: Sun Jun  4 19:32:19 2023
name: msg_def.h, type: 8, len: 134, mtime: Sun Jun  4 18:34:38 2023
name: tcp_client.o, type: 8, len: 5472, mtime: Sun Jun  4 19:32:19 2023
name: message.c, type: 8, len: 1202, mtime: Sun Jun  4 18:34:38 2023
name: utility.h, type: 8, len: 1539, mtime: Sun Jun  4 18:34:38 2023
name: udp_point.c, type: 8, len: 4793, mtime: Sun Jun  4 18:34:38 2023
name: Makefile, type: 8, len: 274, mtime: Sun Jun  4 18:34:38 2023
name: udp_point.o, type: 8, len: 6232, mtime: Sun Jun  4 19:32:19 2023
name: main.o, type: 8, len: 5592, mtime: Sun Jun  4 20:01:56 2023
=====================

TianSong
734 声望138 粉丝

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