TCP實現文件傳輸

TCP協議實現文件傳輸

 使用TCP協議實現傳輸文件
    程序分爲發送端和接收端。首先在傳輸文件數據之前,發送端會把將裝有文件名稱和文件長度等
信息的數據包發送至接收端。接收端收到文件名稱和文件長度信息後會創建好空白文件。接着開始傳輸
文件數據。下面介紹實現功能的主要過程:

1.創建套接字、綁定、監聽、連接、接受連接
//創建TCP協議的套接字
    m_Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    
if(SOCKET_ERROR == m_Socket)
        AfxMessageBox(
"Create Socket Error! "00);

//綁定與監聽
    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!"00);

//連接
    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 sockaddr));
    
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字節。由於傳輸的文件很大,可能幾十兆,或者更大。那麼系統爲每個套接字分配的默認
緩衝區顯然過小。爲此在創建套接字之後,需要修改套接字發送數據緩衝尺寸。在這裏我修改爲80k,
差不多可以夠用了。
    
//設置套接字發送緩衝區
    int nBuf = SOCKET_BUFF;
    
int nBufLen = sizeof(nBuf);
    
int nRe = setsockopt(ClientSock, SOL_SOCKET, SO_SNDBUF, (char*)&nBuf, nBufLen);
    
if(SOCKET_ERROR == nRe)
        AfxMessageBox(
"setsockopt error!");    
    
//檢查緩衝區是否設置成功
    nRe = getsockopt(ClientSock, SOL_SOCKET, SO_SNDBUF, (char*)&nBuf, &nBufLen);
    
if(SOCKET_BUFF != nBuf)
        AfxMessageBox(
"檢查緩衝區:setsockopt error!");

(
2)測量文件大小併發送文件大小和名稱給客戶端

    首先使用C庫函數對源文件進行測量
    
//得到文件地址
    LPTSTR lpPath =     m_sPath.GetBuffer(    m_sPath.GetLength ());
    
//打開文件
    FILE *File = fopen(lpPath, "rb"); 
    
if(NULL == File)
        AfxMessageBox(
"打開文件失敗!");
    
//測量文件大小
    char Buff[PACK_BUFF];
    u_long ulFaceReadByte;
    FileByteCount 
= 0;
    fseek(File, 
0, SEEK_SET);
    
while(!feof(File))
    {
        ulFaceReadByte 
= fread(Buff, 11, File);
        FileByteCount 
+= ulFaceReadByte;
    }
    
//關閉文件
    int nRe = fclose(File);
    
if(nRe)
        AfxMessageBox(
"關閉文件失敗!");
        
    此時以獲取源文件的長度,我們將文件長度和文件名稱放到數據包中,設置數據包爲
'M'類型。
    
//打包
    _DataPack Pack;
    Pack.cType 
= 'M';
    Pack.nPackLen 
= sizeof(Pack);
    
//取得文件名
    ZeroMemory(Pack.FileInfor.sFileName, FILE_NAME_MAX);
    GetFIieNameFromPath(lpPath, Pack.FileInfor.sFileName);
    Pack.FileInfor.ulFileLen 
= FileByteCount;
    
    接着使用send()將打包完成的數據包發送給接收端,把發送線程的全局變量初始化,並創建發送線
程,文件數據將由發送線程負責發送
    
//發送數據包 文件大小和名稱
    nRe = send(m_ClientSockFd.fd_array[0], (char*)&Pack, Pack.nPackLen, 0);
    
if(SOCKET_ERROR == nRe)
            AfxMessageBox(
"Send File Size Failed!");
    
//線程準備全局變量
    strcpy(sPath, m_sPath);
    ClientSocket 
= m_ClientSockFd.fd_array[0];
    
//啓動線程發送文件
    DWORD ID;
    m_hSendThread 
= CreateThread(00, SendDataThrad, 00&ID);


(
3)發送文件數據線程。先打開源文件,爲了一次讀取大量數據將文件緩衝區設置爲FILE_IO_BUFF大小,
然後將讀取的數據打包併發送。這樣不斷地讀、不斷地發送,直到將整個文件發送完爲止。

DWORD __stdcall CServerDlg::SendDataThrad(LPVOID LpP)
{
    
//打開文件
    FILE *File = fopen(sPath, "rb");
    
if(NULL == File)
    {
        AfxMessageBox(
"SendDataThrad中打開文件失敗!");
        
return 1;
    }
    
//設置文件緩衝區
    int nBuff = FILE_IO_BUFF;
    
if(setvbuf(File, (char*)&nBuff, _IOFBF, sizeof(nBuff)))
        AfxMessageBox(
"設置文件緩衝區失敗!");
    
//讀取文件數據併發送
    u_long ulFlagCount = 0;            //記錄讀了多少數據
    u_long FaceReadByte = 0;    //一次實際讀取的字節數
    char sBuff[PACK_BUFF];    
    ZeroMemory(sBuff, PACK_BUFF);
    fseek(File, 
0, SEEK_SET);
    
while (!feof(File))
    {
        FaceReadByte 
= fread(sBuff, 1, PACK_BUFF, File);
        
//打包
        _DataPack DataPack;
        DataPack.cType 
= 'D';
        DataPack.nPackLen 
= sizeof(DataPack);
        DataPack.nContentLen 
= FaceReadByte;
        CopyMemory(DataPack.sContent, sBuff, FaceReadByte);
        DataPack.nPosition 
= ulFlagCount;
        
//發送
        int nResult = send(ClientSocket, (char*)&DataPack, DataPack.nPackLen, 0);
        
if (SOCKET_ERROR == nResult)
        {
            AfxMessageBox(
"SendDataThrad中發送數據失敗!");
        }
else
            ulFlagCount 
+= FaceReadByte;        //記錄發送字節數
    }
    AfxMessageBox(
"發送結束");
    
//關閉
    int nRe = fclose(File);
    
if(nRe)
        AfxMessageBox(
"SendDataThrad中關閉文件失敗!");
    
return 0;
}

4.接收端
//接收線程用的全局變量
_FileInfor FileInfor;        //文件信息
u_long ulWriteByte;            //記錄總共寫入的字節
char lpPath[FILE_NAME_MAX];    //文件路徑
char sFilePathAndName[FILE_NAME_MAX];        //完整的文件路徑

(
1)設置套接字接收緩衝區大小。
//設置套接字接收緩衝區
    int nBuf = SOCKET_BUFF;
    
int nBufLen = sizeof(nBuf);
    SOCKET ClientSock 
= m_Sock.GetSocket();
    
int nRe = setsockopt(ClientSock, SOL_SOCKET, SO_RCVBUF, (char*)&nBuf, nBufLen);
    
if(SOCKET_ERROR == nRe)
            AfxMessageBox(
"setsockopt error!");
    
//檢查緩衝區是否設置成功
    nRe = getsockopt(ClientSock, SOL_SOCKET, SO_RCVBUF, (char*)&nBuf, &nBufLen);
    
if(SOCKET_BUFF != nBuf)
        AfxMessageBox(
"檢查緩衝區:setsockopt error!");
        
(
2)接收文件信息和文件數據線程。先判斷數據包是屬於文件信息還是文件類型,如果是文件信息就根
據信息創建空文件,如果是文件數據就將數據已追加的方式寫進目的文件中。

DWORD __stdcall CClientDlg::ReceiveDataPro(LPVOID LpP)
{
    CSocket_Win32 
*pCSock = (CSocket_Win32*)LpP;
    u_long ulWriteByteCount 
= 0;
    
while (1)
    {
        _DataPack DataPack;
        ZeroMemory(
&DataPack, sizeof(DataPack));
        
if(!(*pCSock).Receive(&DataPack, sizeof(DataPack)))
        {
            AfxMessageBox(
"Receive DataPack Failed!");
        }
        
//判斷數據包類型
        if('M' == DataPack.cType)    //包爲文件信息
        {
            
//接收文件信息
            FileInfor.ulFileLen = DataPack.FileInfor.ulFileLen;  //獲取文件長度
            strcpy(FileInfor.sFileName, DataPack.FileInfor.sFileName); //獲取文件名稱
            
//得到文件目錄
            char sFilePath[FILE_NAME_MAX];
            ZeroMemory(sFilePath, FILE_NAME_MAX);
            strcpy(sFilePath, lpPath);
            strcat(sFilePath, FileInfor.sFileName);
            strcat(sFilePathAndName, sFilePath);    
//保存完整的文件路徑
            
//創建文件
            SECURITY_ATTRIBUTES attr;
            attr.nLength 
= FileInfor.ulFileLen;
            attr.lpSecurityDescriptor 
= NULL;
            HANDLE hRe 
= CreateFile(sFilePath, GENERIC_WRITE, FILE_SHARE_WRITE, &attr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
            
if(INVALID_HANDLE_VALUE == hRe)
                AfxMessageBox(
"創建文件失敗!");
            
bool bRe = ::CloseHandle(hRe);
            
//可以開始接受文件
            bIsStartReceive = true;
        }
else if('D' == DataPack.cType)        //包爲文件數據
        {
            
//打開文件
            char sPath[FILE_NAME_MAX];
            strcpy(sPath, sFilePathAndName);
            FILE 
*File = fopen(sPath, "ab");
            
if(0 == File)
                AfxMessageBox(
"打開文件失敗!");
            
//設置文件緩衝區
            int nBuff = FILE_IO_BUFF;
            
if(setvbuf(File, (char*)&nBuff, _IOFBF, sizeof(nBuff)))
                AfxMessageBox(
"設置文件緩衝區失敗!");
            
//定位文件
            u_long nPosition = DataPack.nPosition;
            
int nRe = fseek(File, nPosition, SEEK_SET);
            
if(nRe)
                AfxMessageBox(
"SendDataThrad中定位失敗!");
            
//寫文件
            u_long nNumberOfBytesWritten = fwrite(&DataPack.sContent, 1, DataPack.nContentLen, File);
            
if(DataPack.nContentLen != nNumberOfBytesWritten)
                AfxMessageBox(
"寫文件失敗!");
            
else
            {
                ulWriteByteCount 
+= nNumberOfBytesWritten;
            }
            fflush(File);                                
//清除文件緩衝區
            
//關閉文件
            nRe = fclose(File);
            
if(nRe)
                AfxMessageBox(
"關閉文件失敗!");

            
if(ulWriteByteCount >= FileInfor.ulFileLen)
            {
                AfxMessageBox(
"接收結束");
                
break;
            }    
        }
    }
    
return 0;
}

發佈了18 篇原創文章 · 獲贊 19 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章