connect()函数阻塞问题

方法一:采用select

在学习嵌入式Linux网络编程中,不少同窗都发现了一个问题,那就是调用connect函数时,若是服务端关闭,客户 端调用connect()函数时,发现阻塞在那里,并且利用ctrl+c信号去中止客户端程序时,须要等待一个较为长的时间才能响应了,这个时间若是你们 细心会发现,每次都是75秒的时间。那么有没有什么比较好的办法,能够以用户能接受的一个时间响应来中止掉一个正在connect链接的客户端那?好比我 们在作一个网络控制台的程序,用户须要随时能够中止掉任何一个网络服务链接,那么对于这样一个须要等待75秒时间才能反馈出服务状态的程序,用户是没法接 受的。编程

对于如何解决这个问题,咱们能够分析下,要想完成用户在一个能接受的时间里迅速反馈出服务 端已经关闭的状态,那么咱们的程序应该作到在一个规定的时间片内,能够捕获到用户发出的控制状态,而后处理用户的需求。那么要作到能够在规定的时间片内捕 获用户的控制状态,就必须禁止让咱们的connect()函数阻塞75秒的状况发生,也就是说,要让connect()函数变为非阻塞状态才行。服务器

好了,如今解决问题的关键就是如何把connect变为非阻塞状态了,咱们知道,socket编程的操做对象是socket,而socket他又属于系统描述符类型,那么对于系统描述符,咱们是怎么操做他变为非阻塞的那?是利用fcntl()函数或者ioctl()函数。网络

想到这里,好像问题应该已经解决了,可是咱们调试发现,在服务端出现错误的时候,connect确实立刻返回,可是,若是服务端正确那,connect仍是立刻返回,这样,咱们没法判断connect函数是否成功了,那这个问题又该如何解决呢?socket

咱们是否想到了一个select函数那,他具有监听文件描述符的功能,若是咱们把以前的socket让select监听他是否可写,是否是问题也就解决了。函数

好了,那么咱们总结下整个思路:学习

1.创建socket
        2.将该socket设置为非阻塞模式
        3.调用connect()
        4.使用select()检查该socket描述符是否可写
        5.根据select()返回的结果判断connect()结果
        6.将socket设置为阻塞模式(若是你的程序不须要用阻塞模式,这步就省了,不过通常状况都是用阻塞模式,这样容易管理)
spa

那么根据上面的6个步骤,咱们写一个简单的模块程序来调试看下:调试

{
                int sockfd = socket(AF_INET, SOCK_STREAM, 0);
                if(sockfd < 0) exit(1);
                struct sockaddr_in serv_addr;
                ………//以服务器地址填充结构serv_addr
                int error=-1, len;
                len = sizeof(int);
                timeval tm;
                fd_set set;
                unsigned long ul = 1;
                ioctl(sockfd, FIONBIO, &ul); //设置为非阻塞模式
                bool ret = false;
                if( connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1)
                {
                        tm.tv_set = TIME_OUT_TIME;
                        tm.tv_uset = 0;
                        FD_ZERO(&set);
                        FD_SET(sockfd, &set);
                        if( select(sockfd+1, NULL, &set, NULL, &tm) > 0)
                        {
                                getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len);
                                if(error == 0) ret = true;
                                else ret = false;
                        } else ret = false;
                }
                else ret = true;
                ul = 0;
                ioctl(sockfd, FIONBIO, &ul); //设置为阻塞模式
                //下面还能够进行发包收包操做
                ……………
        }
方法二:定义信号处理函数
  1. sigset(SIGALRM, u_alarm_handler);
  2. alarm(2);
  3. code = connect(socket_fd, (struct sockaddr*)&socket_st, sizeof(struct sockaddr_in));
  4. alarm(0);
  5. sigrelse(SIGALRM);
     首先定义一个中断信号处理函数u_alarm_handler,用于超时后的报警处理,而后定义一个2秒的定时器,执行connect,当系统 connect成功,则系统正常执行下去;若是connect不成功阻塞在这里,则超过定义的2秒后,系统会产生一个信号,触发执行 u_alarm_handler函数, 当执行完u_alarm_handler后,程序将继续从connect的下面一行执行下去。
     其中,处理函数能够以下定义,也能够加入更多的错误处理。
    
  1. void u_alarm_handler()
  2. {
  3. }
相关文章
相关标签/搜索