Linux网络编程之基于UDP实现可靠的文件传输示例
了解网络传输协议的人都知道,采用TCP实现文件传输很简单。相对于TCP,由于UDP是面向无连接、不可靠的传输协议,所以我们需要考虑丢包和后发先至(包的顺序)的问题,所以我们想要实现UDP传输文件,则需要解决这两个问题。方法就是给数据包编号,按照包的顺序接收并存储,接收端接收到数据包后发送确认信息给发送端,发送端接收确认数据以后再继续发送下一个包,如果接收端收到的数据包的编号不是期望的编号,则要求发送端重新发送。
下面展示的是基于linux下C语言实现的一个示例程序,该程序定义一个包的结构体,其中包含数据和包头,包头里包含有包的编号和数据大小,经过测试后,该程序可以成功传输一个视频文件。
具体实现代码如下:
server端代码如下:
/************************************************************************* >FileName:server.c >Author:SongLee ************************************************************************/ #include<sys/types.h> #include<sys/socket.h> #include<unistd.h> #include<netinet/in.h> #include<arpa/inet.h> #include<stdio.h> #include<stdlib.h> #include<errno.h> #include<netdb.h> #include<stdarg.h> #include<string.h> #defineSERVER_PORT8000 #defineBUFFER_SIZE1024 #defineFILE_NAME_MAX_SIZE512 /*包头*/ typedefstruct { intid; intbuf_size; }PackInfo; /*接收包*/ structSendPack { PackInfohead; charbuf[BUFFER_SIZE]; }data; intmain() { /*发送id*/ intsend_id=0; /*接收id*/ intreceive_id=0; /*创建UDP套接口*/ structsockaddr_inserver_addr; bzero(&server_addr,sizeof(server_addr)); server_addr.sin_family=AF_INET; server_addr.sin_addr.s_addr=htonl(INADDR_ANY); server_addr.sin_port=htons(SERVER_PORT); /*创建socket*/ intserver_socket_fd=socket(AF_INET,SOCK_DGRAM,0); if(server_socket_fd==-1) { perror("CreateSocketFailed:"); exit(1); } /*绑定套接口*/ if(-1==(bind(server_socket_fd,(structsockaddr*)&server_addr,sizeof(server_addr)))) { perror("ServerBindFailed:"); exit(1); } /*数据传输*/ while(1) { /*定义一个地址,用于捕获客户端地址*/ structsockaddr_inclient_addr; socklen_tclient_addr_length=sizeof(client_addr); /*接收数据*/ charbuffer[BUFFER_SIZE]; bzero(buffer,BUFFER_SIZE); if(recvfrom(server_socket_fd,buffer,BUFFER_SIZE,0,(structsockaddr*)&client_addr,&client_addr_length)==-1) { perror("ReceiveDataFailed:"); exit(1); } /*从buffer中拷贝出file_name*/ charfile_name[FILE_NAME_MAX_SIZE+1]; bzero(file_name,FILE_NAME_MAX_SIZE+1); strncpy(file_name,buffer,strlen(buffer)>FILE_NAME_MAX_SIZE?FILE_NAME_MAX_SIZE:strlen(buffer)); printf("%s\n",file_name); /*打开文件*/ FILE*fp=fopen(file_name,"r"); if(NULL==fp) { printf("File:%sNotFound.\n",file_name); } else { intlen=0; /*每读取一段数据,便将其发给客户端*/ while(1) { PackInfopack_info; if(receive_id==send_id) { ++send_id; if((len=fread(data.buf,sizeof(char),BUFFER_SIZE,fp))>0) { data.head.id=send_id;/*发送id放进包头,用于标记顺序*/ data.head.buf_size=len;/*记录数据长度*/ if(sendto(server_socket_fd,(char*)&data,sizeof(data),0,(structsockaddr*)&client_addr,client_addr_length)<0) { perror("SendFileFailed:"); break; } /*接收确认消息*/ recvfrom(server_socket_fd,(char*)&pack_info,sizeof(pack_info),0,(structsockaddr*)&client_addr,&client_addr_length); receive_id=pack_info.id; } else { break; } } else { /*如果接收的id和发送的id不相同,重新发送*/ if(sendto(server_socket_fd,(char*)&data,sizeof(data),0,(structsockaddr*)&client_addr,client_addr_length)<0) { perror("SendFileFailed:"); break; } /*接收确认消息*/ recvfrom(server_socket_fd,(char*)&pack_info,sizeof(pack_info),0,(structsockaddr*)&client_addr,&client_addr_length); receive_id=pack_info.id; } } /*关闭文件*/ fclose(fp); printf("File:%sTransferSuccessful!\n",file_name); } } close(server_socket_fd); return0; }
client端代码如下:
/************************************************************************* >FileName:client.c >Author:SongLee ************************************************************************/ #include<sys/types.h> #include<sys/socket.h> #include<unistd.h> #include<netinet/in.h> #include<arpa/inet.h> #include<stdio.h> #include<stdlib.h> #include<errno.h> #include<netdb.h> #include<stdarg.h> #include<string.h> #defineSERVER_PORT8000 #defineBUFFER_SIZE1024 #defineFILE_NAME_MAX_SIZE512 /*包头*/ typedefstruct { intid; intbuf_size; }PackInfo; /*接收包*/ structRecvPack { PackInfohead; charbuf[BUFFER_SIZE]; }data; intmain() { intid=1; /*服务端地址*/ structsockaddr_inserver_addr; bzero(&server_addr,sizeof(server_addr)); server_addr.sin_family=AF_INET; server_addr.sin_addr.s_addr=inet_addr("127.0.0.1"); server_addr.sin_port=htons(SERVER_PORT); socklen_tserver_addr_length=sizeof(server_addr); /*创建socket*/ intclient_socket_fd=socket(AF_INET,SOCK_DGRAM,0); if(client_socket_fd<0) { perror("CreateSocketFailed:"); exit(1); } /*输入文件名到缓冲区*/ charfile_name[FILE_NAME_MAX_SIZE+1]; bzero(file_name,FILE_NAME_MAX_SIZE+1); printf("PleaseInputFileNameOnServer:"); scanf("%s",file_name); charbuffer[BUFFER_SIZE]; bzero(buffer,BUFFER_SIZE); strncpy(buffer,file_name,strlen(file_name)>BUFFER_SIZE?BUFFER_SIZE:strlen(file_name)); /*发送文件名*/ if(sendto(client_socket_fd,buffer,BUFFER_SIZE,0,(structsockaddr*)&server_addr,server_addr_length)<0) { perror("SendFileNameFailed:"); exit(1); } /*打开文件,准备写入*/ FILE*fp=fopen(file_name,"w"); if(NULL==fp) { printf("File:\t%sCanNotOpenToWrite\n",file_name); exit(1); } /*从服务器接收数据,并写入文件*/ intlen=0; while(1) { PackInfopack_info; if((len=recvfrom(client_socket_fd,(char*)&data,sizeof(data),0,(structsockaddr*)&server_addr,&server_addr_length))>0) { if(data.head.id==id) { pack_info.id=data.head.id; pack_info.buf_size=data.head.buf_size; ++id; /*发送数据包确认信息*/ if(sendto(client_socket_fd,(char*)&pack_info,sizeof(pack_info),0,(structsockaddr*)&server_addr,server_addr_length)<0) { printf("Sendconfirminformationfailed!"); } /*写入文件*/ if(fwrite(data.buf,sizeof(char),data.head.buf_size,fp)<data.head.buf_size) { printf("File:\t%sWriteFailed\n",file_name); break; } } elseif(data.head.id<id)/*如果是重发的包*/ { pack_info.id=data.head.id; pack_info.buf_size=data.head.buf_size; /*重发数据包确认信息*/ if(sendto(client_socket_fd,(char*)&pack_info,sizeof(pack_info),0,(structsockaddr*)&server_addr,server_addr_length)<0) { printf("Sendconfirminformationfailed!"); } } else { } } else { break; } } printf("ReceiveFile:\t%sFromServerIPSuccessful!\n",file_name); fclose(fp); close(client_socket_fd); return0; }
感兴趣的朋友可以动手测试一下该程序,相信会对大家的Linux下C语言网络编程带来一定的帮助。