在Linux下基于TCP协议的文件传输程序
- 格式:doc
- 大小:599.00 KB
- 文档页数:12
linux tcp重传机制摘要:一、TCP 重传机制简介二、Linux TCP 重传机制的工作原理三、Linux TCP 重传机制的优化四、总结正文:一、TCP 重传机制简介TCP(传输控制协议)是互联网中使用最广泛的协议之一,它的可靠数据传输依赖于一系列复杂的机制,其中之一就是重传机制。
当数据包在网络中丢失或损坏时,TCP 会通过重传机制来重新发送这些数据包,以确保数据的可靠传输。
TCP 重传机制包括超时重传和重复确认重传两种方式。
二、Linux TCP 重传机制的工作原理Linux 操作系统中的TCP 重传机制遵循RFC 6298 标准,并在此基础上进行了一定的优化。
具体来说,Linux TCP 重传机制的工作原理可以分为以下几个步骤:1.当TCP 发送方发送数据包后,如果在规定的时间内没有收到接收方的确认(ACK),发送方会启动超时重传定时器(RTO)。
2.在RTO 超时之前,如果发送方收到接收方的重复确认(DUP ACK)信号,说明接收方已经成功接收了数据包,此时发送方会立即停止重传定时器,并重新计算RTO 值。
3.如果RTO 超时后,发送方仍然没有收到接收方的确认信号,发送方会重传数据包。
4.如果重传后,发送方仍然没有收到接收方的确认信号,发送方会继续重传数据包,直到达到最大重传次数(通常为3 次)或成功收到接收方的确认信号为止。
三、Linux TCP 重传机制的优化为了提高TCP 重传机制的性能,Linux 操作系统在实现TCP 重传机制时采用了一些优化措施,包括:1.避免不必要的重传:在收到DUP ACK 信号后,发送方会立即停止重传定时器,并重新计算RTO 值。
这样做可以避免在网络状况不佳的情况下,因误判而启动不必要的重传。
2.快速重传:当发送方连续收到多个DUP ACK 信号时,发送方会快速重传数据包,而不再等待RTO 超时。
这样可以减少重传的延迟,提高传输速度。
3.拥塞避免:当发送方检测到网络拥塞时,会减小发送速率,以避免进一步加剧拥塞。
linux 开发板之间数据传输方式
Linux开发板之间的数据传输方式有多种,以下是一些常见的方式:1.网络传输:通过网线或Wi-Fi连接,使用TCP/IP协议栈进行数据传
输。
这种方式适合大量数据的快速传输,但需要稳定的网络环境。
2.串口传输:通过串口连接,使用串口通信协议(如RS-232、RS-485
等)进行数据传输。
这种方式适合短距离、低速的数据传输,常用于设备之间的调试和通信。
B传输:通过USB接口连接,使用USB协议进行数据传输。
这种
方式速度较快,适用于大量数据的传输,但需要开发板支持USB接口。
4.SD卡/eMMC传输:将数据存储到SD卡或eMMC等存储介质中,
然后通过插槽或接口连接到另一块开发板进行数据传输。
这种方式适合大量数据的存储和传输,但需要开发板支持相应的存储接口。
5.I2C/SPI传输:通过I2C或SPI等总线协议进行数据传输。
这种方式
适用于短距离、低速的数据传输,常用于设备之间的通信和控制。
具体选择哪种传输方式,需要根据应用场景、传输距离、传输速率、设备接口等因素综合考虑。
列举几个linux中使用远程传输文件的命令或协议
列举几个Linux中使用远程传输文件的命令或协议如下:
1. scp命令:用于在本地和远程服务器之间传输文件。
例如:
`scp <本地文件路径> <远程服务器用户名@服务器IP地址:目
标路径>`。
2. sftp命令:用于通过SSH安全地在本地和远程服务器之间传输文件。
例如:`sftp <远程服务器用户名@服务器IP地址>`后,再使用相关命令进行上传或下载。
3. rsync命令:用于在本地和远程服务器之间同步、备份文件。
例如:`rsync <源文件路径> <目标服务器用户名@服务器IP地址:目标路径>`。
4. ftp协议:传输文件协议,通过FTP客户端与远程服务器进
行连接和传输。
例如:`ftp <远程服务器IP地址>`后,再使用
相关命令进行上传或下载。
5. samba协议:用于在Linux和Windows等系统之间共享文件
和打印机的协议。
可以通过挂载samba共享目录将文件从远
程服务器传输到本地。
这些命令或协议都可以在Linux中轻松实现文件的远程传输。
1需求分析1.1系统目的Linux 作为一个先进的操作系统,其发展越来越快,应用领域也越来越广泛。
在学习了《linux内核设计与实现》之后,亲自设计并实现linux下的编程,不仅有助于巩固学过的知识,更能扩宽自己的视野,增强动手实践能力。
1.2课程要求Linux下基于Socket的文件传输程序设计,包括对文件进行管理,包括上传,下载,删除文件,重命名,支持多线程等。
1.3开发工具与开发平台开发工具:C语言开发平台:Linux2系统设计2.1系统功能此软件的主要功能有:1.实现在linux环境下的基于socket文件传输;2.对linux环境下的文件进行简单管理。
其中客户端与服务器的所要完成的具体功能如下:客户端:(1)连接服务器,并发送指令给服务器;(2)接收并识别服务器发来的信息;(3)通过不同指令实现文件的查看,下载,和上传。
服务器:(1)能查看本地的文件,并进行简单的管理,如删除,重命名等(2)接收来自客户端的连接请求,并赋予客户端不同的操作权限;(3)识别客户端的发来的指令,实现与客户端间的文件下载和上传。
2.2系统结构软件由服务器端和客户端两部分组成,服务器端监听客户对某个特定端口的网络连接请求,并与之建立连接,这样,这个客户的数据就可以发送到服务器。
客户端的任务是将用户的指令发送给服务器,且客户端可以有多个。
客户端运行时将立即向服务器的某个端口发送连接请求,待连接建立后则由用户的发送线程发送该用户的指令到服务器。
服务器端的主线程负责监听某个端口,接收用户的发送线程发出的指令。
在客户端,用户接收线程接收服务器发送的数据,由用户端主线程进行处理后显示。
2.3客户端指令查看服务器下指定路径下的所有文件:look_up;下载服务器下指定路径下的指定文件:download filename;上传客户端下指定路径下的指定文件:senddata filename;退出连接:end ;2.4服务器指令运行服务器后,系统提示指令查看所有文件请输入 1 ;重命名文件请输入2 ;删除文件请输入3;开启服务监听模式4;2.5数据流设计3测试结果服务器进行文件管理的操作服务器监听多个客户端并相应其指令客户端0连接服务器客户端1连接服务器4总结这次课程设计是linux下的socket通信,以c为编程语言,并实现了文件管理与多线程开发。
linux服务器之间传输⽂件的四种⽅式本⽂为⼤家分享了linux服务器之间传输⽂件的四种⽅式,供⼤家参考,具体内容如下1. scp【优点】简单⽅便,安全可靠;⽀持限速参数【缺点】不⽀持排除⽬录【⽤法】scp就是secure copy,是⽤来进⾏远程⽂件拷贝的。
数据传输使⽤ ssh,并且和ssh 使⽤相同的认证⽅式,提供相同的安全保证。
命令格式:scp [参数] <源地址(⽤户名@IP地址或主机名)>:<⽂件路径> <⽬的地址(⽤户名 @IP 地址或主机名)>:<⽂件路径>举例:scp /home/work/source.txt work@192.168.0.10:/home/work/ #把本地的source.txt⽂件拷贝到192.168.0.10机器上的/home/work⽬录下scp work@192.168.0.10:/home/work/source.txt /home/work/ #把192.168.0.10机器上的source.txt⽂件拷贝到本地的/home/work⽬录下scp work@192.168.0.10:/home/work/source.txt work@192.168.0.11:/home/work/ #把192.168.0.10机器上的source.txt⽂件拷贝到192.168.0.11机器的/home/work⽬录下scp -r /home/work/sourcedir work@192.168.0.10:/home/work/ #拷贝⽂件夹,加-r参数scp -r /home/work/sourcedir work@:/home/work/ #使⽤主机名scp -r -v /home/work/sourcedir work@:/home/work/ #显⽰详情,加-v参数2. rcp【概述】⽬标主机需要事先打开rcp功能,并设置好rcp的权限:把源主机加⼊到可信任主机列表中,否则⽆法在源主机上使⽤rcp远程复制⽂件到⽬标主机。
TCP协议实现文件传输使用TCP协议实现传输文件程序分为发送端和接收端。
首先在传输文件数据之前,发送端会把将装有文件名称和文件长度等信息的数据包发送至接收端。
接收端收到文件名称和文件长度信息后会创建好空白文件。
接着开始传输文件数据。
下面介绍实现功能的主要过程:1.创建套接字、绑定、监听、连接、接受连接//创建TCP协议的套接字m_Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if(SOCKET_ERROR == m_Socket)AfxMessageBox("Create Socket Error! ", 0, 0);//绑定与监听SOCKADDR_IN addrSrv;addrSrv.sin_addr.s_addr = inet_addr(sIP);addrSrv.sin_family = AF_INET;addrSrv.sin_port = htons(Port);int ret = bind(m_Socket, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR) );if(ret==SOCKET_ERROR)AfxMessageBox("Bind Socket Error!", 0, 0);//连接SOCKADDR_IN ServerAddr;ServerAddr.sin_addr.s_addr = inet_addr(ServerAddr_in);ServerAddr.sin_family = AF_INET;ServerAddr.sin_port = htons(ServerPort);int Result = connect(m_Socket, (struct sockaddr*)&ServerAddr, sizeof(struct s ockaddr));if(SOCKET_ERROR == Result)AfxMessageBox("Connet Failed!");//接受连接SOCKADDR_IN ClientAddr;int len =sizeof(SOCKADDR_IN);SOCKET ClientSock = accept(m_Socket, (struct sockaddr*)&ClientAddr, &len);if(SOCKET_ERROR == ClientSock)AfxMessageBox("Accept Failed!");2.声明宏和结构体声明套接字缓冲区和一次发送文件数据的缓冲区大小#define SOCKET_BUFF 80000 //套接字缓冲区大小#define PACK_BUFF 50000 //数据包缓冲区大小声明文件I/O缓冲区和最大文件路径长度#define FILE_NAME_MAX 100 //文件路径最大长度#define FILE_IO_BUFF PACK_BUFF //文件IO缓冲区//文件信息typedef struct _FileInfor{u_long ulFileLen;char sFileName[ FILE_NAME_MAX ];}_FileInfor;//数据包typedef struct _DataPack{char cType; //'D'为数据 'M'为文件信息int nPackLen;char sContent[ PACK_BUFF ]; //数据包缓冲区u_long nPosition; //数据在文件中的位置int nContentLen; //数据字节数_FileInfor FileInfor; //文件信息}_DataPack;3.发送端//发送线程需要的全局变量char sPath[FILE_NAME_MAX]; //文件地址u_long FileByteCount; //文件大小SOCKET ClientSocket; //(1)设置套接字发送缓冲区大小,在32位Windows XP环境下,系统为每个套接字分配的默认发送数据缓冲区为8192字节。
Linux下基于TCP/IP协议的文件传输程序【设计目的】通过Linux C编程,设计一个基于TCP/IP的文件传输系统,实现网络文件的收发【设计环境】Ubuntu 12.04【设计方案】(1)文件读写任意文件都可以二进制的方式进行读写,为了实现任意文件类型的传输,在读写文件的过程中,必须采用二进制的读写方式。
(2)传输协议为了保证数据的正确性,文件传输需要采用一种可靠的传输协议。
UDP协议实现比较简单,但UDP面向无连接,传输过程中,会出现丢包的情况,导致数据发送失败。
故采用面向连接的TCP/IP协议,防止传输过程中的数据丢失的情况。
(3)大文件的传输对于比较大的文件,应该进行分包操作,以防止占用过多的内存,导致文件发送失败。
【设计流程】如图1所示,服务器程序作为文件的发送方。
首先,服务器端输入要发送的文件。
然后,创建一个流式套接字(SOCK_STREAM),进行绑定。
绑定成功后,执行监听,当有客户发送连接请求,执行Accept(),接收来自客户端的请求。
连接建立后,首先服务器向客服端发送的文件的文件名及扩展名等信息。
信息发送完毕,服务器方将待发送的数据读入缓冲区,通过套接字将数据发送出去。
发送完成后退出,并显示发送完成的信息。
图1 服务器流程图如图2所示,客户端程序完成文件的接收操作。
首先,创建一个流式套接字。
套接字创建成功后,对该套接字进行绑定。
绑定成功后,向服务器方发送连接请求。
连接成功后,首先,接收服务器发送的文件信息。
接收成功后,开始数据的接收。
文件接收完毕,显示文件已接收完成。
图2 客户端流程图【设计测试】了验证设计的正确性,在Ubuntu 12.04系统上对可执行文件进行了回环测试。
步骤如下:(1)测试文件a.txt及服务器端文件源fileserver.c和可执行文件fileserver(2)客户端源文件及可执行文件(3)执行服务器端文件fileserver并输入要传输的文件a.txt,等待客户端连接(4)执行客户端文件fileclient,如果不输入IP地址将显示提示(5)执行客户端文件后,输入完整命令,文件传输文件完成(6)看到服务器端也显示文件传输完成服务器源码:#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>#define PORT 6000#define LISTENQ 20#define BUFFSIZE 4096#define FILE_NAME_MAX_SIZE 512int main(int argc, char **argv[]){//输入文件名称char filename[FILE_NAME_MAX_SIZE];bzero(filename,FILE_NAME_MAX_SIZE);printf("Please input the file name you wana to send:");scanf("%s",&filename);getchar();//设置一个socket地址结构int sockfd,connfd;struct sockaddr_in svraddr,clientaddr;bzero(&svraddr,sizeof(svraddr));//把一段内存区的内容全部设置为0 svraddr.sin_family=AF_INET;svraddr.sin_addr.s_addr=htonl(INADDR_ANY);svraddr.sin_port=htons(PORT);//创建用于internet的流协议(TCP)socketsockfd=socket(AF_INET,SOCK_STREAM,0);if(sockfd<0){perror("socket");exit(1);}//把socket和socket地址结构绑定if(bind(sockfd,(struct sockaddr*)&svraddr,sizeof(svraddr))<0){perror("bind");exit(1);}//监听if(listen(sockfd,LISTENQ)<0){perror("listen");exit(1);}while(1)//服务器端一直运行{socklen_t length=sizeof(clientaddr);//等待请求connfd=accept(sockfd,(struct sockaddr*)&clientaddr,&length);if(connfd<0){perror("connect");exit(1);}//发送文件信息char buff[BUFFSIZE];int count;bzero(buff,BUFFSIZE);strncpy(buff,filename,strlen(filename)>FILE_NAME_MAX_SIZE?FILE_NAME_MA X_SIZE:strlen(filename));count=send(connfd,buff,BUFFSIZE,0);if(count<0){perror("Send file imformation");exit(1);}//读取文件并发送文件FILE *fd=fopen(filename,"rb");if(fd==NULL){printf("File :%s not found!\n",filename);}else{bzero(buff,BUFFSIZE);int file_block_length=0;while((file_block_length=fread(buff,sizeof(char),BUFFSIZE,fd))>0){printf("file_block_length:%d\n",file_block_length);if(send(connfd,buff,file_block_length,0)<0){perror("Send");exit(1);}bzero(buff,BUFFSIZE);}fclose(fd);printf("Transfer file finished !\n");}close(connfd);}close(sockfd);//关闭socketreturn 0;}客户端源码:#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>#define PORT 6000#define LISTENQ 20#define BUFFSIZE 4096#define FILE_NAME_MAX_SIZE 512int main(int argc, char **argv[]){int clientfd;if(argc!=2)//判断参数是否完整{fprintf(stderr,"Usage:./fileclient <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);//INADDR_ANY表示自动获取本机地址clientaddr.sin_port=htons(0); //0表示让系统自动分配一个空闲端口//创建用于internet的流协议(TCP)socket,用clientfd代表客户机socketclientfd=socket(AF_INET,SOCK_STREAM,0);if(clientfd<0){perror("socket");exit(1);}//把客户机的socket和客户机的socket地址结构联系起来if(bind(clientfd,(struct sockaddr*)&clientaddr,sizeof(clientaddr))<0){perror("bind");exit(1);}//设置一个socket地址结构体struct sockaddr_in svraddr;bzero(&svraddr,sizeof(svraddr));if(inet_aton(argv[1],&svraddr.sin_addr)==0)//IP地址来自程序的参数{perror("inet_aton");exit(1);}svraddr.sin_family=AF_INET;svraddr.sin_port=htons(PORT);socklen_t svraddrlen=sizeof(svraddr);//向服务器发起连接,连接成功后client_socket代表了客户机和服务器的一个socket连接if(connect(clientfd,(struct sockaddr*)&svraddr,svraddrlen)<0){perror("connect");exit(1);}//接收文件信息char buff[BUFFSIZE];char filename[FILE_NAME_MAX_SIZE];bzero(filename, FILE_NAME_MAX_SIZE);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_SI ZE:strlen(buff));printf("Preparing recv file : %s---from---%s \n",filename,argv[1]);//接收文件FILE *fd=fopen(filename,"wb+");精选文档if(NULL==fd){perror("open");exit(1);}bzero(buff,BUFFSIZE);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);}bzero(buff,BUFFSIZE);}printf("Receieved file:%s from %s finished!\n",filename,argv[1]);fclose(fd);close(clientfd); //关闭socketreturn 0;}可编辑。
在Linux 中,TCP 重传机制由TCP 协议栈自动处理,当发生丢包或者超时时,TCP 协议栈会根据重传机制重新发送数据包,以确保数据的可靠性和完整性。
具体来说,TCP 重传机制包含以下步骤:
1. 超时计时器启动:当发送端发送数据包后,接收端没有及时响应,发送端会启动一个超时计时器,等待一段时间。
2. 重传定时器到期:如果在计时器设置的时间内仍未收到接收端的确认应答,发送端会重新发送数据包。
3. 重复ACK 机制:如果接收端接收到重复的数据包,会向发送端发送重复ACK 应答,告诉发送端该数据包已经接收到。
4. 确认应答机制:接收端接收到数据包后会向发送端发送确认应答,告诉发送端该数据已经接收。
5. 重试次数限制:如果数据包在一定次数内仍未被接收端确认,发送端会停止重传,并向上层程序返回错误信息。
需要注意的是,TCP 协议栈中的重传机制是基于时间的,而不是基于数据包的。
也就是说,每个数据包都有一个超时时间,如果超过这个时间仍未收到确认应答,就会启动重传机制。
同时,TCP 协议栈也会根据网络状况动态调整超时时间,以提高传输效率和可靠性。
使用TCP协议实现文件传输2013-01-18 10:35:43 我来说两句作者:hanchaoqi收藏我要投稿使用TCP协议实现文件传输。
程序会分为服务器端和客户端,首先运行服务器端,监听来自客户端的连接,客户端运行后会通过程序内的服务器端IP地址,向服务器发送连接请求。
双方建立请求之后,客户端将所需文件的文件名和绝对路径传输给服务器,如果服务器找到此文件,则将此文件传输给客户端,然后断开连接。
具体算法描述如下:【1】服务器端:1、初始化socket服务2、监听连接请求并做相应的处理2.1创建监听套接字2.2监听套接口2.3接受套接字的连接2.4接收客户端传来的数据case 文件绝对路径:按照路径找到文件,并打开。
提取本地文件名,发回给客户端发送文件总长度给客户端case 已准备接收文件完毕if 发送缓冲区为空读取文件,写入缓冲区将文件流分成大小相同的组(最后一组可能会小一点),顺次发送给客户端将缓冲区清空case 文件成功传送打印消息,退出case 文件已存在打印消息,退出2.5关闭同客户端的连接3、释放socket服务【2】客户端:1、初始化socket,winsock服务2、连接服务器,进行数据的传输2.1初始化,创建套接字2.2通过IP地址,向服务器发送连接请求,建立连接2.3主动发送所求文件绝对路径2.4接受服务器端数据并做相应处理case 打开文件错误:重新发送文件绝对路径至服务器,请求重发case 文件长度:打印消息case 文件名:if 文件已经存在发送“文件已经存在”else分配缓冲区,并向服务器发送“Ready”消息case 文件流:为已接收文件名创建文件打开文件,将文件流数据写入文件,直至接收所有分组数据发送“成功接收“消息3、关闭套接字释放服务源程序:【1】服务器端:头文件:[cpp]/*server.h*/#pragma comment(lib, "WS2_32")#include <WinSock2.h>#include <iostream>#include <assert.h>#include<Windows.h>#ifndef COMMONDEF_H#define COMMONDEF_H#define MAX_PACKET_SIZE 10240 // 数据包的最大长度,单位是sizeof(char)#define MAXFILEDIRLENGTH 256 // 存放文件路径的最大长度#define PORT 4096 // 端口号//#define SERVER_IP "127.0.0.1" // server端的IP地址// 各种消息的宏定义#define INVALID_MSG -1 // 无效的消息标识#define MSG_FILENAME 1 // 文件的名称#define MSG_FILELENGTH 2 // 传送文件的长度#define MSG_CLIENT_READY 3 // 客户端准备接收文件#define MSG_FILE 4 // 传送文件#define MSG_SENDFILESUCCESS 5 // 传送文件成功#define MSG_OPENFILE_ERROR 10 // 打开文件失败,可能是文件路径错误找不到文件等原因#define MSG_FILEALREADYEXIT_ERROR 11 // 要保存的文件已经存在了class CCSDef{public:#pragma pack(1) // 使结构体的数据按照1字节来对齐,省空间// 消息头struct TMSG_HEADER{char cMsgID; // 消息标识TMSG_HEADER(char MsgID = INVALID_MSG): cMsgID(MsgID){}};// 请求传送的文件名// 客户端传给服务器端的是全路径名称// 服务器传回给客户端的是文件名struct TMSG_FILENAME : public TMSG_HEADER{char szFileName[256]; // 保存文件名的字符数组TMSG_FILENAME(): TMSG_HEADER(MSG_FILENAME){}};// 传送文件长度struct TMSG_FILELENGTH : public TMSG_HEADER{long lLength;TMSG_FILELENGTH(long length): TMSG_HEADER(MSG_FILELENGTH), lLength(length) {}};// Client端已经准备好了,要求Server端开始传送文件struct TMSG_CLIENT_READY : public TMSG_HEADER{TMSG_CLIENT_READY(): TMSG_HEADER(MSG_CLIENT_READY){}};// 传送文件struct TMSG_FILE : public TMSG_HEADER{union // 采用union保证了数据包的大小不大于MAX_PACKET_SIZE * sizeof(char){char szBuff[MAX_PACKET_SIZE];struct{int nStart;int nSize;char szBuff[MAX_PACKET_SIZE - 2 * sizeof(int)]; }tFile;};TMSG_FILE(): TMSG_HEADER(MSG_FILE){}};// 传送文件成功struct TMSG_SENDFILESUCCESS : public TMSG_HEADER {TMSG_SENDFILESUCCESS(): TMSG_HEADER(MSG_SENDFILESUCCESS){}};// 传送出错信息,包括:// MSG_OPENFILE_ERROR:打开文件失败// MSG_FILEALREADYEXIT_ERROR:要保存的文件已经存在了struct TMSG_ERROR_MSG : public TMSG_HEADER{TMSG_ERROR_MSG(char cErrorMsg): TMSG_HEADER(cErrorMsg){}};#pragma pack()};#endifcpp文件:[cpp]/*Server.cpp*/#include"Server.h"char g_szNewFileName[MAXFILEDIRLENGTH];char g_szBuff[MAX_PACKET_SIZE + 1];long g_lLength;char* g_pBuff = NULL;//初始化socket库bool InitSocket();//关闭socket库bool CloseSocket();//解析消息并进行相应的处理bool ProcessMsg(SOCKET sClient);//监听Client消息void ListenToClient();//打开文件bool OpenFile(CCSDef::TMSG_HEADER* pMagHeader,SOCKET sClient); //传送文件bool SendFile(SOCKET sClient);//读取文件进缓冲区bool ReadFile(SOCKET sClient);int main(){while(1){InitSocket();ListenToClient();CloseSocket();system("del E:\\test1.A_exp");}//system("pause");return 0;}//初始化socket库bool InitSocket(){WSADATA wsaData;WORD socketVersion=MAKEWORD(2,2);if(::WSAStartup(socketVersion,&wsaData)!=0){//初始化WinSock服务printf("Init socket dll error\n");return false;}return true;}//关闭socket库bool CloseSocket(){//释放winsock库::WSACleanup();if(g_pBuff != NULL){delete [] g_pBuff;g_pBuff = NULL;}return true;}//解析消息并进行相应的处理bool ProcessMsg(SOCKET sClient){//从套接口中接收数据,返回copy的字节数int nRecv = ::recv(sClient,g_szBuff,MAX_PACKET_SIZE+1,0); if(nRecv>0){g_szBuff[nRecv]='\0';}//解析命令CCSDef::TMSG_HEADER*pMsgHeader=(CCSDef::TMSG_HEADER*)g_szBuff;switch(pMsgHeader->cMsgID){case MSG_FILENAME://文件名{OpenFile(pMsgHeader,sClient);}break;case MSG_CLIENT_READY://客户端已准备完毕,开始传送文件{SendFile(sClient);}break;case MSG_SENDFILESUCCESS://传送文件成功{printf("Send File Success!\n");return false;}break;case MSG_FILEALREADYEXIT_ERROR://要保存的文件已经存在{printf("The file ready to send already exit!\n"); return false;}break;}return true;}//监听Client消息void ListenToClient(){//创建套接字SOCKET sListen = ::socket(AF_INET, SOCK_STREAM,IPPROTO_TCP);if(sListen == SOCKET_ERROR){printf("Init Socket Error!\n");return;}//绑定socketsockaddr_in sin;sin.sin_family=AF_INET;sin.sin_port=htons(PORT);sin.sin_addr.S_un.S_addr=INADDR_ANY;if (::bind(sListen, (LPSOCKADDR)&sin, sizeof(sockaddr_in)) == SOCKET_ERROR){printf("Bind Error!\n");return;}// 设置socket进入监听状态if(::listen(sListen,10)==SOCKET_ERROR){printf("Listen Error!\n");return;}printf("Listening To Client...\n");//循环接收client端的连接请求sockaddr_in ClientAddr;int nAddrLen = sizeof(sockaddr_in);SOCKET sClient;//取队列最前端客户连接请求,创建套接字连接通道while((sClient=::accept(sListen,(sockaddr*)&ClientAddr,&nAd drLen))==INVALID_SOCKET){}//解析消息并进行相应的处理//int count=10;//作为定时当程序执行10s未完成时直接退出//while(ProcessMsg(sClient)==true&&count>0)//{// Sleep(1000);// count--;while(ProcessMsg(sClient)==true){Sleep(1000);}//关闭同客户端的连接::closesocket(sClient);::closesocket(sListen);}//打开文件bool OpenFile(CCSDef::TMSG_HEADER* pMsgHeader,SOCKET sClient) {CCSDef::TMSG_FILENAME*pRequstFileNameMsg=(CCSDef::TMSG_FILENAME*)pMsgHeader;//对文件名进行处理char *p1,*p2;for(p1=pRequstFileNameMsg->szFileName,p2=g_szNewFileName;*p 1!='\0';p1++,p2++){if(*p1!='\n'){*p2=*p1;}if(*p2=='\\')//将‘\’转换为‘\\’{*(++p2)='\\';}}*p2='\0';ReadFile(sClient);return true;}//传送文件bool SendFile(SOCKET sClient){if (NULL == g_pBuff){//如果缓冲区为空ReadFile(sClient);}int nPacketBufferSize = MAX_PACKET_SIZE - 2 * sizeof(int); // 每个数据包存放文件的buffer大小// 如果文件的长度大于每个数据包所能传送的buffer长度那么就分块传送for (int i = 0; i < g_lLength; i += nPacketBufferSize)CCSDef::TMSG_FILE tMsgFile;tMsgFile.tFile.nStart = i;if (i + nPacketBufferSize + 1> g_lLength){//文件块已经是最后一块tMsgFile.tFile.nSize = g_lLength - i;}else{tMsgFile.tFile.nSize = nPacketBufferSize;}memcpy(tMsgFile.tFile.szBuff, g_pBuff +tMsgFile.tFile.nStart, tMsgFile.tFile.nSize);//copy到缓冲区::send(sClient, (char*)(&tMsgFile),sizeof(CCSDef::TMSG_FILE), 0);Sleep(0.5);}delete [] g_pBuff;g_pBuff = NULL;return true;}//读取文件进缓冲区bool ReadFile(SOCKET sClient){if(g_pBuff!=NULL){//如果缓冲区不为空return true;}//打开文件FILE *pFile;if((pFile = fopen(g_szNewFileName, "rb"))==NULL){//文件打开失败,发送错误报告printf("Cannot find the file, request the client input file name again\n");CCSDef::TMSG_ERROR_MSGtMsgErrorMsg(MSG_OPENFILE_ERROR);::send(sClient, (char*)(&tMsgErrorMsg),sizeof(CCSDef::TMSG_ERROR_MSG), 0);return false;}//传送文件长度到Clientfseek(pFile,0,SEEK_END);//重定位指针到文件末尾g_lLength=ftell(pFile);//返回文件指针相对于文件头的偏移量printf("File Length = %d\n", g_lLength);CCSDef::TMSG_FILELENGTH tMsgFileLength(g_lLength);::send(sClient,(char*)(&tMsgFileLength),sizeof(CCSDef::TMSG_FILELENGTH), 0);// 处理文件全路径名,把文件名分解出来//磁盘号,目录,文件名,后缀名char szDrive[_MAX_DRIVE], szDir[_MAX_DIR],szFname[_MAX_FNAME], szExt[_MAX_EXT];_splitpath(g_szNewFileName, szDrive, szDir, szFname, szExt);strcat(szFname,szExt);CCSDef::TMSG_FILENAME tMsgFileName;strcpy(tMsgFileName.szFileName, szFname);printf("Send File Name: %s\n", tMsgFileName.szFileName); ::send(sClient, (char*)(&tMsgFileName),sizeof(CCSDef::TMSG_FILENAME), 0);//分配缓冲区,读取文件内容g_pBuff = new char[g_lLength + 1];if (g_pBuff == NULL){return false;}fseek(pFile, 0, SEEK_SET);fread(g_pBuff, sizeof(char), g_lLength, pFile);g_pBuff[g_lLength] = '\0';fclose(pFile);return true;}【2】客户端:头文件同服务器端头文件源程序文件:[cpp]/*Client.cpp*/#include"Client.h"long g_lLength = 0;char* g_pBuff = NULL;char g_szFileName[MAXFILEDIRLENGTH];char g_szBuff[MAX_PACKET_SIZE+1];SOCKET g_sClient;// 初始化socket库bool InitSocket();// 关闭socket库bool CloseSocket();// 把用户输入的文件路径传送到server端bool SendFileNameToServer();// 与server端连接bool ConectToServer();// 打开文件失败bool OpenFileError(CCSDef::TMSG_HEADER *pMsgHeader);// 分配空间以便写入文件bool AllocateMemoryForFile(CCSDef::TMSG_HEADER *pMsgHeader); // 写入文件bool WriteToFile(CCSDef::TMSG_HEADER *pMsgHeader);// 处理server端传送过来的消息bool ProcessMsg();int main(){while(1){InitSocket();ConectToServer();CloseSocket();}//system("pause");return 0;}// 初始化socket库bool InitSocket(){//初始化SOCKETWSADATA wsaData;WORD socketVersion=MAKEWORD(2,2);if(::WSAStartup(socketVersion,&wsaData)!=0){printf("Init socket dll error\n");exit(-1);}return true;}// 关闭socket库bool CloseSocket(){// 关闭套接字::closesocket(g_sClient);// 释放winsock库::WSACleanup();return true;}// 把用户输入的文件路径传送到server端bool SendFileNameToServer(){char szFileName[MAXFILEDIRLENGTH];printf("Input the File Directory: ");//fgets(szFileName, MAXFILEDIRLENGTH, stdin);strcpy(szFileName,"E:\\test1.A_exp");// 把文件路径发到server端CCSDef::TMSG_FILENAME tMsgRequestFileName;strcpy(tMsgRequestFileName.szFileName, szFileName);if (::send(g_sClient, (char*)(&tMsgRequestFileName),sizeof(CCSDef::TMSG_FILENAME), 0) == SOCKET_ERROR){printf("Send File Name Error!\n");exit(-1);}return true;}// 与server端连接bool ConectToServer(){// 初始化socket套接字if ((g_sClient = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == SOCKET_ERROR){printf("Init Socket Error!\n");exit(-1);}sockaddr_in servAddr;servAddr.sin_family = AF_INET;servAddr.sin_port = htons(PORT);servAddr.sin_addr.S_un.S_addr = ::inet_addr(SERVER_IP); if ((::connect(g_sClient, (sockaddr*)&servAddr,sizeof(sockaddr_in))) == INVALID_SOCKET){printf("Connect to Server Error!\n");exit(-1);}// 输入文件路径传输到server端SendFileNameToServer();// 接收server端传过来的信息,直到保存文件成功为止while (ProcessMsg() == true){Sleep(1000);}return true;}// 打开文件失败bool OpenFileError(CCSDef::TMSG_HEADER *pMsgHeader){if (g_pBuff != NULL)//如果缓冲区内有数据return true;assert(pMsgHeader != NULL);printf("Cannot find file!\n");// 重新输入文件名称SendFileNameToServer();return true;}// 分配空间以便写入文件bool AllocateMemoryForFile(CCSDef::TMSG_HEADER *pMsgHeader) {assert(pMsgHeader != NULL);if (g_pBuff != NULL){return true;}CCSDef::TMSG_FILENAME* pRequestFilenameMsg = (CCSDef::TMSG_FILENAME*)pMsgHeader;printf("File Name: %s\n",pRequestFilenameMsg->szFileName);// 把文件的路径设置为D盘根目录下strcpy(g_szFileName, "D:\\");strcat(g_szFileName, "test2.B_imp");//strcat(g_szFileName, pRequestFilenameMsg->szFileName); // 查找相同文件名的文件是否已经存在,如果存在报错退出FILE* pFile;if ((pFile = fopen(g_szFileName, "r")) != NULL){// 文件已经存在,要求重新输入一个文件printf("The file already exist!\n");CCSDef::TMSG_ERROR_MSGtMsgErrorMsg(MSG_FILEALREADYEXIT_ERROR);::send(g_sClient, (char*)(&tMsgErrorMsg),sizeof(CCSDef::TMSG_ERROR_MSG), 0);fclose(pFile);return false;}// 分配缓冲区开始接收文件,如果分配成功就给server端发送开始传送文件的要求g_pBuff = new char[g_lLength + 1];if (g_pBuff != NULL){memset(g_pBuff, '\0', g_lLength + 1);printf("Now ready to get the file %s!\n", pRequestFilenameMsg->szFileName);CCSDef::TMSG_CLIENT_READY tMsgClientReady;if (::send(g_sClient, (char*)(&tMsgClientReady),sizeof(CCSDef::TMSG_CLIENT_READY), 0) == SOCKET_ERROR){printf("Send Error!\n");exit(-1);}}else{printf("Alloc memory for file error!\n");exit(-1);}return true;}// 写入文件bool WriteToFile(CCSDef::TMSG_HEADER *pMsgHeader){assert(pMsgHeader != NULL);CCSDef::TMSG_FILE* pMsgFile =(CCSDef::TMSG_FILE*)pMsgHeader;int nStart = pMsgFile->tFile.nStart;int nSize = pMsgFile->tFile.nSize;memcpy(g_pBuff + nStart, pMsgFile->tFile.szBuff, nSize); if (nStart == 0){printf("Saving file into buffer...\n");}memcpy(g_pBuff + nStart, pMsgFile->tFile.szBuff, nSize); // 如果已经保存到缓冲区完毕就写入文件if (nStart + nSize >= g_lLength){printf("Writing to disk....\n");// 写入文件FILE* pFile;pFile = fopen(g_szFileName, "w+b");fwrite(g_pBuff, sizeof(char), g_lLength, pFile);delete [] g_pBuff;g_pBuff = NULL;fclose(pFile);// 保存文件成功传送消息给server退出serverCCSDef::TMSG_SENDFILESUCCESS tMsgSendFileSuccess;while (::send(g_sClient, (char*)(&tMsgSendFileSuccess), sizeof(CCSDef::TMSG_SENDFILESUCCESS), 0) == SOCKET_ERROR){}printf("Save the file %s success!\n", g_szFileName); return true;}else{return false;}}// 处理server端传送过来的消息bool ProcessMsg(){CCSDef::TMSG_HEADER *pMsgHeader;int nRecv = ::recv(g_sClient, g_szBuff, MAX_PACKET_SIZE + 1, 0);pMsgHeader = (CCSDef::TMSG_HEADER*)g_szBuff;switch (pMsgHeader->cMsgID){case MSG_OPENFILE_ERROR: // 打开文件错误{OpenFileError(pMsgHeader);}break;case MSG_FILELENGTH: // 文件的长度{if (g_lLength == 0){g_lLength =((CCSDef::TMSG_FILELENGTH*)pMsgHeader)->lLength;printf("File Length: %d\n", g_lLength);}}break;case MSG_FILENAME: // 文件名{return AllocateMemoryForFile(pMsgHeader);}break;case MSG_FILE: // 传送文件,写入文件成功之后退出这个函数{ if (WriteToFile(pMsgHeader)){/*Sleep(1000);*/return false;}}break;}return true;}。
Linux下基于TCP/IP协议的文件传输程序【设计目的】通过Linux C编程,设计一个基于TCP/IP的文件传输系统,实现网络文件的收发【设计环境】Ubuntu 12.04【设计方案】(1)文件读写任意文件都可以二进制的方式进行读写,为了实现任意文件类型的传输,在读写文件的过程中,必须采用二进制的读写方式。
(2)传输协议为了保证数据的正确性,文件传输需要采用一种可靠的传输协议。
UDP协议实现比较简单,但UDP面向无连接,传输过程中,会出现丢包的情况,导致数据发送失败。
故采用面向连接的TCP/IP协议,防止传输过程中的数据丢失的情况。
(3)大文件的传输对于比较大的文件,应该进行分包操作,以防止占用过多的内存,导致文件发送失败。
【设计流程】如图1所示,服务器程序作为文件的发送方。
首先,服务器端输入要发送的文件。
然后,创建一个流式套接字(SOCK_STREAM),进行绑定。
绑定成功后,执行监听,当有客户发送连接请求,执行Accept(),接收来自客户端的请求。
连接建立后,首先服务器向客服端发送的文件的文件名及扩展名等信息。
信息发送完毕,服务器方将待发送的数据读入缓冲区,通过套接字将数据发送出去。
发送完成后退出,并显示发送完成的信息。
图1 服务器流程图如图2所示,客户端程序完成文件的接收操作。
首先,创建一个流式套接字。
套接字创建成功后,对该套接字进行绑定。
绑定成功后,向服务器方发送连接请求。
连接成功后,首先,接收服务器发送的文件信息。
接收成功后,开始数据的接收。
文件接收完毕,显示文件已接收完成。
图2 客户端流程图【设计测试】了验证设计的正确性,在Ubuntu 12.04系统上对可执行文件进行了回环测试。
步骤如下:(1)测试文件a.txt及服务器端文件源fileserver.c和可执行文件fileserver(2)客户端源文件及可执行文件(3)执行服务器端文件fileserver并输入要传输的文件a.txt,等待客户端连接(4)执行客户端文件fileclient,如果不输入IP地址将显示提示(5)执行客户端文件后,输入完整命令,文件传输文件完成(6)看到服务器端也显示文件传输完成服务器源码:#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>#define PORT 6000#define LISTENQ 20#define BUFFSIZE 4096#define FILE_NAME_MAX_SIZE 512int main(int argc, char **argv[]){//输入文件名称char filename[FILE_NAME_MAX_SIZE];bzero(filename,FILE_NAME_MAX_SIZE);printf("Please input the file name you wana to send:");scanf("%s",&filename);getchar();//设置一个socket地址结构int sockfd,connfd;struct sockaddr_in svraddr,clientaddr;bzero(&svraddr,sizeof(svraddr));//把一段内存区的内容全部设置为0svraddr.sin_family=AF_INET;svraddr.sin_addr.s_addr=htonl(INADDR_ANY);svraddr.sin_port=htons(PORT);//创建用于internet的流协议(TCP)socketsockfd=socket(AF_INET,SOCK_STREAM,0);if(sockfd<0){perror("socket");exit(1);}//把socket和socket地址结构绑定if(bind(sockfd,(struct sockaddr*)&svraddr,sizeof(svraddr))<0){perror("bind");exit(1);}//监听if(listen(sockfd,LISTENQ)<0){perror("listen");exit(1);}while(1)//服务器端一直运行{socklen_t length=sizeof(clientaddr);//等待请求connfd=accept(sockfd,(struct sockaddr*)&clientaddr,&length);if(connfd<0){perror("connect");exit(1);}//发送文件信息char buff[BUFFSIZE];int count;bzero(buff,BUFFSIZE);strncpy(buff,filename,strlen(filename)>FILE_NAME_MAX_SIZE?FILE_NAME_MAX_SIZE:st rlen(filename));count=send(connfd,buff,BUFFSIZE,0);if(count<0){perror("Send file imformation");exit(1);}//读取文件并发送文件FILE *fd=fopen(filename,"rb");if(fd==NULL){printf("File :%s not found!\n",filename);}else{bzero(buff,BUFFSIZE);int file_block_length=0;while((file_block_length=fread(buff,sizeof(char),BUFFSIZE,fd))>0){printf("file_block_length:%d\n",file_block_length);if(send(connfd,buff,file_block_length,0)<0){perror("Send");exit(1);}bzero(buff,BUFFSIZE);}fclose(fd);printf("Transfer file finished !\n");}close(connfd);}close(sockfd);//关闭socketreturn 0;}客户端源码:#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>#define PORT 6000#define LISTENQ 20#define BUFFSIZE 4096#define FILE_NAME_MAX_SIZE 512int main(int argc, char **argv[]){int clientfd;if(argc!=2)//判断参数是否完整{fprintf(stderr,"Usage:./fileclient <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);//INADDR_ANY表示自动获取本机地址clientaddr.sin_port=htons(0); //0表示让系统自动分配一个空闲端口//创建用于internet的流协议(TCP)socket,用clientfd代表客户机socketclientfd=socket(AF_INET,SOCK_STREAM,0);if(clientfd<0){perror("socket");exit(1);}//把客户机的socket和客户机的socket地址结构联系起来if(bind(clientfd,(struct sockaddr*)&clientaddr,sizeof(clientaddr))<0){perror("bind");exit(1);}//设置一个socket地址结构体struct sockaddr_in svraddr;bzero(&svraddr,sizeof(svraddr));if(inet_aton(argv[1],&svraddr.sin_addr)==0)//IP地址来自程序的参数{perror("inet_aton");exit(1);}svraddr.sin_family=AF_INET;svraddr.sin_port=htons(PORT);socklen_t svraddrlen=sizeof(svraddr);//向服务器发起连接,连接成功后client_socket代表了客户机和服务器的一个socket 连接if(connect(clientfd,(struct sockaddr*)&svraddr,svraddrlen)<0){perror("connect");exit(1);}//接收文件信息char buff[BUFFSIZE];char filename[FILE_NAME_MAX_SIZE];bzero(filename, FILE_NAME_MAX_SIZE);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));printf("Preparing recv file : %s---from---%s \n",filename,argv[1]);//接收文件FILE *fd=fopen(filename,"wb+");if(NULL==fd){perror("open");exit(1);}bzero(buff,BUFFSIZE);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);}bzero(buff,BUFFSIZE);}printf("Receieved file:%s from %s finished!\n",filename,argv[1]);fclose(fd);close(clientfd); //关闭socketreturn 0;}。