在VS下建一个对话框的MFC程序UDPChat,去掉所有带的控件。加入以下控件:
按钮一个 IDC_BTN_SEND
编程框三个 IDC_EDIT_PORT(端口号),IDC_EDIT_REC(显示接收到的消息),IDC_EDIT_SEND(输入发送内容)
IP控件一个 IDC_IPADDRESS1
UDPChatDlg.h中加入
#define WM_RECDATA WM_USER+1
来定义一个消息号,用来处理接收到消息的事件
然后是以下方法声明:
private:
bool InitSocket(void);
static DWORD WINAPI RecProc(LPVOID lpParam);
afx_msg LRESULT OnRecData(WPARAM wParam,LPARAM lParam);
afx_msg void OnBnClickedBtnSend();
消息映射里加入两条:
ON_MESSAGE(WM_RECDATA, OnRecData)//处理收到消息事件
ON_BN_CLICKED(IDC_BTN_SEND, &CTcpChatDlg::OnBnClickedBtnSend)//处理按钮点击事件
至此,万事具备,只欠东风。
首先窗口初始化函数OnInitDialog里加入
//构造一个新线程用于监听接收
HANDLE hThread =
CreateThread(NULL, 0, RecProc, (LPVOID)m_hWnd, 0, NULL);
CloseHandle(hThread);
((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->SetAddress(127, 0, 0, 1);
SetDlgItemText(IDC_EDIT_PORT, _T("6000"));
后面就是四个相关的成员函数,需要注意的是在线程必须使用静态函数或者全局函数,因为这程序一开始,线程就运行起来了,而成员方法在那个时候可能还没有生成出来。
bool CTcpChatDlg::InitSocket()
{
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD( 2, 2 );
int err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
/* Tell the user that we could not find a usable */
/* WinSock DLL. */
return false;
}
if ( LOBYTE( wsaData.wVersion ) != 2 ||
HIBYTE( wsaData.wVersion ) != 2 ) {
/* Tell the user that we could not find a usable */
/* WinSock DLL. */
WSACleanup( );
return false;
}
return true;
}
DWORD WINAPI CTcpChatDlg::RecProc(LPVOID lpParam)
{
HWND hWnd = (HWND)lpParam;
//-----------------------------------------------
// Create a receiver socket to receive datagrams
SOCKET RecvSocket = socket(AF_INET, SOCK_DGRAM, 0);
if(INVALID_SOCKET == RecvSocket)
{
::AfxMessageBox(_T("socket创建失败"));
return 1;
}
//-----------------------------------------------
// Bind the socket to any address and the specified port.
SOCKADDR_IN RecvAddr;
RecvAddr.sin_family = AF_INET;
RecvAddr.sin_port = htons(6000);
RecvAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
if(SOCKET_ERROR == bind(RecvSocket, (SOCKADDR *) &RecvAddr, sizeof(RecvAddr)))
{
closesocket(RecvSocket);
::AfxMessageBox(_T("bind失败"));
return 1;
}
//-----------------------------------------------
// Call the recvfrom function to receive datagrams
// on the bound socket.
int retval;
char RecvBuf[1024];
char tmpBuf[1024];
sockaddr_in SenderAddr;
int SenderAddrSize = sizeof(SenderAddr);
while(true)
{
retval = recvfrom(RecvSocket,
RecvBuf,
1024,
0,
(SOCKADDR *)&SenderAddr,
&SenderAddrSize);
if(SOCKET_ERROR == retval)
{
CString strError;
strError.Format("error code : %d", WSAGetLastError());
::AfxMessageBox(strError);
break;
}
sprintf_s(tmpBuf, 1024, "收到%s消息: %s", inet_ntoa(SenderAddr.sin_addr), RecvBuf);
//发送消息
::PostMessage(hWnd, WM_RECDATA, 0, (LPARAM)tmpBuf);
}
//清理工作
closesocket(RecvSocket);
WSACleanup();
return 0;//成功
}
LRESULT CTcpChatDlg::OnRecData(WPARAM wParam,LPARAM lParam)
{
CString str((char*)lParam);
CString origin;
GetDlgItemText(IDC_EDIT_REC,origin);
str += "\r\n";
str += origin;
SetDlgItemText(IDC_EDIT_REC,str);
SetDlgItemText(IDC_EDIT_SEND, _T(""));
return 0;
}
void CTcpChatDlg::OnBnClickedBtnSend()
{
DWORD dwIP;
((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->GetAddress(dwIP);
CString strPort;
GetDlgItemText(IDC_EDIT_PORT, strPort);
//创建发送地址信息
SOCKADDR_IN addrTo;
addrTo.sin_family = AF_INET;
addrTo.sin_port = htons(atoi(strPort));
addrTo.sin_addr.S_un.S_addr = htonl(dwIP);
CString strMsg;
GetDlgItemText(IDC_EDIT_SEND, strMsg);
SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);
if(SOCKET_ERROR ==
sendto(
sock, strMsg,
strMsg.GetLength()+1, 0,
(SOCKADDR *)&addrTo, sizeof(addrTo)
))
{
CString strError;
strError.FormatMessage("Send Failed, Error Code: %d", WSAGetLastError());
MessageBox(strError);
}
closesocket(sock);
}