基于UDP的MFC局域网聊天程序

开发环境VS2010

这个小程序用到了UDP通信和多线程的知识。

已知问题:不能显示中文。发送内容不能过长。滚动条没有自动滚到最后一行。

基本思路:在初始化对话框的时候就创建一个线程,在这个线程里进行套接字初始化,然后一直阻塞到接受到消息。

总结:接受消息显示采用的是往CString 变量里面追加东西,然后再分行显示。这样做个人感觉肯定不是最优,但是我目前只能想到这个办法了。至于不能显示中文的问题,我知道发生在接收消息阶段。整个消息发送接收过程是这样处理的:从控件上获取到CString的,然后转换格式成wchar_t,再格式化成char* 通过sendto函数发送过去。接受端接收了char*也是转成wchar_t再转成CString显示的。这里面格式转换是存在问题的,希望有人能指点。

关键代码存档


在头文件里添加成员变量static DWORD WINAPI ThreadProc1(LPVOID lpParameter); 下面和也是。

View Code
1 /*变量声明*/
2 HANDLE hThread1;
3 HANDLE hThread2;
4 CString m_Edit;  //发送消息的编辑控件的成员变量
5 CIPAddressCtrl m_ip;  //ip控件的成员变量
6 CString m_msg;  // 显示消息控件的成员变量,没用到
7 
8 void OnOK();  /*隐藏默认的OnOK函数,就是按程序运行时下回车时调用的函数,这里我们要重新定义一下*/
View Code
  1 /*在OnInitDialog()中添加额外的初始化代码*/
  2 hThread1 = ::CreateThread(NULL,0,ThreadProc1,this,0,NULL);  /*在初始化对话框的时候就创建线程*/
  3 
  4 
  5 /*点击发送按钮时的代码*/
  6 void CMFCUDPDlg::OnBnClickedButton1()
  7 {
  8     
  9      CSocket socket;
 10      if( !socket.Create(0, SOCK_DGRAM, NULL) )
 11      {
 12         MessageBox(_T("创建套接字失败"));
 13      }
 14      //UpdateData(TRUE);    
 15      
 16      wchar_t *sendMsg;
 17      GetDlgItemTextW(IDC_EDIT2,m_Edit);
 18      
 19      sendMsg=m_Edit.GetBuffer(m_Edit.GetLength()); 
 20      m_Edit.ReleaseBuffer();
 21 
 22      /*把char类型的sendMsg转化为wchar_t类型的WStr。
 23     size_t len = strlen(sendMsg) + 1;
 24     size_t converted = 0;
 25     wchar_t *WStr;
 26     WStr=(wchar_t*)malloc(len*sizeof(wchar_t));
 27     mbstowcs_s(&converted, WStr, len, sendMsg, _TRUNCATE);
 28     */
 29 
 30       /*测试类型转换以后是否有乱码 ,我现在觉得下面这种格式化,还是从wchar_t变成CString方便
 31     m_Edit.Format(_T("%s"),sendMsg); 
 32      AfxMessageBox(m_Edit);
 33     */
 34 
 35      //此时的sendMsg已经变成了wchar_t类型的,接下来要把它变成char*型的
 36       size_t len = wcslen(sendMsg) + 1;
 37       size_t converted = 0;
 38       char *message;
 39       message=(char*)malloc(len*sizeof(char));
 40       wcstombs_s(&converted, message, len, sendMsg, _TRUNCATE);
 41 
 42     /*获取ip空间里的ip地址*/
 43     BYTE f0, f1, f2, f3; 
 44     m_ip.GetAddress(f0, f1, f2, f3); 
 45     CString m_addr; 
 46     m_addr.Format(_T("%d%s%d%s%d%s%d"), f0, ".", f1, ".", f2, ".", f3); 
 47 
 48     int length = socket.SendTo(message, strlen(message), 9000, m_addr, 0);
 49     socket.Close();
 50     
 51 
 52 }
 53 
 54 /*因为线程函数是静态的,不能调用非静态成员,在此创建一个类类型的指针*/
 55 CMFCUDPDlg* p;
 56 /*初始化对话框时,创建线程做的事情*/
 57 DWORD WINAPI CMFCUDPDlg::ThreadProc1(LPVOID lpParameter)
 58 {
 59     
 60         CMFCUDPDlg *p=(CMFCUDPDlg*)lpParameter;
 61         CSocket Server;  
 62 
 63         // 创建用于接收数据报的套接字
 64         if (Server.Create(9000, SOCK_DGRAM, NULL)== 0) 
 65         {
 66             AfxMessageBox(_T("创建Socket失败"));
 67             return -1;
 68         }
 69 
 70     while(TRUE)
 71     {
 72             SOCKADDR_IN ClntAddr; 
 73             int clntAddrLen = sizeof(ClntAddr);
 74             char echoBuffer[1024]=""; 
 75             CString buffer;
 76             static CString save;
 77         
 78             //一直阻塞到有客户端发来数据
 79             int recvMsgSize = Server.ReceiveFrom(echoBuffer,sizeof(echoBuffer), (SOCKADDR*)&ClntAddr, &clntAddrLen, 0);
 80             
 81             if (recvMsgSize < 0)
 82             {
 83               AfxMessageBox(_T("接受数据异常"));
 84             }
 85             else
 86             {
 87                 //AfxMessageBox(_T("有消息"));
 88             }
 89 
 90             //把echoBuffer转格式成wchar_t
 91             size_t len = strlen(echoBuffer) + 1;
 92             size_t converted = 0;
 93             wchar_t *WStr;
 94             WStr=(wchar_t*)malloc(len*sizeof(wchar_t));
 95             mbstowcs_s(&converted, WStr, len, echoBuffer, _TRUNCATE);
 96 
 97             //获取对方ip
 98             char *addr = inet_ntoa(ClntAddr.sin_addr);
 99             //把addr转格式成wchar_t
100             len = strlen(addr) + 1;
101             wchar_t *address;
102             address=(wchar_t*)malloc(len*sizeof(wchar_t));
103             mbstowcs_s(&converted, address, len, addr, _TRUNCATE);
104 
105             /*把发送端的ip地址和消息还有换行都存放在buffer里,然后一直往save里追加*/
106             buffer.Format(_T("【%s】说 :%s\r\n"),address,WStr);
107             save = save+buffer;
108             
109             p->SetDlgItemTextW(IDC_EDIT1,save);
110             p->SetDlgItemTextW(IDC_EDIT2,NULL);
111             //p->m_msg = buffer;
112             //p->UpdateData(false);    
113     }
114     Server.Close();
115 
116     return 0;
117 
118 }
View Code
void CMFCUDPDlg::OnOK()
{
    /*当编辑好发送内容后,直接按回车,就相当于点击了发送按钮*/
    OnBnClickedButton1();
}

提示:如果没有换行,记得将编辑控件的属性里的Multiline属性设置为true,然后记得添加滚动条,需要的话设置成readonly。

感想:我承认里面做了很多烦琐而低效的类型转换。希望有人能指点。化繁为简。

 

原文地址:https://www.cnblogs.com/ligongzi/p/2722597.html