一個封裝好的使用完成埠的socket通訊類
阿新 • • 發佈:2019-02-10
//*********************************************************************************** //檔案說明:TSocket.h //功能: 檔案傳輸客戶端實現標頭檔案 //使用說明:使用TCP的IOCP實現,可以傳送大訊息包、檔案,同一客戶端可以同時傳送多個檔案 // 1、用TClients建立一個物件,pClients // 2、呼叫pClients->CreateClient(...)函式,引數1、2是要連線的服務端IP和埠, // 3服務端返回訊息的事件回撥處理函式,4是服務端斷開連線的事件回撥處理函式 // 3、呼叫pClients->SendMsg(...)給對端發訊息 // 4、呼叫pClients->SendFile(...)給對端發文件 // 5、呼叫pClients->Disconnet(...)主動斷開連線 // 6、銷燬pClients物件 //時間: 2010.5.1 23:13:56 //作者: 廢人 //留待實現: // bool SendFileToAll(const char * filename); // bool SendFileToServers(SOCKETS sClients,const char * filename); // bool SendFileToOther(SOCKET ExceptSocket,char * pData,ULONG Length); // bool SendMsgToAll(char * pData,ULONG Length); // bool SendMsgToServers(SOCKETS sClients,char * pData,ULONG Length); // bool SendMsgToOther(SOCKET ExceptSocket,char * pData,ULONG Length); //*********************************************************************************** #if !defined(AFX_TSOCKET_H__46FFF420_23C3_4356_A88D_AEBDA61EA186__INCLUDED_) #define AFX_TSOCKET_H__46FFF420_23C3_4356_A88D_AEBDA61EA186__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #include <list> #include <afxtempl.h> #include <winsock2.h> #include <vector> #include <queue> #include <string> #include <algorithm> #include <map> #pragma comment(lib,"ws2_32.lib") using namespace std; #pragma warning(disable:4786) //服務埠 #define SVRPORT 10012 //緩衝區大小 #define BUFFER_SIZE 4096 //接收資料 #define RECV_POSTED 0 //傳送資料 #define SEND_POSTED 1 class TClient; class TClients; struct TPack; //單控制代碼資料 typedef struct _PER_HANDLE_DATA { TClient *client; }PER_HANDLE_DATA,*LPPER_HANDLE_DATA; //IO操作資料 typedef struct _PER_IO_OPERATION_DATA { //重疊結構 OVERLAPPED OverLapped; //資料緩衝區 WSABUF RecvDataBuf; WSABUF SendDataBuf; char RecvBuf[BUFFER_SIZE]; char SendBuf[BUFFER_SIZE]; //操作型別表示 bool OperType; }PER_IO_OPERATION_DATA,*PPER_IO_OPERATION_DATA; typedef map<SOCKET,TClient*> TSortClients; //排序的客戶端 typedef map<ULONG,TPack *> TPacks; //有些資料包需要重組才能合成大資料包回撥ProcessRecvData函式,佔時儲存結構 typedef vector<SOCKET> SOCKETS; //回撥處理資料函式原型 typedef void __stdcall ProcessRecvData(char * pData,ULONG DataLength); typedef void __stdcall ProcessPeerClose(); typedef void __stdcall ProcessFileTransfer(char *filename,double speed); DWORD WINAPI ServerWorkerProc(LPVOID lParam); //工作執行緒 DWORD WINAPI ListenProc(LPVOID lParam); ULONG ULSize=sizeof(ULONG); ULONG cSize=sizeof(char); class TData { public: char *data; ULONG totalLen; char *Cur; void AddType(char type); void AddInt(ULONG len); void AddData(char *buf,ULONG len); char GetType(); ULONG GetInt(); char* GetData(ULONG &retlen); }; struct TPack { char *data; char *CurPack; ULONG totalLen; void clear() { CurPack=data; totalLen=0; } void AddData(char*d,ULONG len) { memcpy(CurPack,d,len); CurPack+=len; totalLen+=len; } }; struct TDataMod //用於傳輸的模型,用malloc和free,大小不超過BUFFER_SIZE { char type; //0單獨資料包,1連續訊息資料包頭,2檔案資料包頭,3連續包的訊息體,4檔案包的訊息體,5是否需要銷燬本指標 ULONG Len; ULONG Channel; char data; //1、連續訊息第一個包的 Len是長度,Channel是Send的傳輸號,以後Len就是Send的傳輸號 //2、檔案訊息第一個包的 Len是長度,Channel是Send的傳輸號,以後Len就是Send的傳輸號 //3、單獨包是隻有Len長度,Index是資料內容 }; struct TFileDataHead { char type; ULONG Channel; TCHAR szFileTitle[128]; //檔案的標題名 DWORD dwFileAttributes; //檔案的屬性 FILETIME ftCreationTime; //檔案的建立時間 FILETIME ftLastAccessTime; //檔案的最後訪問時間 FILETIME ftLastWriteTime; //檔案的最後修改時間 DWORD nFileSizeHigh; //檔案大小的高位雙字 DWORD nFileSizeLow; //檔案大小的低位雙字 DWORD dwReserved0; //保留,為0 DWORD dwReserved1; //保留,為0 TCHAR cAlternateFileName; TCHAR cFileName; }; //訊息包的型別: //1、單獨資料包(TData),type==0,Len為長度,data開始儲存的是資料 (MinDataLen) //2、連續訊息資料包頭,type==1,Len為訊息體總長度,Channel是當前通道號,data資訊資料(MinDataHeadLen) //3、連續訊息中間資料包,type==3,Len為Channel號,data資訊資料(MinDataLen) //4、檔案頭TFileDataHead,type==2 //5、檔案中間資料包,type==4,Len為Channel號,data資訊資料(MinDataLen) ULONG MinDataLen=ULSize+cSize; ULONG MinDataHeadLen=MinDataLen+ULSize; ULONG MinFileHeadLen=sizeof(TFileDataHead); class TClient //用於中間實現的類,不能直接使用 { public: TClient(); ~TClient(); void OnReceive(char *data,ULONG len);//data是一個全域性指標 private: void clear(); ProcessPeerClose *m_pfPeerClose; //客戶端訊息回撥函式 ProcessRecvData *m_pfRecvData; //本端管理者指標 TClients *m_ClientManager; //臨時存放分塊資料包進行組裝 TPacks m_Receivepacks; //本端的SOCKET號 SOCKET m_Sock; //對端的IP char m_RemoteIP[16]; //對端的埠 ULONG m_RemotePort; //臨時存放不足一幀的資料 TPack m_tempDta; friend DWORD WINAPI ServerWorkerProc(LPVOID lParam); friend class TClients; }; class TClients //外部介面類,可以用其公有方法介面 { public: TClients(LPCTSTR strLogPath="TClientsLog.log"); ~TClients(); SOCKET CreateClient(const char *pSerIp,ULONG iSvrPort=SVRPORT, ProcessRecvData* OnProcessRecvData=NULL,ProcessPeerClose* OnProcessPeerClose=NULL); bool Disconnet(SOCKET sClient); bool DisconnetAll(); bool SendMsg(SOCKET sClient,char * pData,ULONG Length); bool SendFile(SOCKET sClient,const char * filename,ProcessFileTransfer *OnFileTrans=NULL){return true;} bool GetRemoteAddress(SOCKET sClient,char * IP,ULONG &port); bool GetLocalIP(char *&IP); private: //完成控制代碼 HANDLE CompletionPort; //客戶資訊臨界保護量 CRITICAL_SECTION cClientSection; //當前傳送的通道號 ULONG Channel; //客戶資訊列表 TSortClients m_clients; //寫日誌檔案 char m_LogPath[MAX_PATH]; void WriteLogString(const char *format,...); void InitNetWork(); void UnInit(); private: friend class TClient; friend DWORD WINAPI ServerWorkerProc(LPVOID lParam); }; #endif // !defined(AFX_TSOCKET_H__46FFF420_23C3_4356_A88D_AEBDA61EA186__INCLUDED_)
//*********************************************************************************** //檔案說明:TSocket.cpp //功能: 檔案傳輸客戶端實現標頭檔案 //使用說明:使用TCP的IOCP實現,可以傳送大訊息包、檔案,執行緒安全同一客戶端可以同時傳送訊息、檔案 // 1、用TClients建立一個物件,pClients // 2、呼叫pClients->CreateClient(...)函式,引數1、2是要連線的服務端IP和埠, // 3服務端返回訊息的事件回撥處理函式,4是服務端斷開連線的事件回撥處理函式 // 3、呼叫pClients->SendMsg(...)給對端發訊息 // 4、呼叫pClients->SendFile(...)給對端發文件 // 5、呼叫pClients->Disconnet(...)主動斷開連線 // 6、銷燬pClients物件 //時間: 2010.5.1 23:13:56 //作者: 廢人 //*********************************************************************************** #include "stdafx.h" #include "TSocket.h" #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif #pragma warning(disable:4800) DWORD WINAPI ServerWorkerProc(LPVOID lParam) { TClients* clientManager=(TClients*)lParam; HANDLE CompletionPort=clientManager->CompletionPort; DWORD ByteTransferred; LPPER_HANDLE_DATA PerHandleData; PPER_IO_OPERATION_DATA PerIoData; DWORD RecvByte; while(true) { bool bSuccess=GetQueuedCompletionStatus(CompletionPort, &ByteTransferred, (LPDWORD)&PerHandleData, (LPOVERLAPPED* )&PerIoData, INFINITE); //退出訊號到達,退出執行緒 if(ByteTransferred==-1 && PerIoData==NULL) { return 1L; } //客戶機已經斷開連線或者連接出現錯誤 if(ByteTransferred==0 && (PerIoData->OperType==RECV_POSTED || PerIoData->OperType==SEND_POSTED)) { //將該客戶端資料刪除 ::EnterCriticalSection(&clientManager->cClientSection); clientManager->m_clients.erase(PerHandleData->client->m_Sock); ::LeaveCriticalSection(&clientManager->cClientSection); //記錄退出日誌 clientManager->WriteLogString("Ip: %s,port:%d,Socket : %d Disconneted",PerHandleData->client->m_RemoteIP, PerHandleData->client->m_RemotePort,PerHandleData->client->m_Sock); TRACE("\nSocket : %d Disconneted",PerHandleData->client->m_Sock); //呼叫回撥函式,通知上層該客戶端已經斷開 PerHandleData->client->OnReceive(NULL,0); //關閉套介面 closesocket(PerHandleData->client->m_Sock); GlobalFree(PerHandleData); GlobalFree(PerIoData); continue; } //為讀操作完成,處理資料 if(PerIoData->OperType==RECV_POSTED) { //呼叫回撥函式,處理資料 PerHandleData->client->OnReceive(PerIoData->RecvBuf,ByteTransferred); //將源資料置空 memset(PerIoData->RecvBuf,0,BUFFER_SIZE); ByteTransferred=0; //重置IO操作資料 unsigned long Flag=0; ZeroMemory(&(PerIoData->OverLapped),sizeof(OVERLAPPED)); PerIoData->RecvDataBuf.buf=PerIoData->RecvBuf; PerIoData->RecvDataBuf.len=BUFFER_SIZE; PerIoData->OperType=RECV_POSTED; //提交另一個Recv請求 WSARecv(PerHandleData->client->m_Sock,&(PerIoData->RecvDataBuf),1,&RecvByte,&Flag,&(PerIoData->OverLapped),NULL); } //傳送完成,置空緩衝區,釋放緩衝區 if(PerIoData->OperType==SEND_POSTED) { memset(PerIoData,0,sizeof(PER_IO_OPERATION_DATA)); GlobalFree(PerIoData); ByteTransferred=0; } } return 0L; } TClients::TClients(LPCTSTR strLogPath) { if (NULL==strLogPath||strlen(strLogPath)>=MAX_PATH) { strcpy(m_LogPath,"TClientsLog.log"); }else { strcpy(m_LogPath,strLogPath); } InitNetWork(); } TClients::~TClients() { UnInit(); } void TClients::UnInit() { //1、關閉所有連線 DisconnetAll(); //2、退出工作執行緒 SYSTEM_INFO sys_Info; GetSystemInfo(&sys_Info); for(int i=0;i<sys_Info.dwNumberOfProcessors*2+2;i++) { //寄出退出訊息 PostQueuedCompletionStatus(CompletionPort,-1,-1,NULL); } //3、刪除所有的物件 for (TSortClients::iterator it=m_clients.begin();it!=m_clients.end();it++) { delete it->second; } DeleteCriticalSection(&cClientSection); } void TClients::InitNetWork() { WSADATA wsaData; //1、Net Start Up WSAStartup(MAKEWORD(0x02,0x02),&wsaData); if(WSAStartup(MAKEWORD(0x02,0x02),&wsaData)!=0)WriteLogString("WSAStartUp Faild With Error: %d",WSAGetLastError()); //2、建立完成埠 CompletionPort=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0); if(CompletionPort==INVALID_HANDLE_VALUE) { WriteLogString("CreateIoCompletionPort faild with Error: %d",GetLastError()); return; } //3、建立工作執行緒池 SYSTEM_INFO sys_Info; GetSystemInfo(&sys_Info); for(int i=0;i<sys_Info.dwNumberOfProcessors*2+2;i++) { HANDLE ThreadHandle; DWORD ThreadID; ThreadHandle=CreateThread(NULL,0,ServerWorkerProc,this,0,&ThreadID); if(ThreadHandle==NULL) { WriteLogString("Create Server Work Thread faild with Error: %d",WSAGetLastError()); return ; } CloseHandle(ThreadHandle); } InitializeCriticalSection(&cClientSection); } bool TClients::GetRemoteAddress(SOCKET sClient,char * IP,ULONG &port) { TClient *client=NULL; EnterCriticalSection(&cClientSection); TSortClients::iterator it=m_clients.find(sClient); if (it!=m_clients.end()) { client=it->second; } LeaveCriticalSection(&cClientSection); if (!client)return false; strcpy(IP,client->m_RemoteIP); port=client->m_RemotePort; return true; } bool TClients::GetLocalIP(char *&IP) { char Name[100]; hostent *pHostEntry; in_addr rAddr; if( 0 == gethostname ( Name, 100 ) ) { pHostEntry = gethostbyname( Name ); if( pHostEntry != NULL ) { memcpy( &rAddr, pHostEntry->h_addr_list[0], sizeof(struct in_addr) ); sprintf(IP,"%s",inet_ntoa( rAddr )); return true; } else { WriteLogString("GetHostIp faild with Error: %d",WSAGetLastError()); } } return false; } SOCKET TClients::CreateClient(const char *pSerIp,ULONG iSvrPort, ProcessRecvData* OnProcessRecvData,ProcessPeerClose* OnProcessPeerClose) { int Error=0; //Socket Create SOCKET sock; if((sock=WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED))==INVALID_SOCKET) { Error = WSAGetLastError(); WriteLogString("WSASocket Faild With Error: %d",Error); return INVALID_SOCKET; } struct sockaddr_in inAddr; inAddr.sin_family=AF_INET; inAddr.sin_port=htons(iSvrPort); inAddr.sin_addr.S_un.S_addr=inet_addr(pSerIp); if (connect(sock,(PSOCKADDR)&inAddr,sizeof(inAddr))==SOCKET_ERROR ) { Error=WSAGetLastError(); WriteLogString("connect Server Socket Faild :%d",Error); return INVALID_SOCKET; } TClient *client=new TClient; client->m_ClientManager=this; client->m_pfPeerClose=OnProcessPeerClose; client->m_pfRecvData=OnProcessRecvData; strcpy(client->m_RemoteIP,pSerIp); client->m_RemotePort=iSvrPort; client->m_Sock=sock; //申請新的控制代碼操作資料 LPPER_HANDLE_DATA PerHandleData=(LPPER_HANDLE_DATA) GlobalAlloc(GPTR,sizeof(PER_HANDLE_DATA)); //控制代碼資料 PerHandleData->client=client; //儲存客戶資訊 ::EnterCriticalSection(&cClientSection); m_clients[sock]=client; ::LeaveCriticalSection(&cClientSection); //轉儲資訊 WriteLogString("Ip: %s,port:%d,Socket : %d Conneted",client->m_RemoteIP,client->m_RemotePort,client->m_Sock); //關聯客戶埠到完成埠,控制代碼資料在此時被繫結到完成埠 CreateIoCompletionPort((HANDLE)sock,CompletionPort,(DWORD)PerHandleData,0); //Io操作資料標誌 PPER_IO_OPERATION_DATA PerIoData=(PPER_IO_OPERATION_DATA) GlobalAlloc(GPTR,sizeof(PER_IO_OPERATION_DATA)); unsigned long Flag=0; DWORD RecvByte; ZeroMemory(&(PerIoData->OverLapped),sizeof(OVERLAPPED)); PerIoData->RecvDataBuf.buf=PerIoData->RecvBuf; PerIoData->RecvDataBuf.len=BUFFER_SIZE; PerIoData->OperType=RECV_POSTED; //提交首個接收資料請求 //這時 //如果客戶端斷開連線 //則也可以以接收資料時得到通知 WSARecv(sock,&(PerIoData->RecvDataBuf),1,&RecvByte,&Flag,&(PerIoData->OverLapped),NULL); return sock; } void TClients::WriteLogString(const char *format,...) { char buf[1025]={0}; try { SYSTEMTIME sysTm; ::GetLocalTime(&sysTm); sprintf(buf,"%d-%d-%d %d:%d:%d:%3d\t%s\n",sysTm.wYear,sysTm.wMonth,sysTm.wDay,sysTm.wHour, sysTm.wMinute,sysTm.wSecond,sysTm.wMilliseconds,buf); int len=strlen(buf); va_list arg; va_start(arg,format); _vsntprintf(buf+len,1024-len, format,arg); va_end(arg); FILE *fp=fopen(m_LogPath,"a"); if (!fp)return; fwrite(buf,strlen(buf),1,fp); fclose(fp); } catch (...) { } } bool TClients::Disconnet(SOCKET sClient) { TClient *client=NULL; ::EnterCriticalSection(&cClientSection); TSortClients::iterator it=m_clients.find(sClient); if (it!=m_clients.end()) { client=it->second; m_clients.erase(sClient); } ::LeaveCriticalSection(&cClientSection); if (client) { delete client; shutdown(sClient,1); return closesocket(sClient)==SOCKET_ERROR?false:true; } return false; } bool TClients::DisconnetAll() { ::EnterCriticalSection(&cClientSection); for (TSortClients::iterator it=m_clients.begin();it!=m_clients.end();it++) { shutdown(it->first,1); closesocket(it->first); delete it->second; } m_clients.clear(); ::LeaveCriticalSection(&cClientSection); return true; } bool TClients::SendMsg(SOCKET sClient,char * pData,ULONG Length) { if (sClient==INVALID_SOCKET||pData==NULL||Length==0)return false; int head=0; ULONG packlen=BUFFER_SIZE-MinDataLen; if (Length>packlen)//需要分包 { head=1; } //申請操作鍵 PPER_IO_OPERATION_DATA PerIoData; ULONG left=Length; TData dat; while (left>0) { PerIoData=(PPER_IO_OPERATION_DATA)GlobalAlloc(GPTR,sizeof(PER_IO_OPERATION_DATA)); //準備緩衝 unsigned long Flag=0; DWORD SendByte; ZeroMemory(&(PerIoData->OverLapped),sizeof(OVERLAPPED)); dat.data=PerIoData->SendBuf; if (0==head) { dat.AddType(0); dat.AddInt(Length); dat.AddData(pData,Length); left=0; }else if(1==head) { dat.AddType(1); dat.AddInt(Length); if (0==++Channel)Channel=1; dat.AddInt(Channel); dat.AddData(pData,BUFFER_SIZE-MinFileHeadLen); pData+=BUFFER_SIZE-MinFileHeadLen; head=2; left=Length-packlen; }else { dat.AddType(3); dat.AddInt(Channel); if (left>packlen) { dat.AddData(pData,packlen); left-=packlen; }else { dat.AddData(pData,left); left=0; } } PerIoData->SendDataBuf.buf=dat.data; PerIoData->SendDataBuf.len=dat.totalLen; PerIoData->OperType=SEND_POSTED; int bRet=WSASend(sClient,&(PerIoData->SendDataBuf),1,&SendByte,Flag,&(PerIoData->OverLapped),NULL); if(bRet==SOCKET_ERROR && GetLastError()!=WSA_IO_PENDING) { WriteLogString("WSASend With Error : %d",GetLastError()); return false; } } return true; } /************************************************************************************************************/ /************************************************************************************************************/ TClient::TClient() { m_tempDta.data=new char[2*BUFFER_SIZE]; m_tempDta.CurPack=m_tempDta.data; m_tempDta.totalLen=0; } TClient::~TClient() { clear(); delete[] m_tempDta.data; } void TClient::clear() { for (TPacks::iterator it=m_Receivepacks.begin();it!=m_Receivepacks.end();it++) { delete[] it->second->data; delete it->second; } } void TClient::OnReceive(char *data,ULONG len)//data是一個全域性指標 { //1、已經斷開連線 if (0==len&&m_pfPeerClose) { m_pfPeerClose(); clear(); return; } //2、先處理以前遺留的資料,有資料就合起來 lb1: if(m_tempDta.totalLen==0)//上面沒有遺留資料,不用拷貝直接使用data指標 { if (len<=MinDataLen)//兩個資料包相加都不滿足最小需求,繼續等待 { m_tempDta.AddData(data,len); return; } TData dat; dat.data=data; dat.totalLen=len; char type=dat.GetType(); ULONG ilen=dat.GetInt(); switch(type) { case 1: { ULONG Channel=dat.GetInt(); if (0==Channel||len!=BUFFER_SIZE)//不足一幀,交由m_tempDta去等待 { m_tempDta.AddData(data,len); return; } TPack *pack=new TPack; pack->data=new char[ilen]; pack->CurPack=pack->data; pack->totalLen=ilen; pack->AddData(data+MinDataHeadLen,len-MinDataHeadLen); m_Receivepacks[Channel]=pack; break; } case 2: { break; } case 3: { TPack *pack=m_Receivepacks[ilen]; ULONG curlen=pack->CurPack-pack->data; if (pack->totalLen==len-MinDataLen+curlen)//已經完成了資料包的接收 { pack->AddData(data+MinDataHeadLen,len-MinDataHeadLen); m_pfRecvData(pack->data,pack->totalLen); delete[] pack->data; delete pack; m_Receivepacks.erase(ilen); }else if (pack->totalLen>len-MinDataLen+curlen) { if (len!=BUFFER_SIZE)//不滿一幀,交由m_tempDta繼續等待 { m_tempDta.AddData(data,len); }else //滿一幀,直接加入快取 { pack->AddData(data+MinDataHeadLen,len-MinDataHeadLen); } }else //已經滿一幀,並附帶了多餘的其他幀 { pack->AddData(data+MinDataHeadLen,pack->totalLen-curlen); m_pfRecvData(pack->data,pack->totalLen); ULONG tlen=MinDataHeadLen+pack->totalLen-curlen; data+=tlen; len-=tlen; delete[] pack->data; delete pack; m_Receivepacks.erase(ilen); goto lb1;//重新計算 } break; } case 4: { break; } default: { ULONG tlen=MinDataLen+ilen; if (tlen==len) { m_pfRecvData(dat.Cur,ilen); }else if (tlen>len) { m_tempDta.AddData(data,len); }else { m_pfRecvData(dat.Cur,ilen); data+=tlen; len-=tlen; goto lb1;//重新計算 } break; } } }else //上面有遺留資料, { m_tempDta.AddData(data,len); lb2: if (m_tempDta.totalLen<=MinDataLen)return;//兩個資料包相加都不滿足最小需求,繼續等待 TData dat; dat.data=m_tempDta.data; dat.totalLen=m_tempDta.totalLen; char type=dat.GetType(); ULONG ilen=dat.GetInt(); switch(type) { case 1: { ULONG Channel=dat.GetInt(); if (0==Channel||m_tempDta.totalLen<BUFFER_SIZE)//一幀都不夠,繼續等待 { return; } TPack *pack=new TPack; pack->data=new char[ilen]; pack->CurPack=pack->data; pack->totalLen=ilen; pack->AddData(m_tempDta.data+MinDataHeadLen,BUFFER_SIZE-MinDataHeadLen); m_Receivepacks[Channel]=pack; if (m_tempDta.totalLen==BUFFER_SIZE) { m_tempDta.clear(); }else { memcpy(m_tempDta.data,m_tempDta.data+BUFFER_SIZE,m_tempDta.totalLen-BUFFER_SIZE); m_tempDta.totalLen-=BUFFER_SIZE; goto lb2;//重新計算 } break; } case 2: { break; } case 3: { TPack *pack=m_Receivepacks[ilen]; ULONG curlen=pack->CurPack-pack->data; if (pack->totalLen==dat.totalLen-MinDataLen+curlen)//已經完成了資料包的接收 { pack->AddData(dat.data+MinDataHeadLen,dat.totalLen-MinDataHeadLen); m_pfRecvData(pack->data,pack->totalLen); delete[] pack->data; delete pack; m_Receivepacks.erase(ilen); }else if (pack->totalLen>len-MinDataLen+curlen) { if (dat.totalLen==BUFFER_SIZE)//不滿一幀,繼續等待 { pack->AddData(dat.data+MinDataHeadLen,len-MinDataHeadLen); m_tempDta.AddData(dat.data,dat.totalLen); }else if(dat.totalLen>BUFFER_SIZE) //滿一幀,直接加入快取 { pack->AddData(dat.data+MinDataHeadLen,BUFFER_SIZE-MinDataHeadLen); memcpy(m_tempDta.data,dat.data+BUFFER_SIZE,dat.totalLen-BUFFER_SIZE); m_tempDta.totalLen-=BUFFER_SIZE; goto lb2; } }else { pack->AddData(dat.data+MinDataHeadLen,pack->totalLen-curlen); m_pfRecvData(pack->data,pack->totalLen); ULONG tlen=MinDataHeadLen+pack->totalLen-curlen; memcpy(m_tempDta.data,dat.data+tlen,dat.totalLen-tlen); data+=tlen; m_tempDta.totalLen-=tlen; delete[] pack->data; delete pack; m_Receivepacks.erase(ilen); goto lb2;//重新計算 } break; } case 4: { break; } default: { ULONG tlen=MinDataLen+ilen; if (tlen==m_tempDta.totalLen) { m_pfRecvData(dat.Cur,ilen); m_tempDta.clear(); }else if (tlen<m_tempDta.totalLen) { m_pfRecvData(dat.Cur,ilen); memcpy(m_tempDta.data,dat.Cur+ilen,m_tempDta.totalLen-tlen); m_tempDta.totalLen-=tlen; goto lb2;//重新計算 } break; } } } } void TData::AddType(char type) { totalLen=cSize; memcpy(data,&type,totalLen); } void TData::AddInt(ULONG len) { memcpy(data+totalLen,&len,ULSize); totalLen+=ULSize; } void TData::AddData(char *buf,ULONG len) { int clen=len+totalLen>BUFFER_SIZE?BUFFER_SIZE-len:len; memcpy(data+totalLen,buf,clen); totalLen+=clen; } char TData::GetType() { char type; memcpy(&type,data,cSize); Cur=data+cSize; return type; } ULONG TData::GetInt() { if (Cur-data+ULSize>totalLen)return 0; ULONG l; memcpy(&l,Cur,ULSize); Cur+=ULSize; return l; } char* TData::GetData(ULONG &retlen) { retlen=totalLen-(ULONG)(Cur-data); return Cur; }