下面是数据传输的重点-CDataSocket类,函数不多,都比较重要。
1、OnAccept 数据tcp服务器被连接的虚函数,由框架调用。
1 void CDataSocket::OnAccept(int nErrorCode) 2 { 3 // Accept the connection using a temp CSocket object. 4 CAsyncSocket tmpSocket; 5 Accept(tmpSocket); 6 7 SOCKET socket = tmpSocket.Detach(); 8 Close(); 9 10 Attach(socket); 11 12 m_bConnected = TRUE; 13 14 CAsyncSocket::OnAccept(nErrorCode); 15 }
第7行 得到套接字描述符socket。第8行关闭监听的这个套接字,第11行关联socket与本对象,第12行,标记连接标志。这种处理方法少写一个类。因为tcp监听得一个socket,而连接上来的客户端也需要一个socket来与之通信。这样做适合只有一个客户端的情况。
2、OnReceive 数据接收处理函数
1 int CDataSocket::Receive() 2 { 3 TRACE("OnReceive "); 4 int nRead = 0; 5 6 if (m_pControlSocket->m_nStatus == STATUS_UPLOAD) 7 { 8 if (m_File.m_hFile == NULL) 9 return 0; 10 11 byte data[PACKET_SIZE]; 12 nRead = CAsyncSocket::Receive(data, PACKET_SIZE); 13 14 switch(nRead) 15 { 16 case 0: 17 { 18 m_File.Close(); 19 m_File.m_hFile = NULL; 20 Close(); 21 // tell the client the transfer is complete. 22 m_pControlSocket->SendResponse("226 Transfer complete"); 23 // destroy this socket 24 AfxGetThread()->PostThreadMessage(WM_DESTROYDATACONNECTION, 0, 0); 25 break; 26 } 27 case SOCKET_ERROR: 28 { 29 if (GetLastError() != WSAEWOULDBLOCK) 30 { 31 m_File.Close(); 32 m_File.m_hFile = NULL; 33 Close(); 34 m_pControlSocket->SendResponse("426 Connection closed; transfer aborted."); 35 // destroy this socket 36 AfxGetThread()->PostThreadMessage(WM_DESTROYDATACONNECTION, 0, 0); 37 } 38 break; 39 } 40 default: 41 { 42 TRY 43 { 44 m_File.Write(data, nRead); 45 } 46 CATCH_ALL(e) 47 { 48 m_File.Close(); 49 m_File.m_hFile = NULL; 50 Close(); 51 m_pControlSocket->SendResponse("450 Can't access file."); 52 // destroy this socket 53 AfxGetThread()->PostThreadMessage(WM_DESTROYDATACONNECTION, 0, 0); 54 return 0; 55 } 56 END_CATCH_ALL; 57 break; 58 } 59 } 60 } 61 return nRead; 62 }
如果处于上传状态,那么将接收到的数据写入文件。
3、PreSendFile 与发送文件有关的函数
1 BOOL CDataSocket::PrepareSendFile(LPCTSTR lpszFilename) 2 { 3 // close file if it's already open 4 if (m_File.m_hFile != NULL) 5 { 6 m_File.Close(); 7 } 8 9 // open source file 10 if (!m_File.Open(lpszFilename, CFile::modeRead | CFile::typeBinary)) 11 { 12 return FALSE; 13 } 14 m_nTotalBytesSend = m_File.GetLength(); 15 16 if (m_pControlSocket->m_dwRestartOffset < m_nTotalBytesSend) 17 { 18 m_nTotalBytesTransfered = m_pControlSocket->m_dwRestartOffset; 19 } 20 else 21 { 22 m_nTotalBytesTransfered = 0; 23 } 24 return TRUE; 25 }
初始化文件描述符m_File,m_nTotalBytesSend,m_nTotalBytesTransfered。
4、 SendFile函数 调用PrepareSendFile,调用OnSend发送文件。
1 void CDataSocket::SendFile(LPCTSTR lpszFilename) 2 { 3 if (!PrepareSendFile(lpszFilename)) 4 { 5 // change status 6 m_pControlSocket->m_nStatus = STATUS_IDLE; 7 8 m_pControlSocket->SendResponse("426 Connection closed; transfer aborted."); 9 10 // destroy this socket 11 AfxGetThread()->PostThreadMessage(WM_DESTROYDATACONNECTION, 0, 0); 12 return; 13 } 14 OnSend(0); 15 }
5、SendData 发送数据 是为了LIST命令而准备,发送文件目录列表。
6、OnSend 主要区分了list命令的情况和download的情况。
1 void CDataSocket::OnSend(int nErrorCode) 2 { 3 CAsyncSocket::OnSend(nErrorCode); 4 switch(m_pControlSocket->m_nStatus) 5 { 6 case STATUS_LIST: 7 { 8 while (m_nTotalBytesTransfered < m_nTotalBytesSend) 9 { 10 DWORD dwRead; 11 int dwBytes; 12 13 CString strDataBlock; 14 15 dwRead = m_strData.GetLength(); 16 17 if (dwRead <= PACKET_SIZE) 18 { 19 strDataBlock = m_strData; 20 } 21 else 22 { 23 strDataBlock = m_strData.Left(PACKET_SIZE); 24 dwRead = strDataBlock.GetLength(); 25 } 26 27 if ((dwBytes = Send(strDataBlock, dwRead)) == SOCKET_ERROR) 28 { 29 if (GetLastError() == WSAEWOULDBLOCK) 30 { 31 Sleep(0); 32 return; 33 } 34 else 35 { 36 TCHAR szError[256]; 37 wsprintf(szError, "Server Socket failed to send: %d", GetLastError()); 38 39 // close the data connection. 40 Close(); 41 42 m_nTotalBytesSend = 0; 43 m_nTotalBytesTransfered = 0; 44 45 // change status 46 m_pControlSocket->m_nStatus = STATUS_IDLE; 47 48 m_pControlSocket->SendResponse("426 Connection closed; transfer aborted."); 49 50 // destroy this socket 51 AfxGetThread()->PostThreadMessage(WM_DESTROYDATACONNECTION, 0, 0); 52 } 53 } 54 else 55 { 56 m_nTotalBytesTransfered += dwBytes; 57 m_strData = m_strData.Mid(dwBytes); 58 } 59 } 60 if (m_nTotalBytesTransfered == m_nTotalBytesSend) 61 { 62 // close the data connection. 63 Close(); 64 65 m_nTotalBytesSend = 0; 66 m_nTotalBytesTransfered = 0; 67 68 // change status 69 m_pControlSocket->m_nStatus = STATUS_IDLE; 70 71 // tell the client the transfer is complete. 72 m_pControlSocket->SendResponse("226 Transfer complete"); 73 // destroy this socket 74 AfxGetThread()->PostThreadMessage(WM_DESTROYDATACONNECTION, 0, 0); 75 } 76 break; 77 } 78 case STATUS_DOWNLOAD: 79 { 80 while (m_nTotalBytesTransfered < m_nTotalBytesSend) 81 { 82 // allocate space to store data 83 byte data[PACKET_SIZE]; 84 85 m_File.Seek(m_nTotalBytesTransfered, CFile::begin); 86 87 DWORD dwRead = m_File.Read(data, PACKET_SIZE); 88 89 int dwBytes; 90 91 if ((dwBytes = Send(data, dwRead)) == SOCKET_ERROR) 92 { 93 if (GetLastError() == WSAEWOULDBLOCK) 94 { 95 Sleep(0); 96 break; 97 } 98 else 99 { 100 TCHAR szError[256]; 101 wsprintf(szError, "Server Socket failed to send: %d", GetLastError()); 102 103 // close file. 104 m_File.Close(); 105 m_File.m_hFile = NULL; 106 107 // close the data connection. 108 Close(); 109 110 m_nTotalBytesSend = 0; 111 m_nTotalBytesTransfered = 0; 112 113 // change status 114 m_pControlSocket->m_nStatus = STATUS_IDLE; 115 116 m_pControlSocket->SendResponse("426 Connection closed; transfer aborted."); 117 118 // destroy this socket 119 AfxGetThread()->PostThreadMessage(WM_DESTROYDATACONNECTION, 0, 0); 120 } 121 } 122 else 123 { 124 m_nTotalBytesTransfered += dwBytes; 125 } 126 } 127 if (m_nTotalBytesTransfered == m_nTotalBytesSend) 128 { 129 // close file. 130 m_File.Close(); 131 m_File.m_hFile = NULL; 132 133 // close the data connection. 134 Close(); 135 136 m_nTotalBytesSend = 0; 137 m_nTotalBytesTransfered = 0; 138 139 // change status 140 m_pControlSocket->m_nStatus = STATUS_IDLE; 141 142 // tell the client the transfer is complete. 143 m_pControlSocket->SendResponse("226 Transfer complete"); 144 // destroy this socket 145 AfxGetThread()->PostThreadMessage(WM_DESTROYDATACONNECTION, 0, 0); 146 } 147 break; 148 } 149 } 150 }
7、OnConnect函数 tcp连接成功时被框架调用的虚函数。
8、OnClose函数 当数据发送完成或者接收完成后被框架调用的虚函数。主要做清理工作。
9、