Linux平台基于socket的文件传输服务器和客户端
目录
前言
说些废话,本来让写网络视频传输服务,结果除了视频,所有文件都可以进行传输。还好,毕竟文件传输原理是一样的,读取二进制流,传输二进制流,写入二进制流。
一、服务器程序结构
采用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 1024
void video_trans(int sockfd, char *buf);
void sig_chld(int signo)
{
pid_t pid;
int stat;
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;
char buf[MAX_FILE];
socklen_t clilen;
struct sockaddr_in cliaddr, 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 1024
void 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 1024
void trans(FILE *fp, int sockfd, char *file_name, char **argv);
int main(int argc, char **argv)
{
int sockfd;
struct sockaddr_in servaddr;
char file_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 1024
void trans(FILE *fp, int sockfd, char *file_name, char **argv)
{
int maxfdp1, stdineof = 0, length = 1;
fd_set rset;
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';
// 文件名称存入buf
strncpy(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);
}
// 从服务器接收内容到buf
while((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);
}
}
}