`
lovnet
  • 浏览: 6710902 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论

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(客户端关闭)消息。

#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);
}

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);
}


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics