项目复习:基于TCP的文件服务器
创始人
2024-03-14 11:16:16
0

 ser.c(服务器):

#include "./fun.h"int main(int argc,const char * argv[])
{//1.判断入参if(argc!=3){fprintf(stderr,"入参为空,请检查\n");return -1;}//端口号转整型int port=atoi(argv[2]);//变量声明struct sockaddr_in sin;int sinLen=sizeof(sin);struct sockaddr_in cin;int cinLen=sizeof(cin);Info_t info;int newfd;pthread_t tid;//2.创建套接字int sfd=socket(AF_INET,SOCK_STREAM,0);if(sfd==-1){PRINT_ERR("socket error");}//3.快速重用端口号int optval=1;if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(optval))==-1){PRINT_ERR("setsockopt error");}//3.填充服务器结构体信息sin.sin_family=AF_INET;sin.sin_addr.s_addr=inet_addr(argv[1]);sin.sin_port=htons(port);//4.绑定if(bind(sfd,(struct sockaddr *)&sin,sinLen)==-1){PRINT_ERR("bind error");}//5.监听if(listen(sfd,10)==-1){PRINT_ERR("listen error");}//6.多线程并发while(1){newfd=accept(sfd,(struct sockaddr *)&cin,&sinLen);if(newfd==-1){PRINT_ERR("accept error");}info.newfd=newfd;//创建线程,将信息结构体传入线程if(pthread_create(&tid,NULL,callBack,&info)==-1){PRINT_ERR("pthread_create error");}}//7.关闭套接字close(sfd);return 0;
}

cli.c(客户端):

#include "./fun.h"int main(int argc, const char *argv[])
{// 1.判断入参if (argc != 3) {fprintf(stderr, "入参为空,请检查\n");return -1;}//变量声明struct sockaddr_in sin;int sinLen = sizeof(sin);buf_t buf;char path[128] = "";// 2.端口号转整型int port = atoi(argv[2]);// 3.创建套接字int sfd = socket(AF_INET, SOCK_STREAM, 0);if (sfd == -1) {PRINT_ERR("socket error");}// 4.填充结构体信息sin.sin_family = AF_INET;sin.sin_addr.s_addr = inet_addr(argv[1]);sin.sin_port = htons(port);// 5.连接服务器if (connect(sfd, (struct sockaddr *)&sin, sinLen) == -1) {PRINT_ERR("connect error");}while (1) {// 6.打印选项菜单puts("*****************************");puts("*********1.查看文件***********");puts("*********2.下载文件***********");puts("*********3.上传文件***********");puts("*****************************");printf("请输入选项:>>>");memset(&buf, 0, sizeof(buf));memset(path, 0, sizeof(path));scanf("%d", &buf.type);getchar();switch (buf.type) {case 1:break;case 2:if (getcwd(path, sizeof(path)) == NULL) {PRINT_ERR("getcwd error");}strcpy(buf.data1, path);strcat(buf.data1, "/download/");printf("请输入要下载的文件名:>>>");if (fgets(buf.data2, sizeof(buf.data2), stdin) == NULL) {PRINT_ERR("fgets error");}buf.data2[strlen(buf.data2) - 1] = '\0';strcat(buf.data1, buf.data2);break;case 3:if (getcwd(path, sizeof(path)) == NULL) {PRINT_ERR("getcwd error");}strcpy(buf.data1, path);strcat(buf.data1, "/download/");printf("请输入要上传的文件名:>>>");if (fgets(buf.data2, sizeof(buf.data2), stdin) == NULL) {PRINT_ERR("fgets error");}buf.data2[strlen(buf.data2) - 1] = '\0';strcat(buf.data1, buf.data2);break;default:printf("error");}// 7.发送消息if (send(sfd, &buf, sizeof(buf), 0) == -1) {PRINT_ERR("send error");}// printf("1\n");// 8.接收消息while (1) {memset(&buf, 0, sizeof(buf));if (recv(sfd, &buf, sizeof(buf), 0) == -1) {PRINT_ERR("recv error");}// buf.data2是quit说明服务器读取文件名已经读完,退出循环if (strcmp(buf.data2, "quit") == 0)break;puts(buf.data1);if (buf.type == 2 || buf.type == 3)break;}//按任意键继续printf("<<请按回车键继续>>");getchar();}// 8.关闭套接字close(sfd);return 0;
}

fun.h(头文件):

#ifndef __FUN_H__
#define __FUN_H__
#include 
/********共用***********/
//结构体声明
//线程信息结构体
typedef struct{int newfd;
}Info_t;
//消息收发结构体
typedef struct{int type;char data1[256];char data2[256];
}buf_t;
/********服务器端***********/
//函数声明
void *callBack(void *arg);
//显示文件
void showFile(int newfd);
//下载文件
void downloadFile(int newfd,buf_t buf);\
//上传文件
void uploadFile(int newfd,buf_t buf);/********客户端***********/#endif

fun.c(功能文件):

#include "fun.h"void *callBack(void *arg)
{//转分离态pthread_detach(pthread_self());Info_t info = *(Info_t *)arg;int newfd = info.newfd;//变量声明buf_t buf;//循环收发数据while (1) {if (recv(newfd, &buf, sizeof(buf), 0) == -1) {VPRINT_ERR("recv error");}switch (buf.type) {case 1:showFile(newfd);break;case 2:downloadFile(newfd, buf);break;case 3:uploadFile(newfd, buf);break;default:printf("error");}}//关闭文件描述符close(newfd);//退出线程pthread_exit(0);
}
//显示文件
void showFile(int newfd)
{//变量声明DIR *dir = NULL;buf_t buf;struct dirent *dp = NULL;//打开目录dir = opendir("./");if (NULL == dir) {VPRINT_ERR("opendir error");}while (1) {//循环读取目录,直到读完dp = readdir(dir);if (dp == NULL) {if (errno == 0) {strcpy(buf.data2, "quit");if (send(newfd, &buf, sizeof(buf), 0) == -1) {VPRINT_ERR("send error");}break;} else {VPRINT_ERR("readdir error");}}memset(&buf, 0, sizeof(buf));buf.type = 1;strcpy(buf.data1, dp->d_name);//循环发送if (send(newfd, &buf, sizeof(buf), 0) == -1) {VPRINT_ERR("send error");}}//关闭目录,dir指向NULLclosedir(dir);dir = NULL;
}
//下载文件
void downloadFile(int newfd, buf_t buf)
{//变量声明char server_path[256] = "./";char client_path[256] = "";char str[128] = "";int ret;strcpy(client_path, buf.data1);strcat(server_path, buf.data2);// 1.打开2个文件int fdr = open(server_path, O_RDONLY);int fdw = open(client_path, O_WRONLY | O_CREAT | O_TRUNC, 0664);if (fdr == -1 || fdw == -1) {VPRINT_ERR("open error");}while (1) {// 2.循环读取ret = read(fdr, str, sizeof(str));if (ret == -1) {VPRINT_ERR("read error");} else if (ret == 0) {break;}// 3.循环写入write(fdw, str, ret);}// 4.发送消息memset(&buf, 0, sizeof(buf));buf.type = 2;strcpy(buf.data1, "下载成功");if (send(newfd, &buf, sizeof(buf), 0) == -1) {VPRINT_ERR("send error");}// 5.关闭2个文件close(fdr);close(fdw);
}
//上传文件
void uploadFile(int newfd, buf_t buf)
{//变量声明char server_path[256] = "./";char client_path[256] = "";char str[128] = "";int ret;strcpy(client_path, buf.data1);strcat(server_path, buf.data2);// 1.打开2个文件int fdr = open(client_path, O_RDONLY);int fdw = open(server_path, O_WRONLY | O_CREAT | O_TRUNC, 0664);if (fdr == -1 || fdw == -1) {VPRINT_ERR("open error");}while (1) {// 2.循环读取ret = read(fdr, str, sizeof(str));if (ret == -1) {VPRINT_ERR("read error");} else if (ret == 0) {break;}// 3.循环写入write(fdw, str, ret);}// 4.发送消息memset(&buf, 0, sizeof(buf));buf.type = 3;strcpy(buf.data1, "上传成功");if (send(newfd, &buf, sizeof(buf), 0) == -1) {VPRINT_ERR("send error");}// 5.关闭2个文件close(fdr);close(fdw);
}

工程管理文件(makefile):

-include config.mk
#版本1
# a.out:$(NAME).o $(FUN).o
# 	$(CC) $^ -o $@
# %.o:%.c
# 	$(CC) -c $^ -o $@# .PHONY:clean
# clean:
# 	rm -rf $(NAME).o $(FUN).o a.out#版本2(生成动态库版本)
# a.out:$(NAME).o lib$(FUN).so
# 	$(CC) $< -L./lib -l$(FUN) -o $@
# lib$(FUN).so:$(FUN).o
# 	$(CC) -shared $^ -o $@
# 	mv $@ ./lib
# $(NAME).o:$(NAME).c
# 	$(CC) -c $^ -o $@
# $(FUN).o:$(FUN).c
# 	$(CC) -Wall -fPIC -c $^ -o $@
# .PHONY:clean
# clean:
# 	rm -rf *.o lib/*.so a.out#版本3(生成动态库版本+链接线程库版)
# a.out:$(NAME).o lib$(FUN).so
# 	$(CC) $< -L./lib -l$(FUN) -o $@ -lpthread
# lib$(FUN).so:$(FUN).o
# 	$(CC) -shared $^ -o $@
# 	mv $@ ./lib
# $(NAME).o:$(NAME).c
# 	$(CC) -c $^ -o $@
# $(FUN).o:$(FUN).c
# 	$(CC) -Wall -fPIC -c $^ -o $@
# .PHONY:clean
# clean:
# 	rm -rf *.o lib/*.so a.out#版本4(生成动态库版本+非父子进程通信版本)
# .PHONY:all
# all:F A B
# A:$(NAMEA).o lib$(FUN).so
# 	$(CC) $< -L./lib -l$(FUN) -o $@
# B:$(NAMEB).o lib$(FUN).so
# 	$(CC) $< -L./lib -l$(FUN) -o $@
# F:$(NAMEF).o lib$(FUN).so
# 	$(CC) $< -L./lib -l$(FUN) -o $@
# lib$(FUN).so:$(FUN).o
# 	$(CC) -shared $^ -o $@
# 	mv $@ ./lib
# $(NAMEF).o:$(NAMEF).c
# 	$(CC) -c $^ -o $@
# $(NAMEA).o:$(NAMEA).c
# 	$(CC) -c $^ -o $@
# $(NAMEB).o:$(NAMEB).c
# 	$(CC) -c $^ -o $@
# $(FUN).o:$(FUN).c
# 	$(CC) -Wall -fPIC -c $^ -o $@
# .PHONY:clean
# clean:
# 	rm -rf *.o lib/*.so A B F#版本5(生成动态库版本+非父子进程通信版本+链接线程库版本)
# .PHONY:all
# all:F A B
# A:$(NAMEA).o lib$(FUN).so
# 	$(CC) $< -L./lib -l$(FUN) -o $@ -lpthread
# B:$(NAMEB).o lib$(FUN).so
# 	$(CC) $< -L./lib -l$(FUN) -o $@ -lpthread
# F:$(NAMEF).o lib$(FUN).so
# 	$(CC) $< -L./lib -l$(FUN) -o $@
# lib$(FUN).so:$(FUN).o
# 	$(CC) -shared $^ -o $@
# 	mv $@ ./lib
# $(NAMEF).o:$(NAMEF).c
# 	$(CC) -c $^ -o $@
# $(NAMEA).o:$(NAMEA).c
# 	$(CC) -c $^ -o $@
# $(NAMEB).o:$(NAMEB).c
# 	$(CC) -c $^ -o $@
# $(FUN).o:$(FUN).c
# 	$(CC) -Wall -fPIC -c $^ -o $@
# .PHONY:clean
# clean:
# 	rm -rf *.o lib/*.so A B F#版本6(生成动态库版本+客户端服务器通信版本)
# .PHONY:all
# all:cli ser
# ser:$(NAMESER).o lib$(FUN).so
# 	$(CC) $< -L./lib -l$(FUN) -o $@
# cli:$(NAMECLI).o lib$(FUN).so
# 	$(CC) $< -L./lib -l$(FUN) -o $@
# lib$(FUN).so:$(FUN).o
# 	$(CC) -shared $^ -o $@
# 	mv $@ ./lib
# $(NAMESER).o:$(NAMESER).c
# 	$(CC) -c $^ -o $@
# $(NAMECLI).o:$(NAMECLI).c
# 	$(CC) -c $^ -o $@
# $(FUN).o:$(FUN).c
# 	$(CC) -Wall -fPIC -c $^ -o $@
# .PHONY:clean
# clean:
# 	rm -rf *.o lib/*.so ser cli#版本6(生成动态库版本+客户端服务器通信版本+线程库版本)
.PHONY:all
all:cli ser
ser:$(NAMESER).o lib$(FUN).so$(CC) $< -L./lib -l$(FUN) -o $@ -lpthread
cli:$(NAMECLI).o lib$(FUN).so$(CC) $< -L./lib -l$(FUN) -o $@ -lpthread
lib$(FUN).so:$(FUN).o$(CC) -shared $^ -o $@mv $@ ./lib
$(NAMESER).o:$(NAMESER).c$(CC) -c $^ -o $@
$(NAMECLI).o:$(NAMECLI).c$(CC) -c $^ -o $@
$(FUN).o:$(FUN).c$(CC) -Wall -fPIC -c $^ -o $@
.PHONY:clean
clean:rm -rf *.o lib/*.so ser cli

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

#对应版本1,2,3的makefile
# CC=gcc
# NAME=test
# FUN=fun#对应版本4,5的makefile
# CC=gcc
# NAMEA=Atest
# NAMEB=Btest
# NAMEF=Ftest
# FUN=fun#对应版本6的makefile
CC=gcc
NAMESER=ser
NAMECLI=cli
FUN=fun

测试结果:

编译makefile:

 打开服务器和客户端:

 上传和下载文件的位置:

 选择1查看文件:

 选择2下载文件:

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

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

 

相关内容

热门资讯

汽车油箱结构是什么(汽车油箱结... 本篇文章极速百科给大家谈谈汽车油箱结构是什么,以及汽车油箱结构原理图解对应的知识点,希望对各位有所帮...
美国2年期国债收益率上涨15个... 原标题:美国2年期国债收益率上涨15个基点 美国2年期国债收益率上涨15个基...
嵌入式 ADC使用手册完整版 ... 嵌入式 ADC使用手册完整版 (188977万字)💜&#...
重大消息战皇大厅开挂是真的吗... 您好:战皇大厅这款游戏可以开挂,确实是有挂的,需要了解加客服微信【8435338】很多玩家在这款游戏...
盘点十款牵手跑胡子为什么一直... 您好:牵手跑胡子这款游戏可以开挂,确实是有挂的,需要了解加客服微信【8435338】很多玩家在这款游...
senator香烟多少一盒(s... 今天给各位分享senator香烟多少一盒的知识,其中也会对sevebstars香烟进行解释,如果能碰...
终于懂了新荣耀斗牛真的有挂吗... 您好:新荣耀斗牛这款游戏可以开挂,确实是有挂的,需要了解加客服微信8435338】很多玩家在这款游戏...
盘点十款明星麻将到底有没有挂... 您好:明星麻将这款游戏可以开挂,确实是有挂的,需要了解加客服微信【5848499】很多玩家在这款游戏...
总结文章“新道游棋牌有透视挂吗... 您好:新道游棋牌这款游戏可以开挂,确实是有挂的,需要了解加客服微信【7682267】很多玩家在这款游...
终于懂了手机麻将到底有没有挂... 您好:手机麻将这款游戏可以开挂,确实是有挂的,需要了解加客服微信【8435338】很多玩家在这款游戏...