使用非阻塞连接在LAN上进行主机发现

时间:2013-10-11 08:59:42

标签: c sockets

我尝试通过执行非阻塞连接然后执行select以查看套接字是可写还是异常,来尝试在网络上检查主机。我尝试在网络上建立套接字连接端口80,139。如果套接字在连接后可写,或者在主机发送RST包等时发生异常,则主机将是可发现的。

我使用Windows套接字编写了一个代码并且逻辑工作正常,但是使用linux套接字程序没有给出所需的结果。即使没有主机,select函数也会返回任何给定的ip地址1在那个ip上。选择会在winsock的情况下超时返回0.我已经编写了下面的代码,让我知道问题究竟在哪里。

#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <stdio.h>
#include <sys/fcntl.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

int port[]={80,139};

void Free(int Sock_Arr[])
{
    for(int i=0;i<2;i++)
    {
        close(Sock_Arr[i]);
    }
    return ;
}

int  main()
{
    int Socket[2],result=0; //Socket array
    struct sockaddr_in service;


    fd_set writefds;
    fd_set exceptfds;


    struct timeval timer;
    timer.tv_sec=5;

    timer.tv_usec=0;

    int flag=0;

    FD_ZERO(&writefds);
    FD_ZERO(&exceptfds);


    char Ip_Addr[20];
    for(int i=0;i<2;i++)
    {
        if((Socket[i]=socket(AF_INET, SOCK_STREAM, IPPROTO_IP)) < 0)
        {
             Free(Socket);
        }
        fcntl(Socket[i],F_SETFL,O_NONBLOCK);



    }

    bzero(&service, sizeof(sockaddr_in));

    printf("Enter the ip-address : ");
    scanf("%s",Ip_Addr);



    service.sin_family=AF_INET;
    service.sin_addr.s_addr=inet_addr(Ip_Addr);



    for(int i=0;i<2;i++)
    {


        FD_SET(Socket[i],&writefds);
        FD_SET(Socket[i],&exceptfds);
        service.sin_port=htons((unsigned short int )port[i]);
        connect(Socket[i],(struct sockaddr *)&service,sizeof(sockaddr_in));



        result= select(Socket[i]+1,NULL,&writefds,&exceptfds,&timer);

        if(result<0||result==0)
        {
                flag=0;
                printf("\n The machine could not be found on the port %d ",port[i]);
                printf("result : %d",result);
                perror("select");

        }
        else
        {

                printf("\n The machine could  be found on the port %d ",port[i]);
                flag=1;
                printf("result : %d",result);

                if(FD_ISSET(Socket[i],&writefds))
                {
                    printf("The socket triggered on write on port %d",port[i]);
                }

                else if(FD_ISSET(Socket[i],&exceptfds))
                {
                    printf("The socket triggered on except on port %d",port[i]);
                }
                else
                {
                    printf("No socket triggered on %d",port[i]);
                }

        }

        FD_ZERO(&writefds);
        FD_ZERO(&exceptfds);



    }

    Free(Socket);

    if(flag==1)
    {
        return 1;
    }
    else
    {
        return 0;
    }



}

1 个答案:

答案 0 :(得分:1)

下面

result= select(Socket[i],NULL,&writefds,&exceptfds,&timer);

select的第一个参数应该是你想要select()在+ 1上的最大文件描述符。你只是l你没有加1,这可能会导致问题。试试

result= select(Socket[i]+1,NULL,&writefds,&exceptfds,&timer);

你最好不要在套接字上有一个循环并将所有套接字文件描述符添加到fd_sets,计算出最大的文件描述符,执行所有非阻塞connect调用和然后只使用一次select调用来检查其中任何一个上的套接字活动(使用最大文件描述符+1作为要选择的第一个参数)。然后,如果select()返回肯定,您可以使用FD_ISSET来标识导致select()触发的套接字;这样,您可以使用select()一次查找多个套接字上的活动。这是我在多个套接字上监听活动时的工作方式。

至少你应该清理你的fd_set,每次迭代你执行connect()调用的第二个循环,因为你没有删除你在之前的迭代中添加的套接字fds,所以你可能除了当前添加的插座之外,还要寻找其他插座上的活动。

此外,按照您的方式执行此操作,您不会重置发送到select()电话的超时值。在select手册页中,它明确指出select 可能更改超时值。因此,如果是这种情况,则有可能通过对select的后续迭代调用减少了超时,并尝试在每次迭代时将超时重置为5秒。如果超时已减少到零,那么select()调用将立即返回(轮询)。