Linux C ++套接字选择循环 [英] linux C++ socket select loop

查看:38
本文介绍了Linux C ++套接字选择循环的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在使用套接字时遇到了一些麻烦,在循环时,除了第一个循环外,我没有收到任何数据,每次都超时.如果我关闭并重新打开套接字,则每个循环虽然我似乎都能正确获取数据.有什么想法吗?

不关闭而循环的示例:

  int socketHandle =套接字(AF_INET,SOCK_DGRAM,0);sockaddr_in serverAddr;serverAddr.sin_family = AF_INET;serverAddr.sin_addr.s_addr = inet_addr(/* UDP IP地址*/);serverAddr.sin_port = htons(/* UDP端口*/);struct timeval tv;fd_set rfds;FD_ZERO(& rfds);FD_SET(socketHandle,& rfds);tv.tv_usec = 0.0;int recVal = 0;int sockLen = sizeof(serverAddr);bind(socketHandle,(struct sockaddr *)& serverAddr,(socklen_t)sockLen);bool timePassed = false;time_t startListenTime = time(NULL);tv.tv_sec = maxUpdateTime;而(真){recVal = select(socketHandle + 1,& rfds,NULL,NULL,& tv);开关(recVal){案例(0):{//超时休息;}情况1):{//错误休息;}默认:{/*数据包数据类型*/pkt;if(recvfrom(socketHandle,& pkt,sizeof(/*数据包数据类型*/),0,(sockaddr *)& serverAddr,(socklen_t *)&sockLen)< 0){//无法接收数据休息;}别的{//收到的数据!!}休息;}}} 

关闭循环的示例:

  while(true){int socketHandle =套接字(AF_INET,SOCK_DGRAM,0);sockaddr_in serverAddr;serverAddr.sin_family = AF_INET;serverAddr.sin_addr.s_addr = inet_addr(/* UDP IP地址*/);serverAddr.sin_port = htons(/* UDP端口*/);struct timeval tv;fd_set rfds;FD_ZERO(& rfds);FD_SET(socketHandle,& rfds);tv.tv_usec = 0.0;int recVal = 0;int sockLen = sizeof(serverAddr);bind(socketHandle,(struct sockaddr *)& serverAddr,(socklen_t)sockLen);bool timePassed = false;time_t startListenTime = time(NULL);tv.tv_sec = maxUpdateTime;recVal = select(socketHandle + 1,& rfds,NULL,NULL,& tv);开关(recVal){案例(0):{//超时休息;}情况1):{//错误休息;}默认:{/*数据包数据结构*/pkt;if(recvfrom(socketHandle,& pkt,sizeof(/*数据包数据结构*/),0,(sockaddr *)& serverAddr,(socklen_t *)&sockLen)< 0){//读取数据包失败休息;}别的{//读取数据包!!}休息;}}关闭(socketHandle);} 

解决方案

select()函数使用指定的文件描述符掩码来确定要监视某个事件(读取,写入,等等.).当文件描述符可用于I/O活动(读,写)时, select()函数将修改描述符,以指示哪些文件已准备好进行给定的I/O操作.

请参见选择函数以及与文件一起使用的宏/函数的相关文章描述符.

旧式的Unix类型程序经常将文件描述符当作位掩码,而只是检查位.但是,文件描述符的实际实现会因编译器而异,因此最好使用标准文件描述符宏/函数来设置,清除和测试各种文件描述符.

因此,在使用 select()函数时,您需要使用 FD_ZERO() FD_SET(),以便您设置特定的您需要为此特定调用 select()函数的文件描述符.当 select()返回时,它将指示哪个指定的文件描述符实际上已准备好用于I/O操作(读取,写入等).

所以您的代码实际上将类似于:

  while(true){fd_set rfds;FD_ZERO(& rfds);FD_SET(socketHandle,& rfds);recVal = select(socketHandle + 1,& rfds,NULL,NULL,& tv);开关(recVal){案例(0):{//超时休息;}情况1):{//错误休息;}默认:{/*数据包数据类型*/pkt;if(recvfrom(socketHandle,& pkt,sizeof(/*数据包数据类型*/),0,(sockaddr *)& serverAddr,(socklen_t *)&sockLen)< 0){//无法接收数据休息;}别的{//收到的数据!!}休息;}} 

但是,您真正应该做的是使用 FD_ISSET()函数来检查哪些特定的文件描述符已准备好使用.在您的情况下,您只有一个,但是在有多个描述符的情况下,您也想使用 FD_ISSET()函数.

I'm having a little trouble with sockets, when looping I'm not receiving data except for the first loop, it's timing out each time. If I close and reopen the socket each loop though I seem to be getting the data correctly. Any ideas as to why?

Example of looping without closing:

int socketHandle = socket(AF_INET,SOCK_DGRAM,0);

sockaddr_in serverAddr;

serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = inet_addr(/*UDP IP ADDRESS*/);
serverAddr.sin_port = htons(/*UDP PORT*/);

struct timeval tv;
fd_set rfds;

FD_ZERO(&rfds);
FD_SET(socketHandle, &rfds);

tv.tv_usec = 0.0;
int recVal = 0;
int sockLen = sizeof(serverAddr);
bind(socketHandle, (struct sockaddr*)&serverAddr, (socklen_t)sockLen);

bool timePassed = false;
time_t startListenTime = time(NULL);

tv.tv_sec = maxUpdateTime;

while(true)
{
    recVal = select(socketHandle + 1, &rfds, NULL, NULL, &tv);
    switch(recVal)
    {
        case(0):
        {
            //Timeout
            break;
        }
        case(-1):
        {
            //Error
             break;
        }
        default:
        {
            /*Packet Data Type*/ pkt;
            if(recvfrom(socketHandle, &pkt, sizeof(/*Packet Data Type*/), 0, (sockaddr*)&serverAddr, (socklen_t*)&sockLen) < 0)
            {
                //Failed to Recieve Data
                break;
            }
            else
            {
                //Recieved Data!!
            }
            break;
        }
    }
}

Example of looping with closing:

while(true)
{
    int socketHandle = socket(AF_INET,SOCK_DGRAM,0);

    sockaddr_in serverAddr;

    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = inet_addr(/*UDP IP ADDRESS*/);
    serverAddr.sin_port = htons(/*UDP PORT*/);

    struct timeval tv;
    fd_set rfds;

    FD_ZERO(&rfds);
    FD_SET(socketHandle, &rfds);

    tv.tv_usec = 0.0;
    int recVal = 0;
    int sockLen = sizeof(serverAddr);
    bind(socketHandle, (struct sockaddr*)&serverAddr, (socklen_t)sockLen);


    bool timePassed = false;
    time_t startListenTime = time(NULL);

    tv.tv_sec = maxUpdateTime;

    recVal = select(socketHandle + 1, &rfds, NULL, NULL, &tv);
    switch(recVal)
    {
        case(0):
        {
            //Timeout
            break;
        }
        case(-1):
        {
            //Error
             break;
        }
        default:
        {
            /*Packet Datastructure*/ pkt;
            if(recvfrom(socketHandle, &pkt, sizeof(/*Packet Datastructure*/), 0, (sockaddr*)&serverAddr, (socklen_t*)&sockLen) < 0)
            {
                //Failed to read packet
                break;
            }
            else
            {
                //Read Packet!!
            }
            break;
        }
    }
    close(socketHandle);
}

解决方案

The select() function uses the specified file descriptor mask to determine which file descriptors to monitor for an event (read, write, etc.). When a file descriptor is available for an I/O activity (read, write) the select() function modifies the descriptors to indicate which of the files are ready for the given I/O action.

See this article on the select function and the macros/functions used with the file descriptors.

Old style Unix type programs often treated the file descriptor as a bit mask and just checked the bits. However the actual implementation of the file descriptor can vary by compiler so it is best to use the standard file descriptor macros/functions to set, clear, and test the various file descriptors.

So when using the select() function you need to use FD_ZERO() and FD_SET() so that you will set the specific file descriptors that you want for this particular call to the select() function. When select() returns, it will indicate which of the file descriptors designated are actually ready to be used for the I/O action (read, write, etc.).

So your code will actually be something like:

while(true)
{
    fd_set rfds;

    FD_ZERO(&rfds);
    FD_SET(socketHandle, &rfds);
    recVal = select(socketHandle + 1, &rfds, NULL, NULL, &tv);
    switch(recVal)
    {
        case(0):
        {
            //Timeout
            break;
        }
        case(-1):
        {
            //Error
             break;
        }
        default:
        {
            /*Packet Data Type*/ pkt;
            if(recvfrom(socketHandle, &pkt, sizeof(/*Packet Data Type*/), 0, (sockaddr*)&serverAddr, (socklen_t*)&sockLen) < 0)
            {
                //Failed to Recieve Data
                break;
            }
            else
            {
                //Recieved Data!!
            }
            break;
        }
    }

However what you really should do is to use the FD_ISSET() function to check which particular file descriptors are ready for use. In your case you have only the one but in a situation where there were multiple descriptors you would want to use the FD_ISSET() function as well.

这篇关于Linux C ++套接字选择循环的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆