基於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。

感想:我承認里面做了很多煩瑣而低效的類型轉換。希望有人能指點。化繁為簡。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM