【问题标题】:recv (winsock) function hangs, though data is availablerecv (winsock) 函数挂起,但数据可用
【发布时间】:2014-03-11 22:07:08
【问题描述】:

我正在使用 winsock 套接字 api 发送具有非常高端口号的 udp 数据包,我希望在目的地不会使用该数据包。我的意图是接收一个带有目的地不可达/端口不可达消息的 icmp 数据包。我创建了两个套接字,一个是我发送 UDP 数据包的套接字,另一个是我期待 icmp 数据包的套接字。发送成功。目的地也返回 ICMP 回复,我可以在 wireshark 上验证。现在当我做一个recv来接收数据时,recv函数挂起。我的目标是从 recv 函数中读取数据,这不会发生。

对于理解/修复此行为的任何帮助将不胜感激。提前致谢。

我在这里附上代码sn-ps...

    void sendPacket(unsigned int socketFd, char *packet, char* remoteIP, char* pingType)
{
    int nsent = -1;
    int rc = -1;

    struct addrinfo hints, *res;

    memset(&hints, 0, sizeof(struct addrinfo));

    if (strcasecmp(pingType, "UDP")==0)
    {
        hints.ai_flags      = AI_CANONNAME;         /* always return canonical name */
        hints.ai_family     = AF_INET;              /* 0, AF_INET, AF_INET6, etc. */
        hints.ai_socktype   = SOCK_DGRAM;           /* 0, SOCK_STREAM, SOCK_DGRAM, etc. */
    }

    rc = getaddrinfo(remoteIP, NULL, &hints, &res);
    if ( rc != 0)
    {
        printf("... Function: %s\tError setting remote address. Exiting. ... \n", __FUNCTION__);    
        exit(-1);
    }

    if (strcasecmp(pingType, "UDP")==0)
    {
        ((struct sockaddr_in *)res->ai_addr)->sin_port = htons(34344);
        strcpy(packet, "TIMESTAMP");
    }

    do
    {
        if (strcasecmp(pingType, "UDP")==0)
        {
            nsent=sendto(socketFd, packet, strlen(packet), 0, (struct sockaddr *)res->ai_addr, (socklen_t)res->ai_addrlen);
            if (nsent < 0)
            {
                continue;
            }
        }
    }while(nsent < 0);

    return;
}


double receivePacket(int socketFd, struct timeval* tvSend, pingReply** lastReplyNode, char* pingType)
{
    ssize_t nRecv = -1;
    double rc = -1;
    char recvbuf[1024];

    do
    {
        nRecv = recv(socketFd, (char *)recvbuf, 1024, 0);
    }
    while(nRecv < 0);

    if (nRecv < 0)
    {
        return -1;
    }       

    rc = processPacket(recvbuf, nRecv, tvSend, lastReplyNode, pingType);

    if (rc == -1)
    {
        printf("... Function: %s\tReceiving error in Data/Protocol ...\n", __FUNCTION__);
        return -1;
    }

    return rc;
}

void createSocket(unsigned int *sendSocketFd, unsigned int *receiveSocketFd, char *pingType)
{
#ifdef _WIN32
    WORD wVersionRequested;
    WSADATA wsaData;
    int err;

    /* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
    wVersionRequested = MAKEWORD(2, 2);

    err = WSAStartup(wVersionRequested, &wsaData);
    if (err != 0) 
    {
        /* Tell the user that we could not find a usable */
        /* Winsock DLL.                                  */
        printf("WSAStartup failed with error: %d\n", err);
        exit(-1);
    }
#endif

#ifdef _WIN32
    if (strcasecmp(pingType, "UDP")==0)
    {
        int rc = -1;
        struct sockaddr_in src_address;
        unsigned long int length;
        int optval = 1;
        DWORD Length;
        OSVERSIONINFO     g_OSVersionInfo;
        BOOLEAN           g_IsWindowsLonghorn = TRUE;
        BOOLEAN           g_UseFtosToSetTos = TRUE;
        int  ret, iVal=0;
        unsigned int sz = sizeof(iVal);

        g_OSVersionInfo.dwOSVersionInfoSize = sizeof( OSVERSIONINFO);
        GetVersionEx( &g_OSVersionInfo );

        if( g_OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT )
        {
            if( g_OSVersionInfo.dwMajorVersion >= 6 )
            {
                g_IsWindowsLonghorn = TRUE;
                g_UseFtosToSetTos = TRUE;
            }
        }

        *receiveSocketFd = INVALID_SOCKET;
        *receiveSocketFd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
        if (*receiveSocketFd < 0)
        {
            printf("Function: %s\tReceiving Socket creation error.\tErrNo %d. ...\n", __FUNCTION__, WSAGetLastError());
            exit(-1);
        }

        src_address.sin_family=AF_INET;
        src_address.sin_addr.s_addr=inet_addr("x.x.x.x");
        src_address.sin_port=htons(0);

        rc = bind((SOCKET)*receiveSocketFd,(struct sockaddr *)&src_address,sizeof(src_address));
        if (rc < 0)
        {
            printf("Function: %s\tReceiving Socket bind error.\tErrNo %d. ...\n", __FUNCTION__, WSAGetLastError());
            exit(-1);
        }   

        iVal = 30000;   // in milliseconds
        ret = setsockopt(*receiveSocketFd, SOL_SOCKET, SO_RCVTIMEO, (char *)&iVal, sz);
        if (ret == SOCKET_ERROR)
        {
            printf("%d\n", WSAGetLastError());
            return; // Temporary
        }

        rc = WSAIoctl((SOCKET)*receiveSocketFd, SIO_RCVALL, &optval, sizeof(optval), NULL, 0, &length, NULL, NULL);
        if (rc == SOCKET_ERROR)
        {
            printf("Function: %s\tReceiving Socket ioctl error.\tErrNo %d. ...\n", __FUNCTION__, WSAGetLastError());
            exit(-1);
        }
        printf("Function: %s\treceiveSocketFd %d ...\n", __FUNCTION__, *receiveSocketFd);
    }
    else
    {
        *receiveSocketFd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
        if (*receiveSocketFd < 0)
        {
            printf("Function: %s\tReceiving Socket creation error.\tErrNo %d. ...\n", __FUNCTION__, WSAGetLastError());
            exit(-1);
        }
        printf("Function: %s\treceiveSocketFd %d ...\n", __FUNCTION__, *receiveSocketFd);
    }
#endif

#ifndef _WIN32
    unsigned int size = 1024;       /* OK if setsockopt fails */
    setsockopt(*receiveSocketFd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
#else
    char size[5] = "1024";      /* OK if setsockopt fails */
    setsockopt(*receiveSocketFd, SOL_SOCKET, SO_RCVBUF, size, sizeof(size));
#endif

    if (strcasecmp(pingType, "UDP")==0)
    {
        *sendSocketFd = socket(AF_INET, SOCK_DGRAM, 0);
        if (*sendSocketFd < 0)
        {
            printf("Send Socket creation error.");
            exit(-1);
        }
        printf("Function: %s\tsendSocketFd %d ...\n", __FUNCTION__, *sendSocketFd);
    }

    return;
}

【问题讨论】:

  • 你确定recv函数是阻塞的,而不是你的无限循环出错了吗?您应该真正检查错误并适当地处理它,而不是在错误时永远循环,因为错误不会神奇地消失。
  • @JoachimPileborg 没有错误,recv 函数没有返回。只有当recv函数返回时,我才能检查错误。
  • 你调试了吗? IE。在调试器中单步执行代码以查看它是否真的阻塞了?或添加例如在调用recv 之前和之后的循环内打印输出以确保?因为 if 有一个错误你有一个无限循环。
  • @JoachimPileborg 是的,我已经调试过了。有两件事,一是如果我在 createSocket 函数中删除 SO_RCVTIMEO 并删除 recv 周围的 while 循环,则 recv 挂起......它不会返回。正如我在上面的问题中提到的,数据是可用的,我可以在wireshark中看到它。

标签: c++ sockets ping recv icmp


【解决方案1】:

除非套接字在 UDP 意义上已连接,否则您不会得到“无法访问的目标”,然后您不会通过 recv() 获取它,而是从可能的后续 send() 中获取它作为 errno 值.

【讨论】:

  • 正如您在代码中看到的,我使用了绑定调用。您在这里所说的连接套接字到底是什么意思?我相信,无法连接 ICMP 套接字。正如我所提到的,我在wireshark 中看到了一个可用的数据包,以响应我之前发送的UDP 数据包。我只想知道,为什么数据包卡在内核中并且没有传递给我的 recv 函数?我已经多次运行代码,并且偶尔会通过 recv 函数读取数据,但大多数时候 recv 挂起。你能详细说明你的答案吗?
猜你喜欢
  • 2012-04-21
  • 2013-03-25
  • 2013-07-31
  • 1970-01-01
  • 1970-01-01
  • 2013-02-16
  • 2012-07-27
  • 2016-08-10
  • 1970-01-01
相关资源
最近更新 更多