linux阻塞与非阻塞(connect连接超时)

非阻塞connect详情介绍可以参见文章:https://blog.csdn.net/qq_41453285/article/details/89890429一、非阻塞connect概述man手册connect的man手册有如下一段内容:EINPROGRESSThesocketisnonblockingandtheconnectioncannotbe…

大家好,又见面了,我是你们的朋友全栈君。

一、非阻塞connect概述

man手册

  • connect的man手册有如下一段内容:

linux阻塞与非阻塞(connect连接超时)

EINPROGRESS
    The socket is nonblocking and the connection cannot be completed immediately.  It is possible to select(2) or poll(2)  for  com‐pletion  
    by  selecting  the  socket  for writing.  After select(2) indicates writability, use getsockopt(2) to read the SO_ERROR option at level SOL_SOCKET 
    to determine whether connect() completed successfully (SO_ERROR is zero) or unsuccessfully  (SO_ERROR is one of
     the usual error codes listed here, explaining the reason for the failure).

 

linux阻塞与非阻塞(connect连接超时)

  • 解析文档,非阻塞connect如何使用:
    • ①当我们将sock设置为非阻塞之后,使用connect去连接服务端,即使服务端开启了,connect系统调用也不会连接成功,connect而是以失败告终,并返回错误
    • ②但是非阻塞connect返回的错误是有讲究的:
      • 如果非阻塞connect返回的错误是EINPROGRESS,代表不是connect系统调用出错了,而是connect可能会在后面才会建立完整地连接(只是当前连接还没有建立完整),所以我们可以在通过给select、pol或epoll设置等待时间,来等待这个connect的连接成功,从而进一步处理
      • 如果非阻塞connect返回的错误不是EINPROGRESS,代表就是connect系统调用本身出错了,那么就可以做一些相应的错误处理了
    • ③当非阻塞connect以EINPROGRESS错误返回之后,我们可以给select、pol或epoll设置等待时间,并将客户端封装在等待可写的结构中,进一步来等待非阻塞connect客户端与服务端建立完整地连接,在等待的过程中,如果非阻塞connect建立成功了,客户端的sock_fd就会变成可写的(这个在本人的IO复用文章中介绍过,见下图)
    • ④当非阻塞connect建立成功之后还可以利用getsockopt来读取错误码并清除该socket上的错误:
      • 如果错误码为0,表示连接成功建立
      • 否则连接失败

linux阻塞与非阻塞(connect连接超时)

二、非阻塞connect的移植性问题

  • 移植性问题如下:
    • 1.首先,非阻塞的socket可能导致connect始终失败
    • 2.其次,select对处于EINPROGRESS状态下的socket可能不起作用
    • 3.最后,对于出错的socket,getsockopt在有些系统(比如Linux)上返回-1,而在有些系统上(比如源自伯克利的UNIX)返回0
  • 这些问题没有一个统一的解决办法

三、编码演示案例

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <strings.h>
#include <libgen.h> //for basename
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <sys/select.h>
#include <arpa/inet.h>


int setnonblocking(int fd);

int set_nonblocking_connect(const char* ip,const char* port,int time);

int main(int argc,char *argv[])
{
    //must write ip and port
    if(argc<3){
        printf("usage:./%s [ip] [port]\n",basename(argv[0]));
        exit(EXIT_FAILURE);
    }

    int cli_fd;
    if((cli_fd=set_nonblocking_connect(argv[1],argv[2],10))==-1) 
        exit(EXIT_FAILURE);

    sleep(10);
    close(cli_fd);
    exit(EXIT_SUCCESS);
}

/*****************************************************************************
 函 数 名  : setnonblocking
 功能描述  : 将参数所指的fd设置为非阻塞
 输入参数  : int fd  
 输出参数  : 无
 返 回 值  : int
            失败退出程序,成功返回fd的旧标志
 调用函数  : 
 被调函数  : 
 
 修改历史      :
  1.日    期   : 2019年12月23日
    作    者   : 江南_董少
    修改内容   : 新生成函数

*****************************************************************************/
int setnonblocking(int fd)
{
    int old_options=fcntl(fd,F_GETFL);
    int new_options=old_options|O_NONBLOCK;

    if(fcntl(fd,F_SETFL,new_options)==-1){
        perror("fcntl");
        exit(EXIT_FAILURE);
    }
    return old_options;
}

/*****************************************************************************
 函 数 名  : set_nonblocking_connect
 功能描述  : 
 输入参数  : const char* ip  
             int port        
             int time        
 输出参数  : 无
 返 回 值  : int
 调用函数  : 
 被调函数  : 
 
 修改历史      :
  1.日    期   : 2019年12月23日
    作    者   : 江南_董少
    修改内容   : 新生成函数

*****************************************************************************/
int set_nonblocking_connect(const char* ip,const char* port,int time)
{
    int sock_fd,old_options,ret_val;
    struct sockaddr_in serAddress;

    //socket
    if((sock_fd=socket(AF_INET,SOCK_STREAM,0))==-1){
        perror("socket");
        exit(EXIT_FAILURE);
    }

    //set nonblock
    old_options=setnonblocking(sock_fd);

    //init address
    bzero(&serAddress,sizeof(serAddress));
    serAddress.sin_family=AF_INET;
    serAddress.sin_port=htons(atoi(port));
    if(inet_pton(AF_INET,ip,&serAddress.sin_addr.s_addr)==-1){
        perror("inet_pton");
        exit(EXIT_FAILURE);
    }

    //connect

    //nonblocking connect success,return sock_fd
    if((ret_val=connect(sock_fd,(struct sockaddr*)&serAddress,sizeof(serAddress)))==0){
        printf("connect with server suucess\n");

        //set fd block and return
        if(fcntl(sock_fd,F_SETFL,old_options)==-1){
            perror("fcntl");
            exit(EXIT_FAILURE);
        }
        
        return sock_fd;
    }
    
    //nonblocking connect error
    else if(errno!=EINPROGRESS){
        printf("unblock connect not support\n");
        return -1;
    }

    fd_set readfds;
    fd_set writefds;
    struct timeval timeout;

    FD_ZERO(&readfds);
    FD_ZERO(&writefds);
    timeout.tv_sec=time;
    timeout.tv_usec=0;

    ret_val=select(sock_fd+1,NULL,&writefds,NULL,&timeout);
    if(ret_val<=0){
        //select error or timeout,return
        printf("connection time out\n");
        close(sock_fd);
        return -1;
    }

    //nonblocking connect fd is not ready write,return
    if(!FD_ISSET(sock_fd,&writefds)){
        printf("no events on sock_fd found\n");
        close(sock_fd);
        return -1;
    }

    int error=0;
    socklen_t length=sizeof(error);
    //get error and save to error
    if(getsockopt(sock_fd,SOL_SOCKET,SO_ERROR,&error,&length)==-1){
        perror("getsockopt");
        exit(EXIT_FAILURE);
    }
    //error is not 0,meaning connect error
    if(error!=0){
        printf("connect failed after select with the error:%d\n",error);
        close(sock_fd);
        return -1;
    }

    //connect success
    printf("connect ready after select with the socket:%d\n",sock_fd);
    //set fd block and return
    fcntl(sock_fd,F_SETFL,old_options);
    return sock_fd;
}

测试①

  • 我们使用程序去连接8888端口的服务器,但是服务器未开启,select等待10秒之后超时退出

linux阻塞与非阻塞(connect连接超时)

测试②

  • 这个测试中,我们的服务器开启了8888监听端口
  • 我们客户端程序connect没有执行成功,但是返回了EINPROGRESS错误。于是在后面的select中等待非阻塞connect建立成功并且客户端fd变为可写的。接着消除错误编码并打印相关提示信息

linux阻塞与非阻塞(connect连接超时)

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/127648.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 监控平台架构设计

    监控平台架构设计

    2022年1月8日
    175
  • Vue开发手机端页面

    我的github记录册(PS:希望star):https://github.com/tonyzheng1…适配终端:讲解:http://www.w3cplus.com/mobile…源码:https://github.com/amfe/lib-f…代码规范https://github.com/pablohp…

    2022年4月8日
    38
  • boot sector与superblock

    boot sector与superblock

    2021年7月31日
    52
  • OpenProcessToken学习

    OpenProcessToken学习  要对一个任意进程(包括系统安全进程和服务进程)进行指定了写相关的访问权的OpenProcess操作,只要当前进程具有SeDeDebug权限就可以了。要是一个用户是Administrator或是被给予了相应的权限,就可以具有该权限。可是,就算我们用Administrator帐号对一个系统安全进程执行OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwProcessID…

    2022年6月25日
    21
  • adrc过渡过程 c语言,初步认识ADRC与应用「建议收藏」

    adrc过渡过程 c语言,初步认识ADRC与应用「建议收藏」初步认识ADRC与应用初步认识ADRC与应用这是一个目录ADRC的基本原理一、参考资料推荐二、为什么PID好,以及,为什么PID不够好1.为什么PID好——基于模型的现代控制理论不实用2.为什么PID不够好——PID的缺点三、ADRC给出的方案——如何保留PID的优点,同时弥补PID的缺点1.误差的取法——安排过渡过程2.由误差提取误差微分的方法——跟踪微分器3.加权和的策略不一定最好——非…

    2022年5月19日
    61
  • C++数据结构——队列「建议收藏」

    C++数据结构——队列「建议收藏」C++数据结构——队列参考博客:http://www.cnblogs.com/QG-whz/p/5171123.htmlhttp://www.169it.com/article/2718050585107790752.html1、队列(Queue)与栈一样,是一种线性存储结构,它具有如下特点:(1)队列中的数据元素遵循“先进先出”(FirstInFirstOut)的原则,简称FIFO结构;(…

    2022年5月5日
    119

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号