了解用于接收UDP消息和打开TCP套接字的Select循环

时间:2012-12-05 20:01:45

标签: c sockets networking tcp udp

您好我正在为一个网络类工作,我们在网络中创建节点,通过udp从控制应用程序接收消息,然后与其他节点创建tcp连接。基本顺序为:1)控制管理器向节点A发送udp消息,告知它与节点B连接2)节点A接收udp消息并将udp消息转发给节点B 3)节点B接收udp消息,随机选择一个端口number,在该端口上打开一个侦听tcp套接字,并使用端口号将udp消息发送回A)节点A接收udp消息并在该tcp端口上打开到节点B的连接。

基本上,我想循环接收的udp消息并跳转到我的消息解析功能,同时循环tcp连接。这部分看起来很简单,但我真的不明白如何将监听套接字添加到文件描述符列表中。以下代码是我放在一起,我想知道基本结构是否正确?我真的不明白如何创建监听tcp套接字并将其添加到fd列表中?

SOCKET udpsock;
udpsock = initudp(port); //setup udp socket
SOCKET tcpsock;
FD_ZERO(&rdsocks);

max = udpsock + 1;
while(1)
{
    SOCKET temp;

    FD_SET(udpsock,&rdsocks);//setup udp macros
    FD_SET(tcpsock,&rdsocks);

    if( select(max,&rdsocks,NULL,NULL,NULL) == SOCKET_ERROR )
    {
        perror("Select error");
        WSACleanup();
        return 1;
    }
    for(temp = 0; temp<=max;temp++) // loop on TCP sockets
    {
        if(FD_ISSET(temp,&rdsocks))
        {
            printf("Socket %d is ready \n",temp);
            // process tcp messages
        }

    }
    if(FD_ISSET(udpsock,&rdsocks)) // udp connection, parse control message
    {
        int ret = 0;
        res = recvfrom(udpsock, buff,sizeof(buff),0,(struct sockaddr*)&udpclient,lenaddr);
        //process udp message, setup tcp connection here if requested and add to file descriptor list?
    }

}

2 个答案:

答案 0 :(得分:2)

创建侦听TCP套接字(socket(2)bind(2)listen(2))后,使用setsockopt(2)将其标记为非阻塞,并将其添加到读取集中select(2)

当它变为“可读”时,表示您有待连接的客户端连接,请致电accept(2)。将新连接的套接字也添加到读取集中。

您可能希望保留列表/哈希/这些客户端套接字中的任何一个,因为在每次调用select(2)之前必须重新初始化read-set并计算其第一个参数(max fd)。

注0:我在这里引用Linux手册页,但逻辑几乎是跨平台的。您可以在MSDN上找到Windows引用。 注1:Windows select() IGNORES是其第一个参数。

答案 1 :(得分:1)

在调用FD_ZERO()FD_SET()之前,您需要在每次循环迭代时调用select()。此外,在尝试首先连接之前,请不要向fd_set添加TCP套接字。保留您创建的TCP套接字列表,以便在每次循环迭代时将它们重新添加到fd_set

尝试这样的事情:

udpsock = initudp(port); //setup udp socket

std::vector<SOCKET> tcpsocks;
SOCKET tcpsock;

while(1)
{
    FD_ZERO(&rdsocks);
    FD_SET(udpsock, &rdsocks);
    max = udpsock;

    for(size_t i = 0; i < tcpsocks.size(); ++i)
    {
        tcpsock = tcpsocks[i];
        FD_SET(tcpsock, &rdsocks);

        if( tcpsock > udpsock )
            max = tcpsock;
    }

    if( select(max+1, &rdsocks, NULL, NULL, NULL) == SOCKET_ERROR )
    {
        perror("Select error");
        WSACleanup();
        return 1;
    }

    for(size_t i = 0; i < tcpsocks.size(); ++i) // loop on TCP sockets
    {
        tcpsock = tcpsocks[i];            
        if( FD_ISSET(tcpsock, &rdsocks) )
        {
            printf("Socket %d is ready \n", tcpsock);
            // process tcp message
        }
    }

    if( FD_ISSET(udpsock, &rdsocks) )
    {
        int ret = 0;
        res = recvfrom(udpsock, buff, sizeof(buff), 0, (struct sockaddr*)&udpclient, lenaddr);
        // process udp message...
        if( setup tcp connection is requested )
        {
            tcpsock = ...;
            if( tcpsock != INVALID_SOCKET )
                tcpsocks.push_back(tcpsock);
        }
    }
}