最近要做一个聊天室的网络编程小项目,同事给我一份源码,也能运行,但是有很多的bug,还有很多不太合理的地方。本来就想着把代码看懂就行了,
可是有很多的内存泄露问题,于是决定自己动手写一个。在写的过程中,发现了一些问题,现在总结如下:
第一个问题:在使用MFC编写socket编程时,必须包含
服务器端的主要过程:必须首先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,以防内存泄露。