本例采用Socket实现局域网通信。
开发环境:XP+VS2005+MFC
源码已上传到CSDN资源中:http://download.csdn.net/detail/segen_jaa/4638910,也可私信给我,单独发送。
关键API:WSAAsyncSelect,以网络事件为基础,基于消息实现异步通信。
说明:同一socket上,两次调用WSAAsyncSelect,第二次注册消息会覆盖第一次消息。
1、服务端
效果如下图。
主对话框,响应FD_ACCEPT(接受客户端)|FD_READ(获取客户端信息)|FD_CLOSE(客户端关闭)消息。
- #define WM_NETWORK WM_USER+100
- LRESULT OnNetwork(WPARAM wParam, LPARAM lParam);
- ON_MESSAGE(WM_NETWORK,&CAsyncServerDlg::OnNetwork)
- {
- ......
- //注册感兴趣的网络事件
- int nAsyncRet = WSAAsyncSelect(m_sListen, m_hWnd, WM_NETWORK, FD_ACCEPT|FD_READ|FD_CLOSE);
- if (SOCKET_ERROR == nAsyncRet)
- {
- CAsyncFunc::SetAppendText(this, IDC_EDIT_DESC, _T("注册网络事件失败!"));
- return;
- }
- ......
- }
- LRESULT CAsyncServerDlg::OnNetwork(WPARAM wParam, LPARAM lParam)
- {
- SOCKET sClient = (SOCKET)wParam;
- WORD netEvent = WSAGETSELECTEVENT(lParam);
- WORD error = WSAGETSELECTERROR(lParam);
- if (error!=0)
- {
- CString strErrorInfo = _T("");
- strErrorInfo.Format(_T("Error code: %d"), error);
- CAsyncFunc::SetAppendText(this, IDC_EDIT_DESC, strErrorInfo);
- return -1;
- }
- switch(netEvent)
- {
- case FD_ACCEPT: OnAccept(); break;
- case FD_READ: OnFDRead(sClient); break;
- case FD_CLOSE: OnFDClose(sClient); break;
- }
- return 0;
- }
- void CAsyncServerDlg::OnFDRead(SOCKET sClient)
- {
- //该客户端首次收到消息,启动对话框
- CAsyncTalkMng::GetInstance()->StartTalkDlg(sClient, this);
- }
- void CAsyncServerDlg::OnFDClose(SOCKET sClient)
- {
- //删除列表条目
- int nItemCount = m_ClientList.GetCount();
- for (int i = 0; i < nItemCount; i++)
- {
- SOCKET sTemp = (SOCKET)m_ClientList.GetItemData(i);
- if (sTemp == sClient)
- {
- m_ClientList.DeleteString(i);
- break;
- }
- }
- //关闭连接
- CAsyncTalkMng::GetInstance()->RemoveClient(sClient);
- //通知对话框,连接已关闭
- CAsyncTalkMng::GetInstance()->StopTalkDlg(sClient);
- }
- void CAsyncServerDlg::OnAccept()
- {
- struct sockaddr_in client;
- int iAddrSize = sizeof(client);
- SOCKET sClient = accept(m_sListen, (struct sockaddr*)&client, &iAddrSize);
- if (sClient == INVALID_SOCKET)
- {
- CString strErrorInfo = _T("");
- strErrorInfo.Format(_T("accept() failed:%d"), WSAGetLastError());
- CAsyncFunc::SetAppendText(this, IDC_EDIT_DESC, strErrorInfo);
- return;
- }
- char* sClientIP = inet_ntoa(client.sin_addr);
- wstring wsClientIP = CAsyncFunc::AnsiToUnicode(string(sClientIP));
- CString strClientIP = wsClientIP.c_str();
- CString strIPAddr = _T("");
- strIPAddr.Format(_T("%s:%d"), strClientIP, ntohs(client.sin_port));
- CString strTipInfo = _T("");
- strTipInfo += _T("accepted client:");
- strTipInfo += strIPAddr;
- CAsyncFunc::SetAppendText(this, IDC_EDIT_DESC, strTipInfo);
- StClientSock* pClientSock = new StClientSock;
- pClientSock->sClient = sClient;
- pClientSock->strIPAddr = strIPAddr;
- CAsyncTalkMng::GetInstance()->AddClient(pClientSock);
- int nItemIndex = m_ClientList.AddString(strIPAddr);
- m_ClientList.SetItemData(nItemIndex, sClient);
- }
#define WM_NETWORK WM_USER+100
LRESULT OnNetwork(WPARAM wParam, LPARAM lParam);
ON_MESSAGE(WM_NETWORK,&CAsyncServerDlg::OnNetwork)
{
......
//注册感兴趣的网络事件
int nAsyncRet = WSAAsyncSelect(m_sListen, m_hWnd, WM_NETWORK, FD_ACCEPT|FD_READ|FD_CLOSE);
if (SOCKET_ERROR == nAsyncRet)
{
CAsyncFunc::SetAppendText(this, IDC_EDIT_DESC, _T("注册网络事件失败!"));
return;
}
......
}
LRESULT CAsyncServerDlg::OnNetwork(WPARAM wParam, LPARAM lParam)
{
SOCKET sClient = (SOCKET)wParam;
WORD netEvent = WSAGETSELECTEVENT(lParam);
WORD error = WSAGETSELECTERROR(lParam);
if (error!=0)
{
CString strErrorInfo = _T("");
strErrorInfo.Format(_T("Error code: %d"), error);
CAsyncFunc::SetAppendText(this, IDC_EDIT_DESC, strErrorInfo);
return -1;
}
switch(netEvent)
{
case FD_ACCEPT: OnAccept(); break;
case FD_READ: OnFDRead(sClient); break;
case FD_CLOSE: OnFDClose(sClient); break;
}
return 0;
}
void CAsyncServerDlg::OnFDRead(SOCKET sClient)
{
//该客户端首次收到消息,启动对话框
CAsyncTalkMng::GetInstance()->StartTalkDlg(sClient, this);
}
void CAsyncServerDlg::OnFDClose(SOCKET sClient)
{
//删除列表条目
int nItemCount = m_ClientList.GetCount();
for (int i = 0; i < nItemCount; i++)
{
SOCKET sTemp = (SOCKET)m_ClientList.GetItemData(i);
if (sTemp == sClient)
{
m_ClientList.DeleteString(i);
break;
}
}
//关闭连接
CAsyncTalkMng::GetInstance()->RemoveClient(sClient);
//通知对话框,连接已关闭
CAsyncTalkMng::GetInstance()->StopTalkDlg(sClient);
}
void CAsyncServerDlg::OnAccept()
{
struct sockaddr_in client;
int iAddrSize = sizeof(client);
SOCKET sClient = accept(m_sListen, (struct sockaddr*)&client, &iAddrSize);
if (sClient == INVALID_SOCKET)
{
CString strErrorInfo = _T("");
strErrorInfo.Format(_T("accept() failed:%d"), WSAGetLastError());
CAsyncFunc::SetAppendText(this, IDC_EDIT_DESC, strErrorInfo);
return;
}
char* sClientIP = inet_ntoa(client.sin_addr);
wstring wsClientIP = CAsyncFunc::AnsiToUnicode(string(sClientIP));
CString strClientIP = wsClientIP.c_str();
CString strIPAddr = _T("");
strIPAddr.Format(_T("%s:%d"), strClientIP, ntohs(client.sin_port));
CString strTipInfo = _T("");
strTipInfo += _T("accepted client:");
strTipInfo += strIPAddr;
CAsyncFunc::SetAppendText(this, IDC_EDIT_DESC, strTipInfo);
StClientSock* pClientSock = new StClientSock;
pClientSock->sClient = sClient;
pClientSock->strIPAddr = strIPAddr;
CAsyncTalkMng::GetInstance()->AddClient(pClientSock);
int nItemIndex = m_ClientList.AddString(strIPAddr);
m_ClientList.SetItemData(nItemIndex, sClient);
}
聊天框,响应FD_READ(获取客户端信息)|FD_CLOSE(客户端关闭)消息。
- #define WM_NETWORK_TALK WM_USER+101
- LRESULT OnNetwork(WPARAM wParam, LPARAM lParam);
- void OnFDRead();
- void OnFDClose(SOCKET sClient);
- ON_MESSAGE(WM_NETWORK_TALK,&CAsyncTalkDlg::OnNetwork)
- void CAsyncTalkDlg::InitSocketMsg()
- {
- int nAsyncRet = WSAAsyncSelect(clientSock.sClient, m_hWnd, WM_NETWORK_TALK, FD_READ|FD_CLOSE);
- if (SOCKET_ERROR == nAsyncRet)
- {
- CAsyncFunc::SetAppendText(this, IDC_EDIT_DESC, _T("注册网络事件失败!"));
- return;
- }
- }
- LRESULT CAsyncTalkDlg::OnNetwork(WPARAM wParam, LPARAM lParam)
- {
- WORD netEvent = WSAGETSELECTEVENT(lParam);
- WORD error = WSAGETSELECTERROR(lParam);
- if (error!=0)
- {
- CString strErrorInfo = _T("");
- strErrorInfo.Format(_T("Error code: %d"), error);
- CAsyncFunc::SetAppendText(this, IDC_EDIT_DESC, strErrorInfo);
- return -1;
- }
- switch(netEvent)
- {
- case FD_READ: OnFDRead(); break;
- case FD_CLOSE: OnFDClose((SOCKET)wParam); break;
- }
- return 0;
- }
- void CAsyncTalkDlg::OnFDRead()
- {
- char szBuff[DEFAULT_BUFFER] = {0};
- int ret = recv(clientSock.sClient, szBuff, DEFAULT_BUFFER, 0);
- if (0 == ret)
- {
- return;
- }
- else if (SOCKET_ERROR == ret)
- {
- CAsyncFunc::SetAppendText(this, IDC_EDIT_RECV, _T("recv() failed"));
- return;
- }
- szBuff[ret] = '\0';
- wstring wsRecvInfo = CAsyncFunc::AnsiToUnicode(string(szBuff));
- CString strRecvInfo = _T("");
- strRecvInfo += clientSock.strIPAddr;
- strRecvInfo += _T(":");
- strRecvInfo += (CString)(wsRecvInfo.c_str());
- CAsyncFunc::SetAppendText(this, IDC_EDIT_RECV, strRecvInfo);
- }
- void CAsyncTalkDlg::OnFDClose(SOCKET sClient)
- {
- if (NULL == m_pMsgFlow)
- {
- return;
- }
- m_pMsgFlow->OnFDClose(sClient);
- }
#define WM_NETWORK_TALK WM_USER+101
LRESULT OnNetwork(WPARAM wParam, LPARAM lParam);
void OnFDRead();
void OnFDClose(SOCKET sClient);
ON_MESSAGE(WM_NETWORK_TALK,&CAsyncTalkDlg::OnNetwork)
void CAsyncTalkDlg::InitSocketMsg()
{
int nAsyncRet = WSAAsyncSelect(clientSock.sClient, m_hWnd, WM_NETWORK_TALK, FD_READ|FD_CLOSE);
if (SOCKET_ERROR == nAsyncRet)
{
CAsyncFunc::SetAppendText(this, IDC_EDIT_DESC, _T("注册网络事件失败!"));
return;
}
}
LRESULT CAsyncTalkDlg::OnNetwork(WPARAM wParam, LPARAM lParam)
{
WORD netEvent = WSAGETSELECTEVENT(lParam);
WORD error = WSAGETSELECTERROR(lParam);
if (error!=0)
{
CString strErrorInfo = _T("");
strErrorInfo.Format(_T("Error code: %d"), error);
CAsyncFunc::SetAppendText(this, IDC_EDIT_DESC, strErrorInfo);
return -1;
}
switch(netEvent)
{
case FD_READ: OnFDRead(); break;
case FD_CLOSE: OnFDClose((SOCKET)wParam); break;
}
return 0;
}
void CAsyncTalkDlg::OnFDRead()
{
char szBuff[DEFAULT_BUFFER] = {0};
int ret = recv(clientSock.sClient, szBuff, DEFAULT_BUFFER, 0);
if (0 == ret)
{
return;
}
else if (SOCKET_ERROR == ret)
{
CAsyncFunc::SetAppendText(this, IDC_EDIT_RECV, _T("recv() failed"));
return;
}
szBuff[ret] = '\0';
wstring wsRecvInfo = CAsyncFunc::AnsiToUnicode(string(szBuff));
CString strRecvInfo = _T("");
strRecvInfo += clientSock.strIPAddr;
strRecvInfo += _T(":");
strRecvInfo += (CString)(wsRecvInfo.c_str());
CAsyncFunc::SetAppendText(this, IDC_EDIT_RECV, strRecvInfo);
}
void CAsyncTalkDlg::OnFDClose(SOCKET sClient)
{
if (NULL == m_pMsgFlow)
{
return;
}
m_pMsgFlow->OnFDClose(sClient);
}
2、客户端
效果图如下。
主要代码,响应FD_READ(获取服务端消息)|FD_CLOSE(服务端socket关闭)消息。
- #define WM_NETWORK WM_USER+100
- LRESULT OnNetwork(WPARAM wParam, LPARAM lParam);
- void OnFDRead();
- void OnFDClose();
- ON_MESSAGE(WM_NETWORK,&CAsyncClientDlg::OnNetwork)
- {
- ......
- //注册感兴趣的网络事件
- int nAsyncRet = WSAAsyncSelect(m_sClient, m_hWnd, WM_NETWORK, FD_READ|FD_CLOSE);
- if (SOCKET_ERROR == nAsyncRet)
- {
- CAsyncFunc::SetAppendText(this, IDC_EDIT_RECV, _T("注册网络事件失败!"));
- return;
- }
- ......
- }
- LRESULT CAsyncClientDlg::OnNetwork(WPARAM wParam, LPARAM lParam)
- {
- //SOCKET s = (SOCKET)wParam;
- WORD netEvent = WSAGETSELECTEVENT(lParam);
- WORD error = WSAGETSELECTERROR(lParam);
- if (error!=0)
- {
- CString strErrorInfo = _T("");
- strErrorInfo.Format(_T("Error code: %d"), error);
- CAsyncFunc::SetAppendText(this, IDC_EDIT_RECV, strErrorInfo);
- return -1;
- }
- switch(netEvent)
- {
- case FD_READ: OnFDRead(); break;
- case FD_CLOSE: OnFDClose(); break;
- }
- return 0;
- }
- void CAsyncClientDlg::OnFDRead()
- {
- char szBuff[DEFAULT_BUFFER] = {0};
- int ret = recv(m_sClient, szBuff, DEFAULT_BUFFER, 0);
- if (0 == ret)
- {
- return;
- }
- else if (SOCKET_ERROR == ret)
- {
- CAsyncFunc::SetAppendText(this, IDC_EDIT_RECV, _T("recv() failed"));
- return;
- }
- szBuff[ret] = '\0';
- wstring wsRecvInfo = CAsyncFunc::AnsiToUnicode(string(szBuff));
- CString strRecvInfo = _T("");
- strRecvInfo += _T("服务端:");
- strRecvInfo += (CString)(wsRecvInfo.c_str());
- CAsyncFunc::SetAppendText(this, IDC_EDIT_RECV, strRecvInfo);
- }
- void CAsyncClientDlg::OnFDClose()
- {
- closesocket(m_sClient);
- m_bSocketClose = true;
- CAsyncFunc::SetAppendText(this, IDC_EDIT_RECV, _T("服务端已关闭"));
- GetDlgItem(IDC_EDIT_SEND)->EnableWindow(FALSE);
- GetDlgItem(IDC_BTN_SEND)->EnableWindow(FALSE);
- }
#define WM_NETWORK WM_USER+100
LRESULT OnNetwork(WPARAM wParam, LPARAM lParam);
void OnFDRead();
void OnFDClose();
ON_MESSAGE(WM_NETWORK,&CAsyncClientDlg::OnNetwork)
{
......
//注册感兴趣的网络事件
int nAsyncRet = WSAAsyncSelect(m_sClient, m_hWnd, WM_NETWORK, FD_READ|FD_CLOSE);
if (SOCKET_ERROR == nAsyncRet)
{
CAsyncFunc::SetAppendText(this, IDC_EDIT_RECV, _T("注册网络事件失败!"));
return;
}
......
}
LRESULT CAsyncClientDlg::OnNetwork(WPARAM wParam, LPARAM lParam)
{
//SOCKET s = (SOCKET)wParam;
WORD netEvent = WSAGETSELECTEVENT(lParam);
WORD error = WSAGETSELECTERROR(lParam);
if (error!=0)
{
CString strErrorInfo = _T("");
strErrorInfo.Format(_T("Error code: %d"), error);
CAsyncFunc::SetAppendText(this, IDC_EDIT_RECV, strErrorInfo);
return -1;
}
switch(netEvent)
{
case FD_READ: OnFDRead(); break;
case FD_CLOSE: OnFDClose(); break;
}
return 0;
}
void CAsyncClientDlg::OnFDRead()
{
char szBuff[DEFAULT_BUFFER] = {0};
int ret = recv(m_sClient, szBuff, DEFAULT_BUFFER, 0);
if (0 == ret)
{
return;
}
else if (SOCKET_ERROR == ret)
{
CAsyncFunc::SetAppendText(this, IDC_EDIT_RECV, _T("recv() failed"));
return;
}
szBuff[ret] = '\0';
wstring wsRecvInfo = CAsyncFunc::AnsiToUnicode(string(szBuff));
CString strRecvInfo = _T("");
strRecvInfo += _T("服务端:");
strRecvInfo += (CString)(wsRecvInfo.c_str());
CAsyncFunc::SetAppendText(this, IDC_EDIT_RECV, strRecvInfo);
}
void CAsyncClientDlg::OnFDClose()
{
closesocket(m_sClient);
m_bSocketClose = true;
CAsyncFunc::SetAppendText(this, IDC_EDIT_RECV, _T("服务端已关闭"));
GetDlgItem(IDC_EDIT_SEND)->EnableWindow(FALSE);
GetDlgItem(IDC_BTN_SEND)->EnableWindow(FALSE);
}