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

MFC的CSocket编程,利用CSocket实现一个基于TCP实现一个QQ聊天程序。

/  服务端 start  ///

先讲讲服务端,一切先从服务端开始:
首先就是要使用AfxSocketInit初始化winsocket,

 
  1. //初始化winSock库,成功则返回非0否则返回0   
  2.     WSAData wsData;  
  3.     if(!AfxSocketInit(&wsData))  
  4.     {  
  5.         AfxMessageBox(_T("Socket 库初始化出错!"));  
  6.         return false;  
  7.     }  
//初始化winSock库,成功则返回非0否则返回0
	WSAData wsData;
	if(!AfxSocketInit(&wsData))
	{
		AfxMessageBox(_T("Socket 库初始化出错!"));
		return false;
	}

m_iSocket 是一个 CServerSocket*的 指针  ,CServerSocket类是一个我们自己的类我会在后面给出相应代码,他继承于CSocket类。

 
  1. //创建服务器端Socket、采用TCP   
  2.     m_iSocket = new CServerSocket();  
  3.     if(!m_iSocket)  
  4.     {  
  5.         AfxMessageBox(_T("动态创建服务器套接字出错!"));  
  6.         return false;  
  7.     }  
//创建服务器端Socket、采用TCP
	m_iSocket = new CServerSocket();
	if(!m_iSocket)
	{
		AfxMessageBox(_T("动态创建服务器套接字出错!"));
		return false;
	}

实例socket好了,就要创建套接字了。。这里的端口要和客户端连接的端口一致,不然就连接不上。而且,这个端口,要和服务器的其他软件端口不能冲突,怎么去判断冲突,可以自己谷歌一下,很简单的。我这里就直接写死了,这个端口一般不会被占用的。。

 
  1.        //端口使用8989   
  2. if(!m_iSocket->Create(8989))  
  3. {  
  4.     AfxMessageBox(_T("创建套接字错误!"));  
  5.     m_iSocket->Close();  
  6.     return false;  
  7. }  
        //端口使用8989
	if(!m_iSocket->Create(8989))
	{
		AfxMessageBox(_T("创建套接字错误!"));
		m_iSocket->Close();
		return false;
	}

创建好了就要,开始监听这个端口了。这个是一般的,socket必须建立的几个过程。。

 
  1. if(!m_iSocket->Listen())  
  2. {  
  3.     AfxMessageBox(_T("监听失败!"));  
  4.     m_iSocket->Close();  
  5.     return false;  
  6. }  
    if(!m_iSocket->Listen())
    {
        AfxMessageBox(_T("监听失败!"));
        m_iSocket->Close();
        return false;
    }

走完上面的几个步骤,这样,服务端,就能和客户端接受和发送消息了。大家可能会很奇怪,上面那个怎么没有绑定端口,直接listen了。。。这个我那个简单socket里有介绍,因为,Create 方法已经包含了 Bind 方法,如果是以 Create 方法创建socket的前提下不能再调用 Bind ,要不一定出错。

然后重载ExitInstance,退出时对进行清理

 
  1. int CNetChatServerApp::ExitInstance()    
  2. {    
  3. if(m_iSocket)    
  4. {    
  5. delete m_iSocket;    
  6. m_iSocket = NULL;    
  7. }    
  8. return CWinApp::ExitInstance();    
  9. }    
    int CNetChatServerApp::ExitInstance()  
    {  
    if(m_iSocket)  
    {  
    delete m_iSocket;  
    m_iSocket = NULL;  
    }  
    return CWinApp::ExitInstance();  
    }  


我再去看看上面用到的CServerSocket类,这个是用来,服务端接收消息用的。开启了监听这里的OnAccept()方法就会一直被循环调用。这个方法其实是重写CSocket类的OnAccept()方法。只要socket开启了监听,OnAccept就会被循环调用,我那个简单的socket实例,是开启一个while进行死循环来达到这个目的。大家不要介意,我也是新手。这里OnAccept方法为什么能一直被循环执行,我到现在也没弄明白,如果有高手知道请告诉我下。但是我知道,这里就是如果服务器有收到消息就会调用这里,提示ClientSocket接受消息。

 
  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. }  
void CServerSocket::OnAccept(int nErrorCode)
{
	//接受到一个连接请求
	CClientSocket* theClientSock(0);
        //初始化在初始化里把m_listSockets赋值到m_pList里
        theClientSock = new CClientSocket(&m_listSockets);
	if(!theClientSock)
	{
		AfxMessageBox(_T("内存不足,客户连接服务器失败!"));
		return;
	}
	Accept(*theClientSock); //接受
	//加入list中便于管理,这个很关键
	m_listSockets.AddTail(theClientSock);
	CSocket::OnAccept(nErrorCode);
}
OnAccept收到消息,就会实例CClientSocket类,这里其实主要是,服务端发送消息和接受消息的主要部分。也是服务端,最核心的部分。OnAccept收到消息后,就会通知CClientSocket来接受消息。注意了,CserverSocket是接收消息而CClientSocket是接收消息。接收,接受还是有区别的。这个关系我们要理解清楚。这个是我自己的理解,不时候是否有错误。还请高手赐教。。我学c++最多不过半个月,有些东西,都真是靠自己的理解。下面的接受消息OnReceive方法怎么调用的,我也有点模糊,这个方法好像是重写Socket的。就有数据来,他就会自动调用。m_listSockets.AddTail(theClientSock);这个很关键,m_listSockets是CPtrList类型,我对这个也还不太了解,经过我一些认识,这个是存放socket连接,成功一个就会加入这个,是一个链表。用来存放所有连接到服务器的socket连接的,这个后面会经常用到。

下面的HEADER是一个结构体,定义如下

 
  1. typedef struct tagHeader{  
  2.     int type ;//协议类型   
  3.     int nContentLen; //将要发送内容的长度   
  4.     char to_user[20];  
  5.     char from_user[20];  
  6. }HEADER ,*LPHEADER;  
typedef struct tagHeader{
	int type ;//协议类型
	int nContentLen; //将要发送内容的长度
	char to_user[20];
	char from_user[20];
}HEADER ,*LPHEADER;
这个结构体,要和客户端保持一致,不然我担心会有问题。就算没有问题,我估计转换也麻烦。尽量保持一直吧,这个也算是一种协议吧。客户端传输的时候,也传递这样的结构体。下面的方法,具体就看看备注吧。我在备注里讲解了。但是注意的是,我们客户端发送消息,是一次发送2个消息,先发送一个头部消息,这个头部消息是一个结构体,是服务端和客户端一种自定义的协议。这样的好处是,能节约资源,并且提前知道内容的长度进行申请内存空间。能先知道对应的消息类型,然后再进行转换和读取。这个头部接受好了,然后再去接受正式的数据。这个,可能你去看看。服务端可能会更容易了解。

 
  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.      
  • 6
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值