TCP协议实现文件传输
?使用TCP協議實現傳輸文件
????程序分為發送端和接收端。首先在傳輸文件數據之前,發送端會把將裝有文件名稱和文件長度等
信息的數據包發送至接收端。接收端收到文件名稱和文件長度信息后會創建好空白文件。接著開始傳輸
文件數據。下面介紹實現功能的主要過程:
1.創建套接字、綁定、監聽、連接、接受連接
//創建TCP協議的套接字
????m_Socket?=?socket(AF_INET,?SOCK_STREAM,?IPPROTO_TCP);
????if(SOCKET_ERROR?==?m_Socket)
????????AfxMessageBox("Create?Socket?Error!?",?0,?0);
//綁定與監聽
????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!",?0,?0);
//連接
????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,?1,?1,?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(0,?0,?SendDataThrad,?0,?0,?&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;
}
?
?
?
轉載于:https://www.cnblogs.com/yunboy4/archive/2009/08/04/1538865.html
總結
以上是生活随笔為你收集整理的TCP协议实现文件传输的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 计算机考研各科目分数,考研各科目及分数
- 下一篇: OPTEE之安全存储详解