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

 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 <head.h>
/********共用***********/
//结构体声明
//线程信息结构体
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指向NULL
    closedir(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测试下载以及上传的文件是否和原来的一致:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值