网络传文件(TCP)

Server:

fileserver.c

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include "rw.h"
#define BACKLOG 10

void *deal_request(void* arg)
{
	int connfd = *((int *)arg);
	char file_name[MAX_LEN];
	int res =0;
	pthread_detach(pthread_self());//当线程终止运行后自动清理所占资源并退出
	
	res = read_cmd(connfd,file_name,sizeof(file_name));
	printf("res = %d",res);
	if(res == -1)
		printf("read file name error\n");
	else{
		//printf("read_cmd :%s",file_name);
		printf("jjres = %d",res);
		if(send_file(connfd,file_name)==-1){
			printf("Send file error\n");	
		}
	}	
	//printf("res = %d",res);
/*	if(res = read_cmd(connfd,file_name,sizeof(file_name))==-1){
		printf("read file name error\n");
	}
	else{
		//printf("read_cmd :%s",file_name);
		printf("jjres = %d",res);
		if(send_file(connfd,file_name)==-1){
			printf("Send file error\n");	
		}	
	}*/
	printf("deal_request finish\n");
	free(arg);//释放动态分配的内存
	close(connfd);
	pthread_exit(NULL);
}
int main(int argc, char** argv)
{
	int sockfd;
	int* connfd;
	struct sockaddr_in servaddr;
	struct sockaddr_in cliaddr;
	struct sockaddr_in tempaddr;
	socklen_t templen;
	socklen_t clilen;
	if((sockfd = socket(AF_INET, SOCK_STREAM, 0))==-1){
		perror("socket");
		exit(1);
	}
	bzero(&servaddr,sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servaddr.sin_port = 0;//自动分配端口
	
	if(bind(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr))==-1){
		perror("bind error");
		exit(1);	
	}
	templen = sizeof(struct sockaddr);
	//由于端口号自动分配 这里获取套接字本地地址以获得端口号
	if(getsockname(sockfd,(struct sockaddr*)&tempaddr,&templen)==-1){
		perror("get sock name error");
		exit(1);	
	}
	printf("Server is listening on port %d\n",ntohs(tempaddr.sin_port));
	if(listen(sockfd, BACKLOG) == -1){
		perror("listen");
		exit(1);	
	}
	
	while(1)
	{
		pthread_t pthread;
		clilen = sizeof(cliaddr);
		connfd = (int *)malloc(sizeof(int));//多线程中 套接字一定要动态分配 否则不同的线程都指向同一个connfd
		*connfd = accept(sockfd,(struct sockaddr *)&cliaddr, &clilen);
		if(*connfd == -1){
			perror("accept");
			continue;
		}
		if(pthread_create(&pthread,NULL,deal_request,connfd)!=0){
			perror("pthread_create");
			break;	
		}
	}
	return 0;
}

rw.h

#ifndef __RW_H
#define __RW_H
#define MAX_LEN 1024*10 //读写缓冲大小

ssize_t readall(int fd, void* buf, size_t* len);
ssize_t writeall(int fd, void* buf, size_t* len);
int read_cmd(int sockfd, char* cmd_buf, int len);
int send_cmd(int sockfd, char* file_name);

#endif

rw.c

#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include "rw.h"

//read函数扩展,解决大数据时,read分次读的问题
ssize_t readall(int fd, void* buf, size_t* len)
{
	size_t nleft;
	ssize_t nread;
	ssize_t total;
	char* ptr;
	ptr = buf;
	nleft = *len;
	total = 0;
	while(nleft>0)//反复读,直到无数据可读
	{
		if((nread = read(fd,ptr,*len))==-1){
			perror("readall");
			break;
		}	
		if(nread ==0){
			break;	
		}
		nleft -=nread;
		ptr +=nread;
		total +=nread;
		*len = nleft;
	}
	*len = total;
	return(nread == -1)?-1:0;
}

//write函数扩展,解决大数据时,write分次写的问题
ssize_t writeall(int fd, void* buf, size_t* len)
{
	size_t nleft;
	ssize_t nwrite;	
	ssize_t total;
	const char* ptr;
	ptr = buf;
	nleft= *len;
	total = 0;
	while(nleft>0)//反复写,直到数据都写入
	{
			if((nwrite = write(fd, ptr, *len)) == -1){
				perror("write all");
				break;
			}
			nleft -= nwrite;
			ptr += nwrite;
			total += nwrite;
			*len = nleft;
	}
	*len = total;
	return (nwrite == -1)?-1:0;
}

//分析客户端命令,提出取请求的文件名
int read_cmd(int sockfd, char* cmd_buf, int len)
{
		char line[MAX_LEN];
		int my_len = 0;
		int total_len =0;
		char *ptr;
		int can_read;
		if(len>MAX_LEN)
			len = MAX_LEN;
		can_read = 1;
		strcpy(cmd_buf,"\0");
		while(can_read)
		{
			if((my_len = read(sockfd,line,len))<0){
				perror("read");
				return -1;
			}
			total_len = total_len + my_len;
			if(total_len > len){
				printf("Recieve command error!\n");
				return -1;
			}
			//strstr(char *str1, char *str2):从字符串str1中查找是否有符串str2,如果有,从str1中的str2位置起,返回str1的指针,如果没有,返回null。
			printf("line =%s",line);
			if((ptr = strstr(line,"\r\n"))==NULL){
				if(total_len<=len){
				//char *strcat(char *dest,char *src):把src所指字符串添加到dest结尾处(覆盖dest结尾处的'\0')并添加'\0'。
					strcat(cmd_buf,line);
				}
			}
			else{
				//char *strncat(char *dest,char *src,int n):把src所指字符串的前n个字符添加到dest结尾处(覆盖dest结尾处的'\0')并添加'\0'。
				strncat(cmd_buf, line, ptr-line);
				can_read =0;
			}
			printf("Client requests file:%s\n",cmd_buf);
		}
		return 0;
	
}

//发文件至客户端
int send_file(int sockfd, char* file_name)
{
	int file_fd;
	int file_size;
	int read_left;
	int len;
	int error_flag;
	int readlen;
	struct stat file_state;
	char buffer[MAX_LEN];
	int dot_number = 0;
	if((file_fd = open(file_name,O_RDONLY)) == -1){
		perror("open");
		return -1;
	}
	//int fstat(int fildes,struct stat *buf):将参数fildes所指的文件状态,复制到buf所指的结构中。
	if(fstat(file_fd,&file_state)==-1){
		perror("fstat");
		return -1;	
	}
	file_size = file_state.st_size;
	read_left = file_size;
	len = MAX_LEN;
	while(read_left>0)
	{
		readlen = MAX_LEN;
		error_flag = readall(file_fd,buffer,&readlen);
		if(error_flag<0){
			return -1;
		}
		read_left -=readlen;
		len =readlen;
		error_flag = writeall(sockfd,buffer,&len);
		if(error_flag == -1){
			return -1;
		}
		if(readlen ==0&&read_left!=0){
			printf("the file is not read fully\n");
			return -1;
		}
		if(read_left ==0){
			printf("\nServer sent file over!\n");
		}
	}
	close(file_fd);
	return 0;
}

Client:

fileclient.c

#include <strings.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "rw.h"
#define DOT_PERIOD 50
int main(int argc, char** argv)
{
	int sockfd;
	int conn_ret;
	struct sockaddr_in servaddr;
	char cmd_buf[MAX_LEN];
	char recvbuf[MAX_LEN];
	int error_flag;
	int len = MAX_LEN;
	int file_fd;
	int dot_number;
	int total_len = 0;
	if(argc!=5){
		printf("Usage: fileclient <address> <port> <src file> <des file>\n");
		return 0;	
	}
	if((sockfd = socket(AF_INET,SOCK_STREAM,0))==-1){
		perror("sock");
		exit(1);
	}
	bzero(&servaddr,sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(atoi(argv[2]));
	inet_pton(AF_INET,argv[1],&servaddr.sin_addr);
	conn_ret = connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
	if(conn_ret = -1){
		perror("connect");
	}
	//创建接收文件
	file_fd = open(argv[4],O_CREAT | O_WRONLY);
	if(file_fd == -1){
		perror("open");
		exit(1);
	}
	
	//构建客户端的请求消息 格式为“文件名\r\n”
	len = strlen(argv[3])+3;
	snprintf(cmd_buf,len,"%s\r\n",argv[3]);
	printf("cmd_buf %s\n",cmd_buf);
	if((error_flag = writeall(sockfd,cmd_buf,&len))==-1){
		printf("writeall error!\n");
		exit(1);	
	}
	
	//缓冲大小,决定每次I/O操作的数据量
	len = MAX_LEN;
	printf("\nfile is transferring:\n");
	while((error_flag = readall(sockfd,recvbuf,&len))==0)
	{
		if(len ==0){
			printf("\nClient has recieved file!\n");
			break;
		}
		printf(".");
		printf("read length is %d\n",len);
		dot_number++;
		if((dot_number%DOT_PERIOD)==0)
		{
			printf("\n");
			dot_number = 0;
		}
		total_len +=len;
		//将接收到数据写入文件
		if(writeall(file_fd,recvbuf,&len) == -1)
		{
			printf("\nclient has some error when receive the file \n");
			break;
		}
		len = MAX_LEN;
	}
	printf("\nRecevied %d bytes\n",total_len);
	close(file_fd);
	close(sockfd);
	return 0;
}

Makefile:

CC=gcc
all:fileserver fileclient 
rw.o:rw.c
	$(CC) -c rw.c -o rw.o
fileserver:fileserver.c rw.o
	$(CC) fileserver.c rw.o -o fileserver -lpthread
fileclient:fileclient.c rw.o
	$(CC) fileclient.c rw.o -o fileclient 

clean:
	@rm fileserver rw.o fileclient


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值