Linux平台基于socket的文件传输服务器和客户端

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);
			
		}

	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值