• 项目复习:基于TCP的文件服务器


     ser.c(服务器):

    1. #include "./fun.h"
    2. int main(int argc,const char * argv[])
    3. {
    4. //1.判断入参
    5. if(argc!=3){
    6. fprintf(stderr,"入参为空,请检查\n");
    7. return -1;
    8. }
    9. //端口号转整型
    10. int port=atoi(argv[2]);
    11. //变量声明
    12. struct sockaddr_in sin;
    13. int sinLen=sizeof(sin);
    14. struct sockaddr_in cin;
    15. int cinLen=sizeof(cin);
    16. Info_t info;
    17. int newfd;
    18. pthread_t tid;
    19. //2.创建套接字
    20. int sfd=socket(AF_INET,SOCK_STREAM,0);
    21. if(sfd==-1){
    22. PRINT_ERR("socket error");
    23. }
    24. //3.快速重用端口号
    25. int optval=1;
    26. if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(optval))==-1){
    27. PRINT_ERR("setsockopt error");
    28. }
    29. //3.填充服务器结构体信息
    30. sin.sin_family=AF_INET;
    31. sin.sin_addr.s_addr=inet_addr(argv[1]);
    32. sin.sin_port=htons(port);
    33. //4.绑定
    34. if(bind(sfd,(struct sockaddr *)&sin,sinLen)==-1){
    35. PRINT_ERR("bind error");
    36. }
    37. //5.监听
    38. if(listen(sfd,10)==-1){
    39. PRINT_ERR("listen error");
    40. }
    41. //6.多线程并发
    42. while(1){
    43. newfd=accept(sfd,(struct sockaddr *)&cin,&sinLen);
    44. if(newfd==-1){
    45. PRINT_ERR("accept error");
    46. }
    47. info.newfd=newfd;
    48. //创建线程,将信息结构体传入线程
    49. if(pthread_create(&tid,NULL,callBack,&info)==-1){
    50. PRINT_ERR("pthread_create error");
    51. }
    52. }
    53. //7.关闭套接字
    54. close(sfd);
    55. return 0;
    56. }

    cli.c(客户端):

    1. #include "./fun.h"
    2. int main(int argc, const char *argv[])
    3. {
    4. // 1.判断入参
    5. if (argc != 3) {
    6. fprintf(stderr, "入参为空,请检查\n");
    7. return -1;
    8. }
    9. //变量声明
    10. struct sockaddr_in sin;
    11. int sinLen = sizeof(sin);
    12. buf_t buf;
    13. char path[128] = "";
    14. // 2.端口号转整型
    15. int port = atoi(argv[2]);
    16. // 3.创建套接字
    17. int sfd = socket(AF_INET, SOCK_STREAM, 0);
    18. if (sfd == -1) {
    19. PRINT_ERR("socket error");
    20. }
    21. // 4.填充结构体信息
    22. sin.sin_family = AF_INET;
    23. sin.sin_addr.s_addr = inet_addr(argv[1]);
    24. sin.sin_port = htons(port);
    25. // 5.连接服务器
    26. if (connect(sfd, (struct sockaddr *)&sin, sinLen) == -1) {
    27. PRINT_ERR("connect error");
    28. }
    29. while (1) {
    30. // 6.打印选项菜单
    31. puts("*****************************");
    32. puts("*********1.查看文件***********");
    33. puts("*********2.下载文件***********");
    34. puts("*********3.上传文件***********");
    35. puts("*****************************");
    36. printf("请输入选项:>>>");
    37. memset(&buf, 0, sizeof(buf));
    38. memset(path, 0, sizeof(path));
    39. scanf("%d", &buf.type);
    40. getchar();
    41. switch (buf.type) {
    42. case 1:
    43. break;
    44. case 2:
    45. if (getcwd(path, sizeof(path)) == NULL) {
    46. PRINT_ERR("getcwd error");
    47. }
    48. strcpy(buf.data1, path);
    49. strcat(buf.data1, "/download/");
    50. printf("请输入要下载的文件名:>>>");
    51. if (fgets(buf.data2, sizeof(buf.data2), stdin) == NULL) {
    52. PRINT_ERR("fgets error");
    53. }
    54. buf.data2[strlen(buf.data2) - 1] = '\0';
    55. strcat(buf.data1, buf.data2);
    56. break;
    57. case 3:
    58. if (getcwd(path, sizeof(path)) == NULL) {
    59. PRINT_ERR("getcwd error");
    60. }
    61. strcpy(buf.data1, path);
    62. strcat(buf.data1, "/download/");
    63. printf("请输入要上传的文件名:>>>");
    64. if (fgets(buf.data2, sizeof(buf.data2), stdin) == NULL) {
    65. PRINT_ERR("fgets error");
    66. }
    67. buf.data2[strlen(buf.data2) - 1] = '\0';
    68. strcat(buf.data1, buf.data2);
    69. break;
    70. default:
    71. printf("error");
    72. }
    73. // 7.发送消息
    74. if (send(sfd, &buf, sizeof(buf), 0) == -1) {
    75. PRINT_ERR("send error");
    76. }
    77. // printf("1\n");
    78. // 8.接收消息
    79. while (1) {
    80. memset(&buf, 0, sizeof(buf));
    81. if (recv(sfd, &buf, sizeof(buf), 0) == -1) {
    82. PRINT_ERR("recv error");
    83. }
    84. // buf.data2是quit说明服务器读取文件名已经读完,退出循环
    85. if (strcmp(buf.data2, "quit") == 0)
    86. break;
    87. puts(buf.data1);
    88. if (buf.type == 2 || buf.type == 3)
    89. break;
    90. }
    91. //按任意键继续
    92. printf("<<请按回车键继续>>");
    93. getchar();
    94. }
    95. // 8.关闭套接字
    96. close(sfd);
    97. return 0;
    98. }

    fun.h(头文件):

    1. #ifndef __FUN_H__
    2. #define __FUN_H__
    3. #include
    4. /********共用***********/
    5. //结构体声明
    6. //线程信息结构体
    7. typedef struct{
    8. int newfd;
    9. }Info_t;
    10. //消息收发结构体
    11. typedef struct{
    12. int type;
    13. char data1[256];
    14. char data2[256];
    15. }buf_t;
    16. /********服务器端***********/
    17. //函数声明
    18. void *callBack(void *arg);
    19. //显示文件
    20. void showFile(int newfd);
    21. //下载文件
    22. void downloadFile(int newfd,buf_t buf);\
    23. //上传文件
    24. void uploadFile(int newfd,buf_t buf);
    25. /********客户端***********/
    26. #endif

    fun.c(功能文件):

    1. #include "fun.h"
    2. void *callBack(void *arg)
    3. {
    4. //转分离态
    5. pthread_detach(pthread_self());
    6. Info_t info = *(Info_t *)arg;
    7. int newfd = info.newfd;
    8. //变量声明
    9. buf_t buf;
    10. //循环收发数据
    11. while (1) {
    12. if (recv(newfd, &buf, sizeof(buf), 0) == -1) {
    13. VPRINT_ERR("recv error");
    14. }
    15. switch (buf.type) {
    16. case 1:
    17. showFile(newfd);
    18. break;
    19. case 2:
    20. downloadFile(newfd, buf);
    21. break;
    22. case 3:
    23. uploadFile(newfd, buf);
    24. break;
    25. default:
    26. printf("error");
    27. }
    28. }
    29. //关闭文件描述符
    30. close(newfd);
    31. //退出线程
    32. pthread_exit(0);
    33. }
    34. //显示文件
    35. void showFile(int newfd)
    36. {
    37. //变量声明
    38. DIR *dir = NULL;
    39. buf_t buf;
    40. struct dirent *dp = NULL;
    41. //打开目录
    42. dir = opendir("./");
    43. if (NULL == dir) {
    44. VPRINT_ERR("opendir error");
    45. }
    46. while (1) {
    47. //循环读取目录,直到读完
    48. dp = readdir(dir);
    49. if (dp == NULL) {
    50. if (errno == 0) {
    51. strcpy(buf.data2, "quit");
    52. if (send(newfd, &buf, sizeof(buf), 0) == -1) {
    53. VPRINT_ERR("send error");
    54. }
    55. break;
    56. } else {
    57. VPRINT_ERR("readdir error");
    58. }
    59. }
    60. memset(&buf, 0, sizeof(buf));
    61. buf.type = 1;
    62. strcpy(buf.data1, dp->d_name);
    63. //循环发送
    64. if (send(newfd, &buf, sizeof(buf), 0) == -1) {
    65. VPRINT_ERR("send error");
    66. }
    67. }
    68. //关闭目录,dir指向NULL
    69. closedir(dir);
    70. dir = NULL;
    71. }
    72. //下载文件
    73. void downloadFile(int newfd, buf_t buf)
    74. {
    75. //变量声明
    76. char server_path[256] = "./";
    77. char client_path[256] = "";
    78. char str[128] = "";
    79. int ret;
    80. strcpy(client_path, buf.data1);
    81. strcat(server_path, buf.data2);
    82. // 1.打开2个文件
    83. int fdr = open(server_path, O_RDONLY);
    84. int fdw = open(client_path, O_WRONLY | O_CREAT | O_TRUNC, 0664);
    85. if (fdr == -1 || fdw == -1) {
    86. VPRINT_ERR("open error");
    87. }
    88. while (1) {
    89. // 2.循环读取
    90. ret = read(fdr, str, sizeof(str));
    91. if (ret == -1) {
    92. VPRINT_ERR("read error");
    93. } else if (ret == 0) {
    94. break;
    95. }
    96. // 3.循环写入
    97. write(fdw, str, ret);
    98. }
    99. // 4.发送消息
    100. memset(&buf, 0, sizeof(buf));
    101. buf.type = 2;
    102. strcpy(buf.data1, "下载成功");
    103. if (send(newfd, &buf, sizeof(buf), 0) == -1) {
    104. VPRINT_ERR("send error");
    105. }
    106. // 5.关闭2个文件
    107. close(fdr);
    108. close(fdw);
    109. }
    110. //上传文件
    111. void uploadFile(int newfd, buf_t buf)
    112. {
    113. //变量声明
    114. char server_path[256] = "./";
    115. char client_path[256] = "";
    116. char str[128] = "";
    117. int ret;
    118. strcpy(client_path, buf.data1);
    119. strcat(server_path, buf.data2);
    120. // 1.打开2个文件
    121. int fdr = open(client_path, O_RDONLY);
    122. int fdw = open(server_path, O_WRONLY | O_CREAT | O_TRUNC, 0664);
    123. if (fdr == -1 || fdw == -1) {
    124. VPRINT_ERR("open error");
    125. }
    126. while (1) {
    127. // 2.循环读取
    128. ret = read(fdr, str, sizeof(str));
    129. if (ret == -1) {
    130. VPRINT_ERR("read error");
    131. } else if (ret == 0) {
    132. break;
    133. }
    134. // 3.循环写入
    135. write(fdw, str, ret);
    136. }
    137. // 4.发送消息
    138. memset(&buf, 0, sizeof(buf));
    139. buf.type = 3;
    140. strcpy(buf.data1, "上传成功");
    141. if (send(newfd, &buf, sizeof(buf), 0) == -1) {
    142. VPRINT_ERR("send error");
    143. }
    144. // 5.关闭2个文件
    145. close(fdr);
    146. close(fdw);
    147. }

    工程管理文件(makefile):

    1. -include config.mk
    2. #版本1
    3. # a.out:$(NAME).o $(FUN).o
    4. # $(CC) $^ -o $@
    5. # %.o:%.c
    6. # $(CC) -c $^ -o $@
    7. # .PHONY:clean
    8. # clean:
    9. # rm -rf $(NAME).o $(FUN).o a.out
    10. #版本2(生成动态库版本)
    11. # a.out:$(NAME).o lib$(FUN).so
    12. # $(CC) $< -L./lib -l$(FUN) -o $@
    13. # lib$(FUN).so:$(FUN).o
    14. # $(CC) -shared $^ -o $@
    15. # mv $@ ./lib
    16. # $(NAME).o:$(NAME).c
    17. # $(CC) -c $^ -o $@
    18. # $(FUN).o:$(FUN).c
    19. # $(CC) -Wall -fPIC -c $^ -o $@
    20. # .PHONY:clean
    21. # clean:
    22. # rm -rf *.o lib/*.so a.out
    23. #版本3(生成动态库版本+链接线程库版)
    24. # a.out:$(NAME).o lib$(FUN).so
    25. # $(CC) $< -L./lib -l$(FUN) -o $@ -lpthread
    26. # lib$(FUN).so:$(FUN).o
    27. # $(CC) -shared $^ -o $@
    28. # mv $@ ./lib
    29. # $(NAME).o:$(NAME).c
    30. # $(CC) -c $^ -o $@
    31. # $(FUN).o:$(FUN).c
    32. # $(CC) -Wall -fPIC -c $^ -o $@
    33. # .PHONY:clean
    34. # clean:
    35. # rm -rf *.o lib/*.so a.out
    36. #版本4(生成动态库版本+非父子进程通信版本)
    37. # .PHONY:all
    38. # all:F A B
    39. # A:$(NAMEA).o lib$(FUN).so
    40. # $(CC) $< -L./lib -l$(FUN) -o $@
    41. # B:$(NAMEB).o lib$(FUN).so
    42. # $(CC) $< -L./lib -l$(FUN) -o $@
    43. # F:$(NAMEF).o lib$(FUN).so
    44. # $(CC) $< -L./lib -l$(FUN) -o $@
    45. # lib$(FUN).so:$(FUN).o
    46. # $(CC) -shared $^ -o $@
    47. # mv $@ ./lib
    48. # $(NAMEF).o:$(NAMEF).c
    49. # $(CC) -c $^ -o $@
    50. # $(NAMEA).o:$(NAMEA).c
    51. # $(CC) -c $^ -o $@
    52. # $(NAMEB).o:$(NAMEB).c
    53. # $(CC) -c $^ -o $@
    54. # $(FUN).o:$(FUN).c
    55. # $(CC) -Wall -fPIC -c $^ -o $@
    56. # .PHONY:clean
    57. # clean:
    58. # rm -rf *.o lib/*.so A B F
    59. #版本5(生成动态库版本+非父子进程通信版本+链接线程库版本)
    60. # .PHONY:all
    61. # all:F A B
    62. # A:$(NAMEA).o lib$(FUN).so
    63. # $(CC) $< -L./lib -l$(FUN) -o $@ -lpthread
    64. # B:$(NAMEB).o lib$(FUN).so
    65. # $(CC) $< -L./lib -l$(FUN) -o $@ -lpthread
    66. # F:$(NAMEF).o lib$(FUN).so
    67. # $(CC) $< -L./lib -l$(FUN) -o $@
    68. # lib$(FUN).so:$(FUN).o
    69. # $(CC) -shared $^ -o $@
    70. # mv $@ ./lib
    71. # $(NAMEF).o:$(NAMEF).c
    72. # $(CC) -c $^ -o $@
    73. # $(NAMEA).o:$(NAMEA).c
    74. # $(CC) -c $^ -o $@
    75. # $(NAMEB).o:$(NAMEB).c
    76. # $(CC) -c $^ -o $@
    77. # $(FUN).o:$(FUN).c
    78. # $(CC) -Wall -fPIC -c $^ -o $@
    79. # .PHONY:clean
    80. # clean:
    81. # rm -rf *.o lib/*.so A B F
    82. #版本6(生成动态库版本+客户端服务器通信版本)
    83. # .PHONY:all
    84. # all:cli ser
    85. # ser:$(NAMESER).o lib$(FUN).so
    86. # $(CC) $< -L./lib -l$(FUN) -o $@
    87. # cli:$(NAMECLI).o lib$(FUN).so
    88. # $(CC) $< -L./lib -l$(FUN) -o $@
    89. # lib$(FUN).so:$(FUN).o
    90. # $(CC) -shared $^ -o $@
    91. # mv $@ ./lib
    92. # $(NAMESER).o:$(NAMESER).c
    93. # $(CC) -c $^ -o $@
    94. # $(NAMECLI).o:$(NAMECLI).c
    95. # $(CC) -c $^ -o $@
    96. # $(FUN).o:$(FUN).c
    97. # $(CC) -Wall -fPIC -c $^ -o $@
    98. # .PHONY:clean
    99. # clean:
    100. # rm -rf *.o lib/*.so ser cli
    101. #版本6(生成动态库版本+客户端服务器通信版本+线程库版本)
    102. .PHONY:all
    103. all:cli ser
    104. ser:$(NAMESER).o lib$(FUN).so
    105. $(CC) $< -L./lib -l$(FUN) -o $@ -lpthread
    106. cli:$(NAMECLI).o lib$(FUN).so
    107. $(CC) $< -L./lib -l$(FUN) -o $@ -lpthread
    108. lib$(FUN).so:$(FUN).o
    109. $(CC) -shared $^ -o $@
    110. mv $@ ./lib
    111. $(NAMESER).o:$(NAMESER).c
    112. $(CC) -c $^ -o $@
    113. $(NAMECLI).o:$(NAMECLI).c
    114. $(CC) -c $^ -o $@
    115. $(FUN).o:$(FUN).c
    116. $(CC) -Wall -fPIC -c $^ -o $@
    117. .PHONY:clean
    118. clean:
    119. rm -rf *.o lib/*.so ser cli

    工程管理配置文件(config.mk):

    1. #对应版本123的makefile
    2. # CC=gcc
    3. # NAME=test
    4. # FUN=fun
    5. #对应版本4,5的makefile
    6. # CC=gcc
    7. # NAMEA=Atest
    8. # NAMEB=Btest
    9. # NAMEF=Ftest
    10. # FUN=fun
    11. #对应版本6的makefile
    12. CC=gcc
    13. NAMESER=ser
    14. NAMECLI=cli
    15. FUN=fun

    测试结果:

    编译makefile:

     打开服务器和客户端:

     上传和下载文件的位置:

     选择1查看文件:

     选择2下载文件:

     另起一个终端客户端,选择3上传文件:

     关闭服务器和客户端,diff测试下载以及上传的文件是否和原来的一致:

     

  • 相关阅读:
    TS 常考知识点记录
    8/23 网络流、最大流+hungary算法
    人工智能学习01--errors
    【Arduino+ESP32专题】外部中断的使用
    【Github】git本地仓库建立与远程连接
    前端面试题 - 元素的innerText outerText innerHTML的区别?
    【Python】-- Turtle绘图(使用代码画喜欢的图形!)
    云原生Kubernetes:K8S集群使用带凭证的harbor仓库
    figma插件都有哪些好用的,分享11个提效插件
    10_18Qt
  • 原文地址:https://blog.csdn.net/qq_53183630/article/details/128156488