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


相关推荐

  • idea打断点调试_vs断点调试快捷键

    idea打断点调试_vs断点调试快捷键IDEA断点调试–基础篇1前言Debug用来追踪代码的运行流程。我们通常会在程序运行过程中出现异常的时候,启用Debug模式来分析定位异常发生的位置,以及在运行过程中参数的变化。通常我们也可以启用Debug模式来跟踪代码的运行流程去学习三方框架的源码。而IDEA作为我们JAVA开发最常用的工具,所以我们对于IDEA的Debug更应该去了解一下。2断点类型IDEA中对于JAVA的断点进行了分类,有如下的4类:JavaLineBreakpoints:行断

    2022年10月20日
    3
  • matlab之griddata函数

    matlab之griddata函数griddata函数

    2022年5月25日
    252
  • android 创建数组

    android 创建数组一:privateString[]data=newString[]{“Hello”,”jike”,”world”};二:创建数组对象:privateCellData[]data=newCellData[]{newCellData(“jike”,”world”),newCellData(“title”,”content”)};publiccl…

    2022年6月1日
    30
  • checkstyle使用_idea checkstyle

    checkstyle使用_idea checkstyleCheckstyle是一款检查java程序代码样式的工具,可以有效的帮助我们检视代码以便更好的遵循代码编写标准,特别适用于小组开发时彼此间的样式规范和统一。Checkstyle提供了高可配置性,以便适

    2022年8月4日
    6
  • 大数据与云计算和物联网之间的关系是什么_大数据信息主要安全问题不包括

    大数据与云计算和物联网之间的关系是什么_大数据信息主要安全问题不包括大数据时代的到来,是全球知名咨询公司麦肯锡最早提出的,麦肯锡称:“数据,已经渗透到当今每一个行业和业务职能领域,成为重要的生产因素。人们对于海量数据的挖掘和运用,预示着新一波生产率增长和消费者盈余浪潮的到来。”《互联网进化论》一书中提出“互联网的未来功能和结构将于人类大脑高度相似,也将具备互联网虚拟感觉,虚拟运动,虚拟中枢,虚拟记忆神经系统”,并绘制…

    2022年9月29日
    6
  • OpenJDK 阅读源代码 Java 实现字节流输入类

    OpenJDK 阅读源代码 Java 实现字节流输入类

    2022年1月13日
    102

发表回复

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

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