当前位置:文档之家› c++MFC CSocket仿QQ聊天软件,实现1对1聊天,群聊

c++MFC CSocket仿QQ聊天软件,实现1对1聊天,群聊

c++/MFC CSocket仿QQ聊天软件,实现1对1聊天,群聊分类:c++ 2012-11-25 16:45 1582人阅读评论(13) 收藏举报c++C++mfcMFCqqQQsocket聊天学习,c++有2个星期了。

本来,本人是做php出身的。

做php快2年了,最近身边多了很多高手。

让自己对c开始感兴趣了,就开始学习c++了。

首先接触的就是mfc。

前几天,看到了一个博文,是有关,mfc网络编程的。

可对方,的实例只能是多对多,出于兴趣,自己改写了下它的程序,实现了点对点的聊天。

所以,本实例并非纯原创的。

这个还请大家见谅,尤其是作者。

我在他程序基础上,增加了1对1的聊天,同时还保留了群聊。

而且,最关键的是,我增加了很多备注。

很适合新手学习。

本人也是新手,还请各位高手提出宝贵建议。

先谢谢大家了。

如果要转载请注明原地址:/open520yin/article/details/8222279实例下载地址:/detail/open520yin/4808903(为了自己能有点下载积分,客户端和服务端一起打包5个积分不算贵吧。

呵呵。

)大家要是想看懂这个可能还需要先了解一下mfc的socket的一些基本使用规则我也有一篇博文写了c++/MFC 极为简单的socket实例:/open520yin/article/details/8202465MFC的CSocket编程,利用CSocket实现一个基于TCP实现一个QQ聊天程序。

///////////////////////////////////////////////////////////////////////// 服务端start ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////先讲讲服务端,一切先从服务端开始:首先就是要使用AfxSocketInit初始化winsocket,[cpp]view plaincopyprint?1. //初始化winSock库,成功则返回非0否则返回02. WSAData wsData;3. if(!AfxSocketInit(&wsData))4. {5. AfxMessageBox(_T("Socket 库初始化出错!"));6. return false;7. }m_iSocket 是一个CServerSocket*的指针 ,CServerSocket类是一个我们自己的类我会在后面给出相应代码,他继承于CSocket类。

[cpp]view plaincopyprint?1. //创建服务器端Socket、采用TCP2. m_iSocket = new CServerSocket();3. if(!m_iSocket)4. {5. AfxMessageBox(_T("动态创建服务器套接字出错!"));6. return false;7. }实例socket好了,就要创建套接字了。

这里的端口要和客户端连接的端口一致,不然就连接不上。

而且,这个端口,要和服务器的其他软件端口不能冲突,怎么去判断冲突,可以自己谷歌一下,很简单的。

我这里就直接写死了,这个端口一般不会被占用的。

[cpp]view plaincopyprint?1. //端口使用89892. if(!m_iSocket->Create(8989))3. {4. AfxMessageBox(_T("创建套接字错误!"));5. m_iSocket->Close();6. return false;7. }创建好了就要,开始监听这个端口了。

这个是一般的,socket必须建立的几个过程。

[cpp]view plaincopyprint?1. if(!m_iSocket->Listen())2. {3. AfxMessageBox(_T("监听失败!"));4. m_iSocket->Close();5. return false;6. }走完上面的几个步骤,这样,服务端,就能和客户端接受和发送消息了。

大家可能会很奇怪,上面那个怎么没有绑定端口,直接listen了。

这个我那个简单socket里有介绍,因为,Create 方法已经包含了Bind 方法,如果是以Create 方法创建socket的前提下不能再调用Bind ,要不一定出错。

然后重载ExitInstance,退出时对进行清理[cpp]view plaincopyprint?1. int CNetChatServerApp::ExitInstance()2. {3. if(m_iSocket)4. {5. delete m_iSocket;6. m_iSocket = NULL;7. }8. return CWinApp::ExitInstance();9. }我再去看看上面用到的CServerSocket类,这个是用来,服务端接收消息用的。

开启了监听这里的OnAccept()方法就会一直被循环调用。

这个方法其实是重写CSocket类的OnAccept()方法。

只要socket开启了监听,OnAccept就会被循环调用,我那个简单的socket实例,是开启一个while进行死循环来达到这个目的。

大家不要介意,我也是新手。

这里OnAccept方法为什么能一直被循环执行,我到现在也没弄明白,如果有高手知道请告诉我下。

但是我知道,这里就是如果服务器有收到消息就会调用这里,提示ClientSocket接受消息。

[cpp]view plaincopyprint?1. void CServerSocket::OnAccept(int nErrorCode)2. {3. //接受到一个连接请求4. CClientSocket* theClientSock(0);5. //初始化在初始化里把m_listSockets赋值到m_pList里6. theClientSock = new CClientSocket(&m_listSockets);7. if(!theClientSock)8. {9. AfxMessageBox(_T("内存不足,客户连接服务器失败!"));10. return;11. }12. Accept(*theClientSock); //接受13. //加入list中便于管理,这个很关键14. m_listSockets.AddTail(theClientSock);15. CSocket::OnAccept(nErrorCode);16. }OnAccept收到消息,就会实例CClientSocket类,这里其实主要是,服务端发送消息和接受消息的主要部分。

也是服务端,最核心的部分。

OnAccept收到消息后,就会通知CClientSocket来接受消息。

注意了,CserverSocket是接收消息而CClientSocket是接收消息。

接收,接受还是有区别的。

这个关系我们要理解清楚。

这个是我自己的理解,不时候是否有错误。

还请高手赐教。

我学c++最多不过半个月,有些东西,都真是靠自己的理解。

下面的接受消息OnReceive 方法怎么调用的,我也有点模糊,这个方法好像是重写Socket的。

就有数据来,他就会自动调用。

m_listSockets.AddTail(theClientSock);这个很关键,m_listSockets是CPtrList类型,我对这个也还不太了解,经过我一些认识,这个是存放socket连接,成功一个就会加入这个,是一个链表。

用来存放所有连接到服务器的socket连接的,这个后面会经常用到。

下面的HEADER是一个结构体,定义如下[cpp]view plaincopyprint?1. typedef struct tagHeader{2. int type ;//协议类型3. int nContentLen; //将要发送内容的长度4. char to_user[20];5. char from_user[20];6. }HEADER ,*LPHEADER;这个结构体,要和客户端保持一致,不然我担心会有问题。

就算没有问题,我估计转换也麻烦。

尽量保持一直吧,这个也算是一种协议吧。

客户端传输的时候,也传递这样的结构体。

下面的方法,具体就看看备注吧。

我在备注里讲解了。

但是注意的是,我们客户端发送消息,是一次发送2个消息,先发送一个头部消息,这个头部消息是一个结构体,是服务端和客户端一种自定义的协议。

这样的好处是,能节约资源,并且提前知道内容的长度进行申请内存空间。

能先知道对应的消息类型,然后再进行转换和读取。

这个头部接受好了,然后再去接受正式的数据。

这个,可能你去看看。

服务端可能会更容易了解。

[cpp]view plaincopyprint?1. void CClientSocket::OnReceive(int nErrorCode)2. {3. //有消息接收4. //先得到信息头5. HEADER head; //定义客户端发送的过来的一样的结构体6. int nlen = sizeof HEADER; //计算结构体大小7. char *pHead = NULL; //用于接受的结构体8. pHead = new char[nlen]; //申请和结构体一样大小的内存空间9. if(!pHead)10. {11. TRACE0("CClientSocket::OnReceive 内存不足!");12. return;13. }14. memset(pHead,0, sizeof(char)*nlen ); //初始化15. Receive(pHead,nlen); //收到内容,并赋值到pHead中,指定接受的空间大小16. //以下是将接收大结构体进行强制转换成我们的结构体,17. head.type = ((LPHEADER)pHead)->type;18. head.nContentLen = ((LPHEADER)pHead)->nContentLen;19. //head.to_user 是char[]类型,如果不进行初始化,可能会有乱码出现20. memset(head.to_user,0,sizeof(head.to_user));21. //讲接受的数据转换过后并赋值到head.to_user,以下同22. strcpy(head.to_user,((LPHEADER)pHead)->to_user);23. memset(head.from_user,0,sizeof(head.from_user));24. strcpy(head.from_user,((LPHEADER)pHead)->from_user);25.26.27. delete pHead; //使用完毕,指针变量的清除28. pHead = NULL;29.30. //再次接收,这次是接受正式数据内容31. //这个就是,头部接受到的内容长度,这样能对应的申请内容空间32. pHead = new char[head.nContentLen];33. if(!pHead)34. {35. TRACE0("CClientSocket::OnRecive 内存不足!");36. return;37. }38. //这里是一个验证,防止内存错误。

相关主题