Linux平台基于socket的文件传输服务器和客户端
目录
前言一、服务器程序结构二、客户程序结构三、代码1.服务器主程序video_serv_fork.c2.服务器子程序video_trans.c3.客户主程序video_cli.c4.客户字程序video_trans_cli.c前言
说些废话,本来让写网络视频传输服务,结果除了视频,所有文件都可以进行传输。还好,毕竟文件传输原理是一样的,读取二进制流,传输二进制流,写入二进制流。
一、服务器程序结构
采用fork()派生子进程来处理和客户的文件传输。主进程只负责监听。有两个文件video_serv_fork.c和video_trans.c,分别是主程序和子程序文件。
二、客户程序结构
采用select内核函数实现左顾右盼能力,同时监听标准输入和套接口。有两个文件video_cli.c和video_trans_cli.c,分别是主程序和子程序文件。
三、代码
1.服务器主程序video_serv_fork.c
代码如下:
#include <unp.h>#define FILE_NAME_MAX_SIZE 1024#define BUFFER_SIZE 1024#define MAX_FILE 1024void video_trans(int sockfd, char *buf);void sig_chld(int signo){pid_tpid;intstat;while((pid = waitpid(-1, &stat, WNOHANG)) > 0)printf("child %d terminated\n", pid);return;}int main(int argc, char ** argv){pid_t childpid;int n, listenfd, connfd;charbuf[MAX_FILE];socklen_tclilen;struct sockaddr_incliaddr, servaddr;listenfd = Socket(AF_INET, SOCK_STREAM, 0);//创建IPV4套接口bzero(&servaddr, sizeof(servaddr));//设置地址结构体servaddr.sin_family= AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(SERV_PORT);Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));//套接口上绑定地址Listen(listenfd, LISTENQ);//开始监听Signal(SIGCHLD, sig_chld);//声明需要捕获子进程信号for( ; ; ){clilen = sizeof(cliaddr);if((connfd = accept(listenfd, (SA *)&cliaddr, &clilen)) < 0)//建立连接{if(errno == EINTR)//慢系统调用continue;else{printf("errno = %d,",errno);err_sys("accept error\n");}}if((childpid = Fork()) == 0)//派生子进程{Close(listenfd);if ((n = recv(connfd, buf, MAXLINE, 0)) == 0)//没有接收到任何字符{printf("please input correct file name\n");}else{video_trans(connfd, buf);}exit(0);}Close(connfd);}}
2.服务器子程序video_trans.c
代码如下:
#include <unp.h>#define FILE_NAME_MAX_SIZE 1024#define BUFFER_SIZE 1024#define MAX_FILE 1024void video_trans(int sockfd, char *buf){int flag;//发送是否成功标志char file_name[FILE_NAME_MAX_SIZE + 1];//文件名数组FILE *fp;//文件指针char *b = (char *)malloc(sizeof(char));//读取二进制文件块指针bzero(file_name, sizeof(file_name));printf("the client needs: %s ", buf);char * find = strchr(buf, '\n');//剔除文件名的回车if(find)*find = '\0';strncpy(file_name, buf, strlen(buf) > FILE_NAME_MAX_SIZE ? FILE_NAME_MAX_SIZE : strlen(buf));fp = fopen(file_name, "rb+");//二进制方式打开file_name文件if(fp == NULL){printf("%s :", file_name);perror("File Not Found\n");}else{bzero(buf, sizeof(buf));flag = 1;printf("\nsending file: %s ......\n", file_name);while(!feof(fp))//文件不为空{fread(b, 1, 1, fp);//从fp文件指针所指的文件中每次读取1个字节的二进制串,读1次放入b指针printf("%d ", *b);//打印读取到的内容if(send(sockfd, b, 1, 0) < 0)//给sockfd把b二进制串发送过去{printf("Failed To Send File: %s ", file_name);printf("\n");flag = 0;}}bzero(buf, sizeof(buf));}fclose(fp);if(flag == 1){printf("\nSuccess To send File: %s", file_name);printf("\n");}}
3.客户主程序video_cli.c
代码如下:
#include <unp.h>#define FILE_NAME_SIZE 1024#define BUFFER_SIZE 1024void trans(FILE *fp, int sockfd, char *file_name, char **argv);int main(int argc, char **argv){int sockfd;struct sockaddr_inservaddr;charfile_name[FILE_NAME_SIZE];if(argc != 2)err_quit("用法: video_cli <IPaddress>\n");sockfd = Socket(AF_INET, SOCK_STREAM, 0);bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(SERV_PORT);if(inet_pton(AF_INET, argv[1], &servaddr.sin_addr) == 0){err_quit("服务器地址错误!\n");}if(connect(sockfd, (SA *)&servaddr, sizeof(servaddr)) < 0){printf("无法连接到服务器 %s!\n", argv[1]);exit(1);}printf("请输入文件名称(如存在请先删除):\n");trans(stdin, sockfd, file_name, argv);exit(0);}
4.客户字程序video_trans_cli.c
代码如下:
#include <unp.h>#define FILE_NAME_SIZE 1024#define BUFFER_SIZE 1024void trans(FILE *fp, int sockfd, char *file_name, char **argv){intmaxfdp1, stdineof = 0, length = 1;fd_setrset;char buf[BUFFER_SIZE];FILE*f;char *b = (char *)malloc(sizeof(char));//写入文件block指针FD_ZERO(&rset);bzero(file_name, sizeof(file_name));bzero(buf, sizeof(buf));for ( ; ; ){if(stdineof == 0){FD_SET(fileno(fp), &rset);}FD_SET(sockfd, &rset);maxfdp1 = max(fileno(fp), sockfd) + 1;Select(maxfdp1, &rset, NULL, NULL, NULL);if(FD_ISSET(fileno(fp), &rset)){if(fgets(file_name, FILE_NAME_SIZE, fp) == NULL)// 从标准输入读取文件名{stdineof = 1;Shutdown(sockfd, SHUT_WR);FD_CLR(fileno(fp), &rset);continue;}else{char * find = strchr(file_name, '\n');if(find)*find = '\0';// 文件名称存入bufstrncpy(buf, file_name, strlen(file_name) > BUFFER_SIZE ? BUFFER_SIZE : strlen(file_name));// 向服务器发送buf数据,此时buf中存放的是客户端需要接受的文件名字send(sockfd, buf, BUFFER_SIZE, 0);}}if(FD_ISSET(sockfd, &rset)){bzero(buf, sizeof(buf)); f = fopen(file_name, "wb+");if(f == NULL){printf("文件: %s 无法打开并写入!\n", file_name);exit(1);}// 从服务器接收内容到bufwhile((length= recv(sockfd, b, 1, 0)) >= 0){if(length == 0){if(stdineof == 1)return;else{printf("\n从服务器 %s 接收到文件: %s\n", argv[1], file_name);//err_quit("服务器已终止\n");return;}}if(length > 0){printf("%d ", *b);fwrite(b, 1, 1, f); }}fclose(f);}}}