程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 使用TCP協議實現文件傳輸

使用TCP協議實現文件傳輸

編輯:C++入門知識

使用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;   }    

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved