实验说明:
Linux下基于TCP协议的大文件传输程序
【实验内容】
通过Linux C编程,设计一个基于TCP协议的大文件传输系统,实现服务器端向客户端的大文件的传输
【实验要求】
(1)大文件的传输。对于比较大的文件,应该进行分包操作,以防止占用过多的内存,导致文件发送
失败,实验中每次最多传输1024个字符;
(2)用户根据参数输入选择传输的文件和传输位置;
(3)发送端和接收端分别显示文件传输相应的信息,包括:对方IP地址,当前已写(读)文件长度等;
(4)对于Ctrl+C命令以及服务器提前关闭的特殊情况给出响应(如:显示信息)
代码:
client.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#define PORT 6000
#define LISTENQ 20
#define BUFFSIZE 1024
#define FILE_NAME_MAX_SIZE 512
extern void sig_proccess(int signo);
extern void sig_pipe(int signo);
static int s;
int main(int argc, char **argv[])
{
int clientfd;
signal(SIGINT, sig_proccess);
signal(SIGPIPE, sig_pipe);
char file[FILE_NAME_MAX_SIZE];
if(argc!=2)
{
fprintf(stderr,"用法错误:./client <IP_Address>\n");
exit(1);
}
struct sockaddr_in clientaddr;
bzero(&clientaddr,sizeof(clientaddr));
clientaddr.sin_family=AF_INET;
clientaddr.sin_addr.s_addr=htons(INADDR_ANY);
clientaddr.sin_port=htons(0);
clientfd=socket(AF_INET,SOCK_STREAM,0);
if(clientfd<0)
{
perror("socket");
exit(1);
}
if(bind(clientfd,(struct sockaddr*)&clientaddr,sizeof(clientaddr))<0)
{
perror("bind");
exit(1);
}
struct sockaddr_in svraddr;
bzero(&svraddr,sizeof(svraddr));
if(inet_aton(argv[1],&svraddr.sin_addr)==0)
{
perror("inet_aton");
exit(1);
}
svraddr.sin_family=AF_INET;
svraddr.sin_port=htons(PORT);
socklen_t svraddrlen=sizeof(svraddr);
if(connect(clientfd,(struct sockaddr*)&svraddr,svraddrlen)<0)
{
perror("connect");
exit(1);
}
else{
//recv file imformation
char buff[BUFFSIZE]={0};
char filename[FILE_NAME_MAX_SIZE]={0};
int count;
bzero(buff,BUFFSIZE);
count=recv(clientfd,buff,BUFFSIZE,0);
if(count<0)
{
perror("recv");
exit(1);
}
strncpy(filename,buff,strlen(buff)>FILE_NAME_MAX_SIZE?FILE_NAME_MAX_SIZE:strlen(buff));
if (strlen(filename)==0 )
printf("准备接收文件不存在!\n");
else{
printf("准备从 : %s 接收文件 %s \n",argv[1],filename);
printf("请输入要接收的文件名(当前目录下)或文件地址:");
scanf("%s",&file);
//recv file
FILE *fd=fopen(file,"wb+");
if(fd==NULL)
{
perror("open");
exit(1);
}
else
{bzero(buff,BUFFSIZE);
int total=0;
int length=0;
while(length=recv(clientfd,buff,BUFFSIZE,0))
{
if(length<0)
{
perror("recv");
exit(1);
}
int writelen=fwrite(buff,sizeof(char),length,fd);
if(writelen<length)
{
perror("write");
exit(1);
}
total=total+length;
printf("已接受的长度:%d,总共接收的长度:%d\n",length,total);
bzero(buff,BUFFSIZE);
}
printf("接收来源于 %s的文件%s 已完成!\n",argv[1],filename);}
fclose(fd);}}
close(clientfd);
return 0;
}
server.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <signal.h>
#define PORT 6000
#define LISTENQ 20
#define BUFFSIZE 1024
#define FILE_NAME_MAX_SIZE 512
extern void sig_proccess(int signo);
extern void sig_pipe(int signo);
static int s;
int main(int argc, char **argv[])
{
signal(SIGINT, sig_proccess);
signal(SIGPIPE, sig_pipe);
//Input the file name
char filename[FILE_NAME_MAX_SIZE];
bzero(filename,FILE_NAME_MAX_SIZE);
//Create socket
int sockfd,connfd;
struct sockaddr_in svraddr,clientaddr;
bzero(&svraddr,sizeof(svraddr));
svraddr.sin_family=AF_INET;//Ipv4网络协议
svraddr.sin_addr.s_addr=htonl(INADDR_ANY);//本地地址
svraddr.sin_port=htons(PORT);//服务器端口
sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd<0)
{
perror("socket");
exit(1);
}
//bind
if(bind(sockfd,(struct sockaddr*)&svraddr,sizeof(svraddr))<0)
{
perror("bind");
exit(1);
}
//listen
if(listen(sockfd,LISTENQ)<0)
{
perror("listen");
exit(1);
}
while(1)
{
socklen_t length=sizeof(clientaddr);
//accept
connfd=accept(sockfd,(struct sockaddr*)&clientaddr,&length);
if(connfd<0)
{
perror("connect");
exit(1);
}
printf("请输入要传输的文件名(当前目录下)或文件地址:");
scanf("%s",&filename);
//getchar();
//read file
FILE *fd=fopen(filename,"rb");
if(fd==NULL)
{
printf("文件 :%s 没有找到!\n",filename);
}
else
{//send file imformation
char buff[BUFFSIZE];
int count;
bzero(buff,BUFFSIZE);
strncpy(buff,filename,strlen(filename)>FILE_NAME_MAX_SIZE?FILE_NAME_MAX_SIZE:strlen(filename));
count=send(connfd,buff,BUFFSIZE,0);
if(count<0)
{
perror("Send file information");
exit(1);
}
struct stat buf ;
if ( stat( filename, &buf ) < 0 )
{
perror( "stat" );
return ;
}
printf("文件大小:%d\n", buf.st_size );
bzero(buff,BUFFSIZE);
int file_block_length=0;
int total=0;
while((file_block_length=fread(buff,sizeof(char),BUFFSIZE,fd))>0)//(BUFFSIZE<buf.st_size ?BUFFSIZE:buf.st_size)
{
total=total+file_block_length;
printf("发送文件大小:%d,总共发送了:%d\n",file_block_length,total);
if(send(connfd,buff,file_block_length,0)<0)
{
perror("Send");
exit(1);
}
bzero(buff,BUFFSIZE);
}
fclose(fd);
printf("传输来源于 %s 端口号为%d的文件 %s已完成! \n",
inet_ntop(AF_INET, &clientaddr.sin_addr, buff, sizeof(buff)), ntohs(clientaddr.sin_port), filename );
}
close(connfd);
}
close(sockfd);
return 0;
}
tcp_process.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <signal.h>
static struct iovec*vs = NULL, *vc=NULL;
void sig_proccess(int signo){
printf(" catch a exit signal\n");
free(vc);
free(vs);
_exit(0);
}
void sig_pipe(int sign)
{
printf("catch a sigpipe signal\n");
free(vc);
free(vs);
_exit(0);
}
Makefile:
all:client server #all规则,它依赖于client和server规则
client:tcp_process.o client.o #client规则,生成客户端可执行程序
gcc -o client tcp_process.o client.o
server:tcp_process.o server.o #server规则,生成服务器端可执行程序
gcc -o server tcp_process.o server.o
tcp_process.o: #tcp_process.o规则,生成tcp_process.o
gcc -c tcp_process.c -o tcp_process.o
clean: #清理规则,删除client、server和中间文件
rm -f client server *.o