使用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() }; #endif cpp文件: [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; } //綁定socket sockaddr_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; } //傳送文件長度到Client fseek(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() { //初始化SOCKET WSADATA 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: // 傳送文件,寫入文件成功之後退出這個函數 { www.2cto.com if (WriteToFile(pMsgHeader)) { /*Sleep(1000);*/ return false; } } break; } return true; }