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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • BUAA_OO_Unit4 & 学期 总结「建议收藏」

    BUAA_OO_Unit4 & 学期 总结「建议收藏」BUAA_OO_Unit4 & 学期 总结

    2022年4月22日
    48
  • navicat永久激活【2021最新】

    (navicat永久激活)这是一篇idea技术相关文章,由全栈君为大家提供,主要知识点是关于2021JetBrains全家桶永久激活码的内容https://javaforall.net/100143.htmlIntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,上面是详细链接哦~00OE5RWT28-eyJsaWNlb…

    2022年3月30日
    76
  • dropout层

    dropout层之前了解的网络都是在全连接层加dropout层,防止模型过拟合。在看deepfm的模型的时候,发现在每一层都可以加入dropout层对dropout层的深入理解做分类的时候,Dropout层一般加在全连接层防止过拟合提升模型泛化能力。而很少见到卷积层后接Dropout(原因主要是卷积参数少,不易过拟合)er类似卷积神经网络,只是在最后的全连接层使用dropout,循环神经…

    2022年6月10日
    63
  • POJ2029——Get Many Persimmon Trees

    POJ2029——Get Many Persimmon Trees

    2022年1月4日
    38
  • leetcode-103二叉树的锯齿形层序遍历「建议收藏」

    leetcode-103二叉树的锯齿形层序遍历「建议收藏」给定一个二叉树,返回其节点值的锯齿形层序遍历。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。例如:给定二叉树 [3,9,20,null,null,15,7], 3 / \ 9 20 / \ 15 7返回锯齿形层序遍历如下:[ [3], [20,9], [15,7]]/** * Definition for a binary tree node. * struct TreeNode { * int

    2022年8月8日
    4
  • 怎么新建pytest的ini文件_python读取ini配置文件

    怎么新建pytest的ini文件_python读取ini配置文件前言pytest配置文件可以改变pytest的运行方式,它是一个固定的文件pytest.ini文件,读取配置信息,按指定的方式去运行查看pytest.ini的配置选项pytest-h找到以下

    2022年7月29日
    6

发表回复

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

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