開發環境VS2010
這個小程序用到了UDP通信和多線程的知識。
已知問題:不能顯示中文。發送內容不能過長。滾動條沒有自動滾到最后一行。
基本思路:在初始化對話框的時候就創建一個線程,在這個線程里進行套接字初始化,然后一直阻塞到接受到消息。
總結:接受消息顯示采用的是往CString 變量里面追加東西,然后再分行顯示。這樣做個人感覺肯定不是最優,但是我目前只能想到這個辦法了。至於不能顯示中文的問題,我知道發生在接收消息階段。整個消息發送接收過程是這樣處理的:從控件上獲取到CString的,然后轉換格式成wchar_t,再格式化成char* 通過sendto函數發送過去。接受端接收了char*也是轉成wchar_t再轉成CString顯示的。這里面格式轉換是存在問題的,希望有人能指點。
關鍵代碼存檔
在頭文件里添加成員變量static DWORD WINAPI ThreadProc1(LPVOID lpParameter); 下面和也是。

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函數,就是按程序運行時下回車時調用的函數,這里我們要重新定義一下*/

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 }

void CMFCUDPDlg::OnOK() { /*當編輯好發送內容后,直接按回車,就相當於點擊了發送按鈕*/ OnBnClickedButton1(); }
提示:如果沒有換行,記得將編輯控件的屬性里的Multiline屬性設置為true,然后記得添加滾動條,需要的話設置成readonly。
感想:我承認里面做了很多煩瑣而低效的類型轉換。希望有人能指點。化繁為簡。