最近要做一個聊天室的網絡編程小項目,同事給我一份源碼,也能運行,但是有很多的bug,還有很多不太合理的地方。本來就想着把代碼看懂就行了,
可是有很多的內存泄露問題,於是決定自己動手寫一個。在寫的過程中,發現了一些問題,現在總結如下:
第一個問題:在使用MFC編寫socket編程時,必須包含<afxsock.h>頭文件
服務器端的主要過程:必須首先AfxSocketInit(), 接着creat一個端口,然後打開監聽端口listen, 然後等待接受客戶端的主動連接請求Accept
客戶端的主要過程:首先也是必須AfxSocketInit(),接着creat, 然後就可以請求連接了connect。
在服務器端必須同時定義兩個CSocket變量a、b,一個a用於creat和listen,另一個用於接受連接進行消息的接收和發送 a.Accept(b);
在類CAsyncSocket中,所有以On開頭的成員函數都是由框架自動調用的,不用自己單獨調用。在這個聊天室項目中,全局變量是CMysocket類型的,該類
繼承於CAsyncSocket類,其定義如下:
class CMysocket : public CAsyncSocket
{
public:
CMysocket();
virtual ~CMysocket();
void SetParent(CDialog *pWnd);
void OnClose(int nErrorCode);
void OnAccept(int nErrorCode);
void OnReceive(int nErrorCode);
private:
CDialog * m_pWnd;
};
在該類的成員函數的實現過程中,將接受客戶端的連接的函數指向了對話框的類的OnAccept函數
// CMysocket 成員函數
void CMysocket::OnAccept(int nErrorCode)
{
((CserverDlg *)m_pWnd)->OnAccept();
CAsyncSocket::OnAccept(nErrorCode);
}
在下面的函數的實現過程中,調用了類CAsyncSocket的成員函數Accept。從而完成了框架的自動完成服務器對客戶端的連接請求的接受,而不需要自己再對其接受連接的調用。同理,服務器端的接收字符和關閉也是如此實現的。
//成員函數, 接受連接
void CserverDlg::OnAccept()
{
//m_severSocket.Accept(m_connectSocket);
int i_ret;
i_ret = m_severSocket.Accept(m_connectSocket);
//AfxMessageBox(_T("AfxSocketInit 出錯誤"));
if(i_ret == SOCKET_ERROR)
{
CString m_ErrorMsg;
m_ErrorMsg = GetErrorMsg();
MessageBox(m_ErrorMsg);
return;
}
}
需要注意的問題:
1、在發送消息時,可能會出現只能發送一個字符的情況,根據發送函數send的返回值顯示正確發送字符串,但是客戶端只能接收一個字符
解決方案:在客戶端和服務器端中同時做此操作。在項目中右鍵點擊“屬性”,在配置屬性->常規->字符 中選擇“使用多字節字符集”
2、在聊天室內容框控件Listbox Control中,會出現聊天信息的先後順序不和發送的順序一致
解決方案:在該控件的屬性中,sort選擇 false
3、傳輸文件的時候,因爲文件大小的原因,所以通常的做法是文件分塊傳輸,同理接收文件的時候,也是分塊接收
在傳輸文件的時候,需要建立一個結構體,用來保存文件的相關信息,如文件的屬性、標題、大小和創建時間等。
typedef struct _SOCKET_STREAM_FILE_INFO {
TCHAR szFileTitle[128]; //文件的標題名
DWORD dwFileAttributes; //文件的屬性
FILETIME ftCreationTime; //文件的創建時間
FILETIME ftLastAccessTime; //文件的最後訪問時間
FILETIME ftLastWriteTime; //文件的最後修改時間
DWORD nFileSizeHigh; //文件大小的高位雙字
DWORD nFileSizeLow; //文件大小的低位雙字
DWORD dwReserved0; //保留,爲0
DWORD dwReserved1; //保留,爲0
} SOCKET_STREAM_FILE_INFO, * PSOCKET_STREAM_FILE_INFO;
分塊發送文件:
recvSocket.Send(&StreamFileInfo,sizeof(SOCKET_STREAM_FILE_INFO));
UINT dwRead=0;
//分段發送文件內容
while(dwRead < StreamFileInfo.nFileSizeLow)
{
byte *data = new byte[1024];
UINT dw = myFile.Read(data, 1024);
recvSocket.Send(data, dw);
dwRead = dwRead + dw;
delete data;
}
接收文件的原理與此相同,分段接收。
4、內存問題
要注意內存泄露問題。包括接收消息時申請的緩衝區,要記得釋放,new與delete要配對存在
還有發送和接收文件的時候,申請的堆空間指針,要在每次用完的時候記得手動delete,以防內存泄露。