网络编程(三)

来源:互联网 发布:搜狗大水牛优化版 编辑:程序博客网 时间:2024/06/05 22:58
 

网络编程(三)---- MFC 仿QQ聊天软件

分类: VC++ 重温笔记 219人阅读 评论(2) 收藏 举报
今天来八一八,MFC的SOCKET 编程,利用CSocket实现一个基于TCP实现一个QQ聊天程序。你会发现,MFC要比WIN32 简单的多。但是如果你不理解具体API socket基础知识,你可能会觉得有一点费解。 所以在开始之前 我还是请大家先看看http://blog.csdn.net/lh844386434/article/details/6664025    


在应用程序开始的时候,我们先应该初始话winSock 库,所以便会用到下面的一个函数。

view plain
  1. BOOL AfxSocketInit( WSADATA* lpwsaData = NULL ); //用来初始化Socket,用WSAStartup();来初始化,在应用程序结束时他会自动调用WSACleanup()  
我们在开始编程之前,应该调用这个函数,对Socket进行初始化。如果初始化成功返回非0 ,否则返回0.

可能人会问,这个函数加载的是那个版本的Socket库呢?通过查看底层代码,我们发现,他加载的是1.1版本的Socket

注意:这个函数只能在你自己应用程序的 CXXWinApp::InitInstance 中初始化.在初始化前还要记得加入头文件Afxsock.h

我服务器端程序 为  NetChatServer  所以我在的CNetChatServerApp::InitInstance()中加入

/////////////////////////////////////////////////////////////////////////////////////////////////////CNetChatServerApp::InitInstance()///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

view plain
  1.   if(!AfxSocketInit())  
  2. {  
  3.     AfxMessageBox(_T("Socket 库初始化出错!"));  
  4.     return false;  
  5. }  

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

view plain
  1.   m_iSocket = new CServerSocket(); // 1.动态创建一个服务器Socket对象。  
  2.     if(!m_iSocket)  
  3.     {  
  4. AfxMessageBox(_T("动态创建服务器套接字出错!"));  
  5. return false;  
  6.     }  

接着创建套接字

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

其中8989 是指定的端口号,但是要注意在保存我们指定的8989端口前,这个端口是空闲的没有被其他进程所占用,那怎么查看端口是否被其他进程占用呢?

首先打开cmd 键入 netstat -aon

 

你会看到所有的TCP/UDP 信息 ,但是由于太多了不好查看,所以。我们再在最下面 tasklist|find “8989”


现在我们看到 我们没有找到任何 和8989端口相关的东西,所以说明8989端口没有被占用。

创建了套接字以后按照win32的步骤我们就应该 对bind端口。

但是MFC 不这样,应为MFC的Create内部已经调用了bind ,如下是MFC的底层代码

view plain
  1. BOOL CAsyncSocket::Create(UINT nSocketPort, int nSocketType,long lEvent, LPCTSTR lpszSocketAddress)  
  2. {  
  3.       if (Socket(nSocketType, lEvent))  
  4.       {  
  5.           if (Bind(nSocketPort,lpszSocketAddress))//调用了bind  
  6.                   return TRUE;  
  7.           int nResult = GetLastError();  
  8.           Close();  
  9.           WSASetLastError(nResult);  
  10.       }  
  11.       return FALSE;  
  12. }  

所以 我们不用在调用bind 了,直接对套接字进行监听

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

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

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

view plain
  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的具体实现

view plain
  1. #pragma once  
  2.   
  3. #include "ClientSocket.h"  
  4.   
  5. class CServerSocket : public CSocket  
  6. {  
  7. public:  
  8.     CServerSocket();  
  9.     virtual ~CServerSocket();  
  10. public :   
  11.     CPtrList m_listSockets;//用来保存服务器与所有客户端连接成功后的ClientSocket  
  12.   
  13.   
  14. public :   
  15.     virtual void OnAccept(int nErrorCode);  
  16. };  

view plain
  1. #include "stdafx.h"  
  2. #include "NetChatServer.h"  
  3. #include "ServerSocket.h"  
  4.   
  5. CServerSocket::CServerSocket()  
  6. {  
  7.   
  8. }  
  9.   
  10. CServerSocket::~CServerSocket()  
  11. {  
  12.   
  13. }  
  14.   
  15. void CServerSocket::OnAccept(int nErrorCode)  
  16. {  
  17.     //接受到一个连接请求  
  18.     CClientSocket* theClientSock(0);  
  19.     theClientSock = new CClientSocket(&m_listSockets);  
  20.     if(!theClientSock)  
  21.     {  
  22.         AfxMessageBox(_T("内存不足,客户连接服务器失败!"));  
  23.         return;  
  24.     }  
  25.     Accept(*theClientSock);  
  26.     //加入list中便于管理  
  27.     m_listSockets.AddTail(theClientSock);  
  28.     CSocket::OnAccept(nErrorCode);  
  29. }  

我们可以看到在CServerSocket中 又出现了一个CClientSocket的类,这个类和CServerSocket一样,也是派生于CSocket类,但是专门用于客户端的Socket。

在这里必须重载OnAccept(int nErrorCode)函数,这样CServerSocket才能接收到客户端的请求,并且必须在OnAccept中调用Accept()函数对连接请求进行响应。

在OnAccept()我们用一个List 将ClientSocket指针保存,以便以后调用访问。

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

接着 我们再来看看CClientSocket类

view plain
  1. #pragma once  
  2.   
  3. #include "stdafx.h"  
  4. /////////////////////////////////////////////////  
  5. ///说明,该类用于和客户端建立通信的Socket  
  6. /////////////////////////////////////////////////  
  7.   
  8. class CClientSocket : public CSocket  
  9. {  
  10. public:  
  11.     CClientSocket(CPtrList* pList);  
  12.     virtual ~CClientSocket();  
  13. public:  
  14.     CPtrList* m_pList;//保存服务器ClientSocket中List的东西,这个是中CServerSocket中传过来的  
  15.     CString m_strName; //连接名称  
  16. public:  
  17.     virtual void OnClose(int nErrorCode);   
  18.     virtual void OnReceive(int nErrorCode);  
  19.     void OnLogoIN(char* buff,int nlen);//处理登录消息  
  20.     void OnMSGTranslate(char* buff,int nlen);//转发消息给其他聊天群  
  21.     CString UpdateServerLog();//服务器端更新、记录日志  
  22.     void UpdateAllUser(CString strUserInfo);//更新服务器端的在线人员列表  
  23. private:  
  24.     BOOL WChar2MByte(LPCWSTR srcBuff, LPSTR destBuff, int nlen);//多字节的转换  
  25. };  
可以看到 我们重载了OnClose()、OnReceive()函数,这样当套接字关闭、有数据到达时,就会自动调用这两个函数,我们便可以在这两个函数中响应、处理事件。

由于本人使用的是VS2010,并且采用的Unicode编码,所以,经常要涉及Unicode转多字节的情况,于是就写了WChar2MByte()进行转换

view plain
  1. #include "stdafx.h"  
  2. #include "NetChatServer.h"  
  3. #include "ClientSocket.h"  
  4. #include "Header.h"  
  5. #include "NetChatServerDlg.h"  
  6.   
  7. CClientSocket::CClientSocket(CPtrList* pList)  
  8.     :m_pList(pList),m_strName(_T(""))  
  9. {  
  10.   
  11. }  
  12.   
  13. CClientSocket::~CClientSocket()  
  14. {  
  15. }  
  16.   
  17. /////////////////////////////////////////////////////////////////////  
  18.  void CClientSocket::OnReceive(int nErrorCode)  
  19.  {  
  20.      //有消息接收  
  21.      //先得到信息头  
  22.      HEADER head;  
  23.      int nlen = sizeof HEADER;  
  24.      char *pHead = NULL;  
  25.      pHead = new char[nlen];  
  26.      if(!pHead)  
  27.      {  
  28.          TRACE0("CClientSocket::OnReceive 内存不足!");  
  29.          return;  
  30.      }  
  31.      memset(pHead,0, sizeof(char)*nlen );  
  32.      Receive(pHead,nlen);  
  33.      head.type = ((LPHEADER)pHead)->type;  
  34.      head.nContentLen = ((LPHEADER)pHead)->nContentLen;  
  35.      delete pHead;  
  36.      pHead = NULL;  
  37.   
  38.      //再次接收,这次是数据类容  
  39.      pHead = new char[head.nContentLen];  
  40.      if(!pHead)  
  41.      {  
  42.          TRACE0("CClientSocket::OnRecive 内存不足!");  
  43.          return;  
  44.      }  
  45.     if( Receive(pHead, head.nContentLen)!=head.nContentLen)  
  46.     {  
  47.         AfxMessageBox(_T("接收数据有误!"));  
  48.         delete pHead;  
  49.         return;  
  50.     }  
  51.      ////////////根据消息类型,处理数据////////////////////  
  52.      switch(head.type)  
  53.      {  
  54.      case MSG_LOGOIN:   
  55.          OnLogoIN(pHead, head.nContentLen);  
  56.          break;  
  57.      case MSG_SEND:   
  58.          OnMSGTranslate(pHead, head.nContentLen);  
  59.          break;  
  60.      default : break;  
  61.      }  
  62.   
  63.      delete pHead;  
  64.      CSocket::OnReceive(nErrorCode);  
  65.  }  
  66.   
  67.  //关闭连接  
  68.  void CClientSocket::OnClose(int nErrorCode)  
  69.  {  
  70.       CTime time;   
  71.      time = CTime::GetCurrentTime();  
  72.      CString strTime = time.Format("%Y-%m-%d  %H:%M:%S  ");  
  73.      strTime = strTime + this->m_strName + _T("  离开...\r\n");  
  74.      ((CNetChatServerDlg*)theApp.GetMainWnd())->DisplayLog(strTime);  
  75.      m_pList->RemoveAt(m_pList->Find(this));  
  76.      //更改服务器在线名单  
  77.      CString str1 = this->UpdateServerLog();  
  78.      //通知客户端刷新在线名单  
  79.      this->UpdateAllUser(str1);  
  80.      this->Close();  
  81.      //销毁该套接字  
  82.      delete this;  
  83.     CSocket::OnClose(nErrorCode);  
  84.  }  
  85.   
  86.  //登录  
  87.  void CClientSocket::OnLogoIN(char* buff, int nlen)  
  88.  {  
  89.      //对得接收到的用户信息进行验证  
  90.      //... (为了简化这步省略)  
  91.      //登录成功  
  92.      CTime time;   
  93.      time = CTime::GetCurrentTime();  
  94.      CString strTime = time.Format("%Y-%m-%d %H:%M:%S  ");  
  95.   
  96.      CString strTemp(buff);  
  97.      strTime = strTime + strTemp + _T("  登录...\r\n");  
  98.     //记录日志  
  99.      ((CNetChatServerDlg*)theApp.GetMainWnd())->DisplayLog(strTime);  
  100.      m_strName = strTemp;  
  101.      //更新服务列表  
  102.      CString str1 = this->UpdateServerLog();  
  103.      //更新在线所有客服端  
  104.      this->UpdateAllUser(str1);  
  105.  }  
  106.   
  107. //转发消息  
  108.  void CClientSocket::OnMSGTranslate(char* buff, int nlen)  
  109.  {  
  110.      HEADER head;  
  111.      head.type = MSG_SEND;  
  112.      head.nContentLen = nlen;  
  113.      POSITION ps = m_pList->GetHeadPosition();  
  114.   
  115.      while(ps!=NULL)  
  116.      {  
  117.         CClientSocket* pTemp = (CClientSocket*)m_pList->GetNext(ps);  
  118.         pTemp->Send(&head,sizeof(HEADER));  
  119.         pTemp->Send(buff, nlen);  
  120.      }  
  121.  }  
  122.   
  123.   
  124.  BOOL CClientSocket::WChar2MByte(LPCWSTR srcBuff, LPSTR destBuff, int nlen)  
  125.  {  
  126.      int n = 0;  
  127.      n = WideCharToMultiByte(CP_OEMCP,0, srcBuff, -1, destBuff,0, 0, FALSE );  
  128.      if(n<nlen)  
  129.         return FALSE;  
  130.   
  131.      WideCharToMultiByte(CP_OEMCP, 0, srcBuff, -1, destBuff, nlen, 0, FALSE);  
  132.   
  133.      return TRUE;  
  134.  }  
  135.   
  136.  //跟新所有在线用户  
  137.  void CClientSocket::UpdateAllUser(CString strUserInfo)  
  138.  {  
  139.       HEADER _head;  
  140.      _head.type = MSG_UPDATE;  
  141.      _head.nContentLen = strUserInfo.GetLength()+1;  
  142.      char *pSend = new char[_head.nContentLen];  
  143.      memset(pSend, 0, _head.nContentLen*sizeof(char));  
  144.     if( !WChar2MByte(strUserInfo.GetBuffer(0), pSend, _head.nContentLen))  
  145.     {  
  146.         AfxMessageBox(_T("字符转换失败"));  
  147.         delete pSend;  
  148.         return;  
  149.     }  
  150.     POSITION ps = m_pList->GetHeadPosition();  
  151.     while(ps!=NULL)  
  152.     {  
  153.          CClientSocket* pTemp = (CClientSocket*)m_pList->GetNext(ps);  
  154.          //发送协议头  
  155.          pTemp->Send((char*)&_head, sizeof(_head));  
  156.          pTemp->Send(pSend,_head.nContentLen );  
  157.     }     
  158.        
  159.     delete pSend;  
  160.    
  161.  }  
  162.   
  163.  //跟新服务器在线名单    
  164.  // 返回在线用户列表的String  
  165. CString CClientSocket::UpdateServerLog()  
  166.  {  
  167.      CString strUserInfo = _T("");  
  168.        
  169.      POSITION ps = m_pList->GetHeadPosition();  
  170.   
  171.      while(ps!=NULL)  
  172.      {  
  173.          CClientSocket* pTemp = (CClientSocket*)m_pList->GetNext(ps);  
  174.          strUserInfo += pTemp->m_strName + _T("#");  
  175.      }  
  176.     ((CNetChatServerDlg*)theApp.GetMainWnd())->UpdateUserInfo(strUserInfo);  
  177.   
  178.     return strUserInfo;  
  179.  }  

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

在上面的代码中 还涉及到一个HEADER struct 这是一个我们自定义的一个头结构,相当于自定义的一个协议,不过这个很简化。在这个协议里我们要指定我们本次要发送数据

的type,既是我们发送的那种消息的数据。还有数据的长度。为了不浪费空间,我们选择2次发送。每次给服务器发数据时都 先发送一个协议头,然后再发送数据本身。

其实也可以既不浪费空间,也只发送一次。但是那就在发送之前,对数据进行序列化。在接收端接收到数据后又反序列化。但是C++中并没有提供相应的方法,所以我们要么自己写,要么用第三方的库类。但是这种方法代价比,我们分两次发送代价高得多,所以为了方便我们就分2次发送。

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////定义协议头 因为直接要传输的类容中有不确定长的的类容
///为了避免浪费空间选择分两部分传输,故定义一个头
////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma once
////////////自定义协议///////////////////


const int MSG_LOGOIN = 0x01; //登录
const int MSG_SEND = 0x11;   //发送消息
const int MSG_CLOSE = 0x02;  //退出
const int MSG_UPDATE = 0x21; //更新信息


#pragma pack(push,1)
typedef struct tagHeader{
int type ;//协议类型
int nContentLen; //将要发送内容的长度
}HEADER ,*LPHEADER;
#pragma pack(pop)

这里面涉及了一个字节对齐的知识,请查看 http://blog.csdn.net/lh844386434/article/details/6680549
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


到这里基本服务器端基本有关发送的框架全部搭建完毕,剩下的就是一些界面编程,比如什么显示之类的工作,在这里我就不贴这些代码,但是呢我会在最后给出整个工程的下载地址,下面我们就简单看看客户端的代码

////////////////////////////////////////////////////////客户端//////////////////////////////////////////////////////////////

客户端相对来说要简单的多,他只涉及一个CClientSocket,但是呢,这个类并不是和服务器端那个一样的,只是名字相同而已。

首先还是要初始化socket库 不多说。位置和添加方法和客户端一样、接着创建客户端的套接字、然后连接服务器。 

view plain
  1. if(!AfxSocketInit())  
  2.     {  
  3.         AfxMessageBox(_T("初始化Socket库失败!"));  
  4.         return false;  
  5.     }  
  6.   
  7.     m_pSocket = new CClientSocket();  
  8.     if(!m_pSocket)  
  9.     {  
  10.         AfxMessageBox(_T("内存不足!"));  
  11.         return false;  
  12.     }  
  13.   
  14.     if(!m_pSocket->Create())  
  15.     {  
  16.         AfxMessageBox(_T("创建套接字失败!"));  
  17.         return false;  
  18.     }  
  19.   
  20.     CLogoInDlg* pLogoinDlg;//登录对话框  
  21.     pLogoinDlg = new CLogoInDlg();  
  22.       
  23.     if(pLogoinDlg->DoModal()==IDOK)//这里其实是点击了推出的按钮,只是ID我用的是IDOK的,没有修改  
  24.     {  
  25.         //不登录  
  26.         delete pLogoinDlg;  
  27.         m_pSocket->Close();  
  28.         return false;  
  29.     }  
  30.     else  
  31.     {  
  32.         delete pLogoinDlg;  
  33.     }  

(上面还有一个CLogoInDlg类,那是一个登录对话框的类,在后面会给出他的部分代码。)

接着和服务器端一样,重载ExitInstance();

view plain
  1. int CNetChatClientApp::ExitInstance()  
  2. {  
  3.     if(m_pSocket)  
  4.     {  
  5.         delete m_pSocket;  
  6.         m_pSocket = NULL;  
  7.     }  
  8.   
  9.     return CWinApp::ExitInstance();  
  10. }  
  11.   
  12. CClientSocket* CNetChatClientApp::GetMainSocket() const  
  13. {  
  14.     return m_pSocket;  
  15. }  

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

然后 看看客户端的CClientSocket的实现

view plain
  1. #pragma once  
  2.   
  3. class CClientSocket : public CSocket  
  4. {  
  5. public:  
  6.     CClientSocket();  
  7.     virtual ~CClientSocket();  
  8. public:  
  9.     virtual void OnReceive(int nErrorCode);//客户端接收消息  
  10.     BOOL SendMSG(LPSTR lpBuff, int nlen);//客户端发送消息  
  11.     BOOL LogoIn(LPSTR lpBuff, int nlen);//客户端登录  
  12.     CString m_strUserName;//用户姓名  
  13. };  

显然我们必须重载OnReceive函数,来处理接收到的数据,其他函数是一些事件处理函数,和说明一样

view plain
  1. #include "stdafx.h"  
  2. #include "NetChatClient.h"  
  3. #include "ClientSocket.h"  
  4. #include "Header.h"  
  5. #include "NetChatClientDlg.h"  
  6. // CClientSocket  
  7.   
  8. CClientSocket::CClientSocket()  
  9.     :m_strUserName(_T(""))  
  10. {  
  11.   
  12. }  
  13.   
  14. CClientSocket::~CClientSocket()  
  15. {  
  16. }  
  17.   
  18.   
  19. void CClientSocket::OnReceive(int nErrorCode)  
  20. {  
  21.     //首先接受head头  
  22.     HEADER head ;  
  23.     char* pHead = NULL;  
  24.     pHead =  new char[sizeof(head)];  
  25.     memset(pHead, 0, sizeof(head));  
  26.     Receive(pHead, sizeof(head));  
  27.   
  28.     head.type =((LPHEADER)pHead)->type;  
  29.     head.nContentLen = ((LPHEADER)pHead)->nContentLen;  
  30.     delete pHead;  
  31.     pHead = NULL;  
  32.   
  33.     char* pBuff = NULL;  
  34.     pBuff = new char[head.nContentLen];  
  35.     if(!pBuff)  
  36.     {  
  37.         AfxMessageBox(_T("内存不足!"));  
  38.         return;  
  39.     }  
  40.     memset(pBuff, 0 , sizeof(char)*head.nContentLen);  
  41.     if(head.nContentLen!=Receive(pBuff, head.nContentLen))  
  42.     {  
  43.         AfxMessageBox(_T("收到数据有误!"));  
  44.         delete pBuff;  
  45.         return;  
  46.     }  
  47.     CString strText(pBuff);  
  48.     switch(head.type)  
  49.     {  
  50.     case MSG_UPDATE:   
  51.         {  
  52.             CString strText(pBuff);  
  53.             ((CNetChatClientDlg*)(AfxGetApp()->GetMainWnd()))->UpdateUserInfo(strText);  
  54.         }  
  55.         break;  
  56.     case MSG_SEND:  
  57.         {   
  58.             //显示接收到的消息  
  59.             CString str(pBuff);  
  60.             ((CNetChatClientDlg*)(AfxGetApp()->GetMainWnd()))->UpdateText(str);  
  61.             break;  
  62.         }  
  63.     defaultbreak;  
  64.     }  
  65.   
  66.     delete pBuff;  
  67.     CSocket::OnReceive(nErrorCode);  
  68. }  
  69.   
  70. BOOL CClientSocket::SendMSG(LPSTR lpBuff, int nlen)  
  71. {  
  72.     //生成协议头  
  73.     HEADER head;  
  74.     head.type = MSG_SEND;  
  75.     head.nContentLen = nlen;  
  76.   
  77.     if(Send(&head, sizeof(HEADER))==SOCKET_ERROR)  
  78.     {  
  79.         AfxMessageBox(_T("发送错误!"));  
  80.         return FALSE;  
  81.     };  
  82.     if(Send(lpBuff, nlen)==SOCKET_ERROR)  
  83.     {  
  84.         AfxMessageBox(_T("发送错误!"));  
  85.         return FALSE;  
  86.     };  
  87.        
  88.     return  TRUE;  
  89. }  
  90.   
  91. BOOL CClientSocket::LogoIn(LPSTR lpBuff, int nlen)  
  92. {  
  93.     HEADER _head;  
  94.     _head.type = MSG_LOGOIN;  
  95.     _head.nContentLen = nlen;  
  96.     int _nSnd= 0;  
  97.     if((_nSnd = Send((char*)&_head, sizeof(_head)))==SOCKET_ERROR)  
  98.         return false;  
  99.     if((_nSnd = Send(lpBuff, nlen))==SOCKET_ERROR)  
  100.         return false;  
  101.   
  102.     return TRUE;  
  103. }  

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

最后来看看 登录、和发送消息的部分代码,也是和服务器端一样,分成两步分发送,先发协议头,再发内容

view plain
  1. void CLogoInDlg::OnBnClickedBtnLogoin()  
  2. {  
  3.     //登录      
  4.     UpdateData();  
  5.     if(m_strUser.IsEmpty())  
  6.     {  
  7.         AfxMessageBox(_T("用户名不能为空!"));  
  8.         return;  
  9.     }  
  10.   
  11.     if(m_dwIP==0)  
  12.     {  
  13.         AfxMessageBox(_T("无效IP地址"));  
  14.         return;  
  15.     }  
  16.   
  17.     CClientSocket* pSock = theApp.GetMainSocket();  
  18.     IN_ADDR addr ;  
  19.     addr.S_un.S_addr = htonl(m_dwIP);  
  20.     CString strIP(inet_ntoa(addr));  
  21.     if(!pSock->Connect(strIP.GetBuffer(0),8989))  
  22.     {  
  23.         AfxMessageBox(_T("连接服务器失败!"));  
  24.         return ;  
  25.     }  
  26.     //发送  
  27.     pSock->m_strUserName = m_strUser;  
  28.     char* pBuff = new char[m_strUser.GetLength()+1];  
  29.     memset(pBuff, 0, m_strUser.GetLength());  
  30.     if(WChar2MByte(m_strUser.GetBuffer(0), pBuff, m_strUser.GetLength()+1))  
  31.         pSock->LogoIn(pBuff, m_strUser.GetLength()+1);  
  32.     delete pBuff;  
  33.     CDialogEx::OnCancel();  
  34. }  
  35.   
  36.   
  37. void CLogoInDlg::OnBnClickedOk()  
  38. {  
  39.     //退出  
  40.     CClientSocket* pSock = theApp.GetMainSocket();  
  41.     pSock->Close();  
  42.     CDialogEx::OnOK();  
  43. }  

///消息发送

view plain
  1. void CNetChatClientDlg::OnBnClickedBtnSend()  
  2. {  
  3.     //发送消息  
  4.     UpdateData();  
  5.     if(m_strSend.IsEmpty())  
  6.     {  
  7.         AfxMessageBox(_T("发送类容不能为空!"));  
  8.         return ;  
  9.     }  
  10.   
  11.     CString temp ;  
  12.     CTime time = CTime::GetCurrentTime();  
  13.     temp = time.Format("%H:%M:%S");  
  14.     //姓名 +_T("\n\t") 时间  
  15.     m_strSend = theApp.GetMainSocket()->m_strUserName+_T("  ") + temp +_T("\r\n   ") + m_strSend +_T("\r\n");  
  16.   
  17.     char* pBuff = new char[m_strSend.GetLength()*2];  
  18.     memset(pBuff, 0, m_strSend.GetLength()*2);  
  19.     //转换为多字节  
  20.     WChar2MByte(m_strSend.GetBuffer(0), pBuff, m_strSend.GetLength()*2);  
  21.     //  
  22.     theApp.GetMainSocket()->SendMSG(pBuff, m_strSend.GetLength()*2);  
  23.   
  24.     delete pBuff;  
  25.   
  26.     m_strSend.Empty();  
  27.     UpdateData(0);  
  28.       
  29. }  

view plain
  1. void CNetChatClientDlg::UpdateUserInfo(CString strInfo)  
  2. {  
  3.     CString strTmp;  
  4.     CListBox* pBox = (CListBox*)GetDlgItem(IDC_LB_ONLINE);  
  5.     pBox->ResetContent();  
  6.     while(!strInfo.IsEmpty())  
  7.     {  
  8.         int n = strInfo.Find('#');  
  9.         if(n==-1)  
  10.             break;  
  11.         strTmp = strInfo.Left(n);  
  12.         pBox->AddString(strTmp);  
  13.         strInfo = strInfo.Right(strInfo.GetLength()-n-1);  
  14.     }  
  15. }  
  16.   
  17. void CNetChatClientDlg::UpdateText(CString &strText)  
  18. {  
  19.     ((CEdit*)GetDlgItem(IDC_ET_TEXT))->ReplaceSel(strText);  
  20. }  

/////////////////////////////////////////以上都是部分代码,我会在后面给出工程下载地址//////////////////////////////////////////

结束语: 我们简单的过了一下windows的网络编程,由于本人水平有限,又是刚开始学着写博客,所以其中错误难免。请大家见谅。其实上面的代码只是实现了,群聊天室。

并没有实现1对1的类似于QQ那种聊天,但是做到这一步,要实现QQ那种聊天那种应该很简单了,加一点代码就可以了。还有 由于最近时间比较紧我没有去写界面。那样的话又会添加更多的代码,但是我会在VC++重温笔记中,重温界面编程时,实现一个QQ2011 里面那种界面效果,在这里就不花时间了


 截图

登录:


服务器记录日志:


两个用户聊天:


说明:本程序在vs2010+win7 X64中通过 

工程下载地址:http://download.csdn.net/source/3513082