Linux(程序设计):55—非阻塞connect(EINPROGRESS)「建议收藏」

Linux(程序设计):55—非阻塞connect(EINPROGRESS)「建议收藏」非阻塞connect详情介绍可以参见文章:https://blog.csdn.net/qq_41453285/article/details/89890429一、非阻塞connect概述man手册connect的man手册有如下一段内容:EINPROGRESSThesocketisnonblockingandtheconnectioncannotbe…

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

一、非阻塞connect概述

man手册

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

Linux(程序设计):55---非阻塞connect(EINPROGRESS)「建议收藏」

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(程序设计):55---非阻塞connect(EINPROGRESS)「建议收藏」

  • 解析文档,非阻塞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(程序设计):55---非阻塞connect(EINPROGRESS)「建议收藏」

二、非阻塞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(程序设计):55---非阻塞connect(EINPROGRESS)「建议收藏」

测试②

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

Linux(程序设计):55---非阻塞connect(EINPROGRESS)「建议收藏」

 

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

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

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


相关推荐

  • mysql show status详解

    mysql show status详解

    2021年8月1日
  • div:给div加滚动条 div的滚动条设置

    div:给div加滚动条 div的滚动条设置
    今天做了个例子:
    div的滚动条问题:
    两种方法:
    一、

    记住宽和高一定要设置噢,否则不成的

    不过在不超出时,会有下面的滚动条,所以不是最好的选择
     
    二、

    2022年7月12日
  • PySpark-prophet预测

    PySpark-prophet预测简介Prophet是facebook开源时间序列预测工具,使用时间序列分解与机器学习拟合的方法进行建模预测。关于prophet模型优点我不打算说,网络上的文章非常多,各种可视化,和参数的解释与demo演示。但是在正在用到工业上大规模的可供学习的中文材料并不多。本文打算使用pyspark进行多序列预测建模,会给出一个比较详细的脚本,供交流学习,重点在于使用hive数据/分布式,以及中间数据预处理,以及pandas_udf对多条序列进行循环执行。背景说明,在十万级别的sku序列上使用prophet预测每

    2022年6月20日
  • java对象转JSONObject、JSONObject转java对象及String转JSONObject

    java对象转JSONObject、JSONObject转java对象及String转JSONObjectJSONObjectjo=(JSONObject)JSONObject.toJSON(javaBean);Studentstu=(Student)JSONObject.toBean(jo,Student.class);

    2022年4月30日
  • 怎样使用 iOS 7 的 AVSpeechSynthesizer 制作有声书(2)

    怎样使用 iOS 7 的 AVSpeechSynthesizer 制作有声书(2)

    2021年11月29日
  • 关于MAC安装Pycharm的总结「建议收藏」

    关于MAC安装Pycharm的总结「建议收藏」关于MAC安装Pycharm的总结昨天,我根据微信公众号里的Mac软件管家下载安装一个Pycharm,我下载的是最新版本的(2019那款),是简体中文版。下载链接:pan.baidu.com/s/1R7j0tQ5EEqAWZjP_tmz5VQ提取码:cixh大家也可以自行关注那个公众号下载你所要滴!但是我在最后激活的时候遇到了一点小麻烦,如下图所示:然后我找到了一些方法去解决,希望…

    2022年8月26日

发表回复

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

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