使用TCP协议实现文件传输

来源:互联网 发布:网络平台是干什么的 编辑:程序博客网 时间:2024/05/21 23:31

使用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】服务器端:

头文件:

/*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()};#endif

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,&nAddrLen))==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;*p1!='\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_MSG tMsgErrorMsg(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】客户端:

头文件同服务器端头文件

源程序文件:

/*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_MSG tMsgErrorMsg(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退出server   CCSDef::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;}


原创粉丝点击