Linux socket非阻塞connect方法(一)

本文详细介绍了Linux中非阻塞模式下socket连接的过程,包括connect函数的使用、非阻塞connect的工作原理以及如何通过select和getsockopt检测连接状态。在非阻塞模式下,connect会立即返回,如果连接未建立成功,可以通过select或再次调用connect来判断连接状态。常见错误码如EINPROGRESS、EISCONN等也在文中提及。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        socket  non-blocking mode connect


        对于面向连接的socket类型(SOCK_STREAM,SOCK_SEQPACKET),在读写数据之前必须建立连接,connect()函数用于完成面向连接的socket的建链过程,对于TCP,也就是三次握手过程。

connect()函数

头文件:

    #include<sys/types.h>

    #include<sys/socket.h>

声明:

    int connect (int sockfd,struct sockaddr * serv_addr,int addrlen);
功能:

    使用套接字sockfd建立到指定网络地址serv_addr的socket连接,参数addrlen为serv_addr指向的内存空间大小,即sizeof(struct sockaddr_in)。

返回值:

   1)成功返回0,表示连接建立成功(如服务器和客户端是同一台机器上的两个进程时,会发生这种情况)

   2)失败返回SOCKET_ERROR,相应的设置errno,通过errno获取错误信息。常见的错误有对方主机不可达或者超时错误,也可能是对方主机没有进程监听对应的端口。

非阻塞connect(non-block mode connect)

套接字执行I/O操作有阻塞和非阻塞两种模式。在阻塞模式下,在I/O操作完成前,执行操作的函数一直等候而不会立即返回,该函数所在的线程会阻塞在这里。相反,在非阻塞模式下,套接字函数会立即返回,而不管I/O是否完成,该函数所在的线程会继续运行。

客户端调用connect()发起对服务端的socket连接,如果客户端的socket描述符为阻塞模式,则connect()会阻塞到连接建立成功或连接建立超时(linux内核中对connect的超时时间限制是75s, Soliris 9是几分钟,因此通常认为是75s到几分钟不等)。如果为非阻塞模式,则调用connect()后函数立即返回,如果连接不能马上建立成功(返回-1),则errno设置为EINPROGRESS,此时TCP三次握手仍在继续。此时可以调用select()检测非阻塞connect是否完成。select指定的超时时间可以比connect的超时时间短,因此可以防止连接线程长时间阻塞在connect处。

select判断规则:

   1)如果select()返回0,表示在select()超时,超时时间内未能成功建立连接,也可以再次执行select()进行检测,如若多次超时,需返回超时错误给用户。

   2)如果select()返回大于0的值,则说明检测到可读或可写的套接字描述符。源自 Berkeley 的实现有两条与 select 和非阻塞 I/O 相关的规则:

        A) 当连接建立成功时,套接口描述符变成 可写(连接建立时,写缓冲区空闲,所以可写)

        B) 当连接建立出错时,套接口描述符变成 既可读又可写(由于有未决的错误,从而可读又可写)

        因此,当发现套接口描述符可读或可写时,可进一步判断是连接成功还是出错。这里必须将B)和另外一种连接正常的情况区分开,就是连接建立好了之后,服务器端发送了数据给客户端,此时select同样会返回非阻塞socket描述符既可读又可写。

        □对于Unix环境,可通过调用getsockopt来检测描述符集合是连接成功还是出错(此为《Unix Network Programming》一书中提供的方法,该方法在Linux环境上测试,发现是无效的):

               A)如果连接建立是成功的,则通过getsockopt(sockfd,SOL_SOCKET,SO_ERROR,(char *)&error,&len) 获取的error 值将是0

               B)如果建立连接时遇到错误,则errno 的值是连接错误所对应的errno值,比如ECONNREFUSED,ETIMEDOUT 等

        □一种更有效的判断方法,经测试验证,在Linux环境下是有效的

        再次调用connect,相应返回失败,如果错误errno是EISCONN,表示socket连接已经建立,否则认为连接失败。

综上所述,这里总结一下非阻塞connect的实现过程。

非阻塞connect的实现过程

1. 创建套接字sockfd

/* 1. obtain a socket */
int sock_fd;
sock_fd = socket(AF_INET, SOCK_STREAM, 0);

2. 设置套接字为非阻塞模式

/* 2. set non-blocking mode no socket */
#if 1
int flags = fcntl(sock_fd, F_GETFL, 0);
fcntl(sock_fd, F_SETFL, flags|O_NONBLOCK);
#else
int imode = 1;
ioctl(sock_fd, FIONBIO, &imode);

3. 调用connect进行连接


                
### Linux Socket 阻塞模式与非阻塞模式 #### 阻塞模式工作原理 在阻塞模式下,当调用诸如 `accept`、`connect` 或者 `recv` 这样的函数时,程序会暂停执行直到操作完成。这意味着如果数据尚未到达,则接收方将直等待;同样,在发送过程中如果没有足够的缓冲区空间可用,进程也会被挂起直至条件满足。 对于服务器端来说,通常采用多线程或多进程的方式处理并发连接请求,因为每次接受新连接都会使当前线程/进程进入等待状态[^1]。 ```c int sockfd = socket(AF_INET, SOCK_STREAM, 0); bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); listen(sockfd, BACKLOG); while(1){ int connfd = accept(sockfd, NULL, NULL); // 此处会发生阻塞 } ``` #### 非阻塞模式工作原理 相比之下,设置为非阻塞后的套接字不会让应用程序陷入长时间的停滞不前的状态。相反,旦遇到无法立即完成的操作(比如读取的数据还未准备好),它就会返回错误码 EAGAIN 或 EWOULDBLOCK 而不是继续等待下去。这允许开发者通过轮询或其他机制来管理多个 I/O 流量而无需额外创建大量子进程或线程[^2]。 要启用非阻塞行为,可以利用 fcntl 函数修改文件描述符标志: ```c fcntl(sock_fd, F_SETFL, O_NONBLOCK); // 设置成功后尝试 connect 可能立刻失败并给出特定 errno 值, // 如 EINPROGRESS 表明正在建立连接但未完成。 ``` #### 使用方法区别 - **初始化阶段** - 创建普通 TCP/IP 客户端和服务端时,默认处于阻塞方式; - 若要切换至非阻塞需显式更改属性。 - **事件响应** - 对于阻塞型接口而言,只要发起次 IO 请求便意味着可能经历较久时间间隔才能得到回应; - 在非阻塞情形里则应循环检测是否具备可操作性,并妥善处理因资源暂时不可得引发的各种异常状况。 #### 实际应用中的差异 实际编程实践中,选择哪种模式取决于具体需求以及性能考量因素。例如高并发场景往往倾向于使用非阻塞模型配合 select/poll/epoll 等多路复用技术实现高效的网络通信框架设计[^3]。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值