我做了一个UDP的服务器采用完成端口的方式,我在wsarecv后可以通过GetQueuedCompletionStatus得到一个完成事件,这时我对得到的数据进行处理,但是我想用通过接受的内容将数据wsasend到发送方,就会出现10057错误,主要是由于套接字没有连接并且(当使用一个 sendto 调用发送数据报套接字时)没有提供地址,发送或接收数据的请求没有被接受。我想问的是如何通过完成端口方式可以得到发送的一些信息:比如发送方的在服务器的端口信息。

解决方案 »

  1.   

    使用WSARecvFrom/WSASendTo
    WSARecvFrom当中就有对方的地址和端口,此时WSASendTo就可以使用之。
      

  2.   

    但是我通过wsarecvfrom以后投递接收,然后GetQueuedCompletionStatus给我的数据信息中也没有客户端的端口信息!
      

  3.   

    WSARecvFrom当中的后面两个出参,也需要动态申请内存,不可以使用栈内存。
      

  4.   

    BOOL CCommunication::InitNetWork(BOOL bTcp, BOOL bUdp, int iTcpPort, int iUdpPort)
    {
    ::InitializeCriticalSection(&m_cSection);
    m_bTcpType = bTcp;
    m_bUdpType = bUdp;
    m_iTcpPort = iTcpPort;
    m_iUdpPort = iUdpPort;
    CString strError;
            if ( m_bUdpType )
    {
    m_sUdpSever = CreateSever(UDP);
    if ( m_sUdpSever == INVALID_SOCKET )
    {
    return FALSE;
    } m_hUdpCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
    if ( m_hUdpCompletionPort == NULL )
    {
    m_dwError = GetLastError();
    strError.Format("%d", m_dwError);
    CloseHandle(m_hTcpCompletionPort);
    AfxMessageBox("Create UDP IO Completion port Fail, error:" + strError);
    return FALSE;
    }
    char szName[_MAX_PATH];
    CString str;
    int err = gethostname(szName, _MAX_PATH);
    if ( err == SOCKET_ERROR )
    {
    str.Format("%d", WSAGetLastError());
    }
    HOSTENT *pHostent = gethostbyname(szName);
    if ( pHostent == NULL )
    {
    AfxMessageBox("Communication gethostbyname error");
    return FALSE;
    }
    in_addr addr;
    memcpy(&addr, pHostent->h_addr_list[0], sizeof(in_addr));
    SYSTEM_INFO sysInfo;
    GetSystemInfo(&sysInfo);
    for ( int i = 0; i < 1/*sysInfo.dwNumberOfProcessors*2+2*/; i++)
    {
    DWORD WorkerThreadForUdpId;
    HANDLE hWorkerThreadForUdp = CreateThread(NULL,
     0,
     &WorkerForUDPProc,
     this,
     0,
     &WorkerThreadForUdpId);
    if ( hWorkerThreadForUdp == NULL )
    {
    m_dwError = GetLastError();
    strError.Format("%d", m_dwError);
    CloseHandle(hWorkerThreadForUdp);
    AfxMessageBox("Create WorkerThreadForUdp Thread Fail, error:" + strError);
    return FALSE;
    }
    CloseHandle(hWorkerThreadForUdp);
    } LPPERHANDLEDATA PerHandleDataForUdp = (LPPERHANDLEDATA)GlobalAlloc(GPTR, sizeof(PERHANDLEDATA));
    SOCKADDR_IN addrFrom;
    int iSize = sizeof(addrFrom);
                    CreateIoCompletionPort((HANDLE)m_sUdpSever,
    m_hUdpCompletionPort,
    (DWORD)&addrFrom,
    0);
    DWORD RecvBytes, Flag = 0;
    LPPERIOHANDLEDATA PerIoHandleDataForUdp = (LPPERIOHANDLEDATA)GlobalAlloc(GPTR, sizeof(PERIOHANDLEDATA));
    memset(&(PerIoHandleDataForUdp->Overlap), 0, sizeof(OVERLAPPED));
    PerIoHandleDataForUdp->RecvDataBuf.buf = PerIoHandleDataForUdp->RecvBuf;
    PerIoHandleDataForUdp->RecvDataBuf.len = POCKET_DEFAULT_SIZE;
    PerIoHandleDataForUdp->iType = RECV_DATA;
                    
    err = WSARecvFrom(m_sUdpSever,
      &(PerIoHandleDataForUdp->RecvDataBuf),
      1,
      &RecvBytes,
      &Flag,
      (sockaddr*)&addrFrom,
      &iSize,
      &(PerIoHandleDataForUdp->Overlap),
      NULL);
    if ( err == SOCKET_ERROR )
    {
    str.Format("%d", WSAGetLastError());
    }
    }
    return TRUE;
    }
    DWORD WINAPI WorkerForUDPProc(LPVOID lParam)
    {
    int err;
    CString strErr;
    CCommunication* pCommunication = (CCommunication*)lParam;

    LPPERHANDLEDATA PerHandleData;
    SOCKADDR_IN addr;
    LPPERIOHANDLEDATA PerIoHandleData;
    DWORD dwRecvByte = 0;
    while (TRUE)
    {
    BOOL bSuccess = GetQueuedCompletionStatus(pCommunication->GetUdpCompletionPort(),
      &dwRecvByte,
      (LPDWORD)&addr,
      (LPOVERLAPPED*)&PerIoHandleData,
      INFINITE);
    if ( dwRecvByte == -1 && PerHandleData == NULL )
    {
    return 1L;
    } if ( dwRecvByte != 0 && PerIoHandleData->iType == RECV_DATA )
    {
    pCommunication->DealDataFromUdpComletionPort(PerHandleData, PerIoHandleData, dwRecvByte);
    DWORD dwFlag = 0;
    memset(PerIoHandleData->SendBuf, 0, POCKET_DEFAULT_SIZE);
    memcpy(PerIoHandleData->SendBuf, PerIoHandleData->RecvBuf, POCKET_DEFAULT_SIZE);
    PerIoHandleData->iType = SEND_DATA;
    PerIoHandleData->SendDataBuf.buf = PerIoHandleData->SendBuf;
    PerIoHandleData->SendDataBuf.len = PerIoHandleData->RecvDataBuf.len;
    memset(PerIoHandleData->RecvBuf, 0, POCKET_DEFAULT_SIZE);
    memset(&PerIoHandleData->Overlap, 0, sizeof(OVERLAPPED));
    err = WSASendTo(pCommunication->GetUdpSeverSocket(),
          &(PerIoHandleData->SendDataBuf),
          1,
          &dwRecvByte,
          dwFlag,
                                          (sockaddr*)&addrFrom,
          sizeof(sockaddr),
          &(PerIoHandleData->Overlap),
          NULL);
    if ( err == SOCKET_ERROR )
    {
    strErr.Format("%d", WSAGetLastError());
    }
    }
    else if ( dwRecvByte == 0 && ((PerIoHandleData->iType == RECV_DATA) || (PerIoHandleData->iType == SEND_DATA)) )
    {
    //delete client
    }
    }
    return 1L;
    }
    我是这样写的,不知道哪里错掉了。
      

  5.   

    to unsigned:
    看到你贴出来的代码,我感觉前面我说的全是废话
    为什么是废话???
      

  6.   

    因为 unsigned 很生气,所以后果很严重。哈哈,开个玩笑。unsigned 说得对,要得到客户端的地址,是通过 WSARecvFrom 的参数,也就是 Address 和 AddressLength。你是知道使用了GetQueuedCompletionStatus,可能还没有深刻理解。此函数返回的单句柄数据结构需要重新定义一下,包含客户地址成员,投递 WSARecvFrom 函数时把使用单句柄数据的客户地址成员作为输入参数,在 GetQueuedCompletionStatus 返回的单句柄数据中就自然包含了客户端的地址。供参考。 unsigned 我说得对吗?
      

  7.   

    首先,UDP当中跟TCP不一样,由于后者是确定在某个连接上的,WSARecv的地址也就被确定了,而前者由于是一个未知的地址,如果你需要进行双向通讯,那么就需要得到该地址,但是在WSARecv当中并没有参数可以返回相关的地址,所以需要替换成WSARecvFrom;
    然后,WSARecvFrom当中的地址和地址长度,都是出参,需要交给系统去改写,那么这个地址,就需要动态分配,否则系统会写入到一个野指针当中,后果难以想象。同样的,既然使用的是一个动态分配的内存空间,那么这个指针就需要保存,更何况后面通讯的时候还需要用到,最好的办法是放到Overlapped的扩展结构当中去。