Socket异步编程之基于消息

本例采用Socket实现局域网通信。

开发环境:XP+VS2005+MFC

源码已上传到CSDN资源中:http://download.csdn.net/detail/segen_jaa/4638910,也可私信给我,单独发送。


关键API:WSAAsyncSelect,以网络事件为基础,基于消息实现异步通信。

说明:同一socket上,两次调用WSAAsyncSelect,第二次注册消息会覆盖第一次消息。


1、服务端

效果如下图。



主对话框,响应FD_ACCEPT(接受客户端)|FD_READ(获取客户端信息)|FD_CLOSE(客户端关闭)消息。

  1. #define WM_NETWORK WM_USER+100
  2. LRESULT OnNetwork(WPARAM wParam, LPARAM lParam);
  3. ON_MESSAGE(WM_NETWORK,&CAsyncServerDlg::OnNetwork)
  4. {
  5. ......
  6. //注册感兴趣的网络事件
  7. int nAsyncRet = WSAAsyncSelect(m_sListen, m_hWnd, WM_NETWORK, FD_ACCEPT|FD_READ|FD_CLOSE);
  8. if (SOCKET_ERROR == nAsyncRet)
  9. {
  10. CAsyncFunc::SetAppendText(this, IDC_EDIT_DESC, _T("注册网络事件失败!"));
  11. return;
  12. }
  13. ......
  14. }
  15. LRESULT CAsyncServerDlg::OnNetwork(WPARAM wParam, LPARAM lParam)
  16. {
  17. SOCKET sClient = (SOCKET)wParam;
  18. WORD netEvent = WSAGETSELECTEVENT(lParam);
  19. WORD error = WSAGETSELECTERROR(lParam);
  20. if (error!=0)
  21. {
  22. CString strErrorInfo = _T("");
  23. strErrorInfo.Format(_T("Error code: %d"), error);
  24. CAsyncFunc::SetAppendText(this, IDC_EDIT_DESC, strErrorInfo);
  25. return -1;
  26. }
  27. switch(netEvent)
  28. {
  29. case FD_ACCEPT: OnAccept(); break;
  30. case FD_READ: OnFDRead(sClient); break;
  31. case FD_CLOSE: OnFDClose(sClient); break;
  32. }
  33. return 0;
  34. }
  35. void CAsyncServerDlg::OnFDRead(SOCKET sClient)
  36. {
  37. //该客户端首次收到消息,启动对话框
  38. CAsyncTalkMng::GetInstance()->StartTalkDlg(sClient, this);
  39. }
  40. void CAsyncServerDlg::OnFDClose(SOCKET sClient)
  41. {
  42. //删除列表条目
  43. int nItemCount = m_ClientList.GetCount();
  44. for (int i = 0; i < nItemCount; i++)
  45. {
  46. SOCKET sTemp = (SOCKET)m_ClientList.GetItemData(i);
  47. if (sTemp == sClient)
  48. {
  49. m_ClientList.DeleteString(i);
  50. break;
  51. }
  52. }
  53. //关闭连接
  54. CAsyncTalkMng::GetInstance()->RemoveClient(sClient);
  55. //通知对话框,连接已关闭
  56. CAsyncTalkMng::GetInstance()->StopTalkDlg(sClient);
  57. }
  58. void CAsyncServerDlg::OnAccept()
  59. {
  60. struct sockaddr_in client;
  61. int iAddrSize = sizeof(client);
  62. SOCKET sClient = accept(m_sListen, (struct sockaddr*)&client, &iAddrSize);
  63. if (sClient == INVALID_SOCKET)
  64. {
  65. CString strErrorInfo = _T("");
  66. strErrorInfo.Format(_T("accept() failed:%d"), WSAGetLastError());
  67. CAsyncFunc::SetAppendText(this, IDC_EDIT_DESC, strErrorInfo);
  68. return;
  69. }
  70. char* sClientIP = inet_ntoa(client.sin_addr);
  71. wstring wsClientIP = CAsyncFunc::AnsiToUnicode(string(sClientIP));
  72. CString strClientIP = wsClientIP.c_str();
  73. CString strIPAddr = _T("");
  74. strIPAddr.Format(_T("%s:%d"), strClientIP, ntohs(client.sin_port));
  75. CString strTipInfo = _T("");
  76. strTipInfo += _T("accepted client:");
  77. strTipInfo += strIPAddr;
  78. CAsyncFunc::SetAppendText(this, IDC_EDIT_DESC, strTipInfo);
  79. StClientSock* pClientSock = new StClientSock;
  80. pClientSock->sClient = sClient;
  81. pClientSock->strIPAddr = strIPAddr;
  82. CAsyncTalkMng::GetInstance()->AddClient(pClientSock);
  83. int nItemIndex = m_ClientList.AddString(strIPAddr);
  84. m_ClientList.SetItemData(nItemIndex, sClient);
  85. }
#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(客户端关闭)消息。

  1. #define WM_NETWORK_TALK WM_USER+101
  2. LRESULT OnNetwork(WPARAM wParam, LPARAM lParam);
  3. void OnFDRead();
  4. void OnFDClose(SOCKET sClient);
  5. ON_MESSAGE(WM_NETWORK_TALK,&CAsyncTalkDlg::OnNetwork)
  6. void CAsyncTalkDlg::InitSocketMsg()
  7. {
  8. int nAsyncRet = WSAAsyncSelect(clientSock.sClient, m_hWnd, WM_NETWORK_TALK, FD_READ|FD_CLOSE);
  9. if (SOCKET_ERROR == nAsyncRet)
  10. {
  11. CAsyncFunc::SetAppendText(this, IDC_EDIT_DESC, _T("注册网络事件失败!"));
  12. return;
  13. }
  14. }
  15. LRESULT CAsyncTalkDlg::OnNetwork(WPARAM wParam, LPARAM lParam)
  16. {
  17. WORD netEvent = WSAGETSELECTEVENT(lParam);
  18. WORD error = WSAGETSELECTERROR(lParam);
  19. if (error!=0)
  20. {
  21. CString strErrorInfo = _T("");
  22. strErrorInfo.Format(_T("Error code: %d"), error);
  23. CAsyncFunc::SetAppendText(this, IDC_EDIT_DESC, strErrorInfo);
  24. return -1;
  25. }
  26. switch(netEvent)
  27. {
  28. case FD_READ: OnFDRead(); break;
  29. case FD_CLOSE: OnFDClose((SOCKET)wParam); break;
  30. }
  31. return 0;
  32. }
  33. void CAsyncTalkDlg::OnFDRead()
  34. {
  35. char szBuff[DEFAULT_BUFFER] = {0};
  36. int ret = recv(clientSock.sClient, szBuff, DEFAULT_BUFFER, 0);
  37. if (0 == ret)
  38. {
  39. return;
  40. }
  41. else if (SOCKET_ERROR == ret)
  42. {
  43. CAsyncFunc::SetAppendText(this, IDC_EDIT_RECV, _T("recv() failed"));
  44. return;
  45. }
  46. szBuff[ret] = '\0';
  47. wstring wsRecvInfo = CAsyncFunc::AnsiToUnicode(string(szBuff));
  48. CString strRecvInfo = _T("");
  49. strRecvInfo += clientSock.strIPAddr;
  50. strRecvInfo += _T(":");
  51. strRecvInfo += (CString)(wsRecvInfo.c_str());
  52. CAsyncFunc::SetAppendText(this, IDC_EDIT_RECV, strRecvInfo);
  53. }
  54. void CAsyncTalkDlg::OnFDClose(SOCKET sClient)
  55. {
  56. if (NULL == m_pMsgFlow)
  57. {
  58. return;
  59. }
  60. m_pMsgFlow->OnFDClose(sClient);
  61. }
#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关闭)消息。

  1. #define WM_NETWORK WM_USER+100
  2. LRESULT OnNetwork(WPARAM wParam, LPARAM lParam);
  3. void OnFDRead();
  4. void OnFDClose();
  5. ON_MESSAGE(WM_NETWORK,&CAsyncClientDlg::OnNetwork)
  6. {
  7. ......
  8. //注册感兴趣的网络事件
  9. int nAsyncRet = WSAAsyncSelect(m_sClient, m_hWnd, WM_NETWORK, FD_READ|FD_CLOSE);
  10. if (SOCKET_ERROR == nAsyncRet)
  11. {
  12. CAsyncFunc::SetAppendText(this, IDC_EDIT_RECV, _T("注册网络事件失败!"));
  13. return;
  14. }
  15. ......
  16. }
  17. LRESULT CAsyncClientDlg::OnNetwork(WPARAM wParam, LPARAM lParam)
  18. {
  19. //SOCKET s = (SOCKET)wParam;
  20. WORD netEvent = WSAGETSELECTEVENT(lParam);
  21. WORD error = WSAGETSELECTERROR(lParam);
  22. if (error!=0)
  23. {
  24. CString strErrorInfo = _T("");
  25. strErrorInfo.Format(_T("Error code: %d"), error);
  26. CAsyncFunc::SetAppendText(this, IDC_EDIT_RECV, strErrorInfo);
  27. return -1;
  28. }
  29. switch(netEvent)
  30. {
  31. case FD_READ: OnFDRead(); break;
  32. case FD_CLOSE: OnFDClose(); break;
  33. }
  34. return 0;
  35. }
  36. void CAsyncClientDlg::OnFDRead()
  37. {
  38. char szBuff[DEFAULT_BUFFER] = {0};
  39. int ret = recv(m_sClient, szBuff, DEFAULT_BUFFER, 0);
  40. if (0 == ret)
  41. {
  42. return;
  43. }
  44. else if (SOCKET_ERROR == ret)
  45. {
  46. CAsyncFunc::SetAppendText(this, IDC_EDIT_RECV, _T("recv() failed"));
  47. return;
  48. }
  49. szBuff[ret] = '\0';
  50. wstring wsRecvInfo = CAsyncFunc::AnsiToUnicode(string(szBuff));
  51. CString strRecvInfo = _T("");
  52. strRecvInfo += _T("服务端:");
  53. strRecvInfo += (CString)(wsRecvInfo.c_str());
  54. CAsyncFunc::SetAppendText(this, IDC_EDIT_RECV, strRecvInfo);
  55. }
  56. void CAsyncClientDlg::OnFDClose()
  57. {
  58. closesocket(m_sClient);
  59. m_bSocketClose = true;
  60. CAsyncFunc::SetAppendText(this, IDC_EDIT_RECV, _T("服务端已关闭"));
  61. GetDlgItem(IDC_EDIT_SEND)->EnableWindow(FALSE);
  62. GetDlgItem(IDC_BTN_SEND)->EnableWindow(FALSE);
  63. }
#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);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值