使用TCP协议实现文件传输
使用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】服务器端:
头文件:
/*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;}