当前位置:网站首页>I/O复用的高级应用之一:非阻塞 connect———使用 select 实现(也可以用 poll 实现)
I/O复用的高级应用之一:非阻塞 connect———使用 select 实现(也可以用 poll 实现)
2022-04-23 14:36:00 【m0_51551385】
一、啥是 非阻塞 connect
?
我们之前唯一使用过的非阻塞socket文件描述符是在服务器中,使用非阻塞的方式可以大大提升服务器程序的效率。
现在试想,我们把客户端程序的 socket 设置为非阻塞的,那么用该 socket 文件描述符进行 connect
连接就很容易报错。为什么呢?因为,非阻塞下,对该文件描述符的调用直接返回结果,而 socket 连接很难这么快完成,所以就很容易调用失败,此时会设置errno
的值为:EINPROGRESS
。该错误类型发生在,对非阻塞的 socket 调用 connect
,而连接又没有建立时。所以当 connect
返回-1时,并不一定发生了错误,可能连接还在进行中!
此事的解决方法是,调用select
、poll
等函数监听这个连接失败的 socket 上的可写事件。当select
或 poll
等函数返回后,再利用getsockopt
读取错误码。如果错误码是0,表示刚刚的 connect 确实没发生错误,连接成功建立,若错误码为1,表示确实是发生了错误。
这么麻烦的用非阻塞 connect
有啥好处吗?
通过使用非阻塞connect
,我们就能同时发起多个连接并一起等待。
2、非阻塞 connect
实例程序
下面是一个客户端程序,它使用一个非阻塞 socket 连接服务器。
#include<sys/socket.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<stdio.h>
#include<assert.h>
#include<string.h>
#include<errno.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<time.h>
#include<sys/ioctl.h>
#define BUFFER_SIZE 1023
int setnonblocking(int fd)
{
int old_option = fcntl(fd, F_GETFL);
int new_option = old_option | O_NONBLOCK;
fcntl(fd, F_SETFL, new_option);
return old_option;
}
/* 超时连接函数,参数分别为IP地址,端口号,和超时时间(毫秒)。成功返回连接状态的 socket,失败返回-1 */
int unblock_connect(const char* ip, int port, int time)
{
int ret = 0;
struct sockaddr_in address;
bzero(&address, sizeof(address));
address.sin_family = AF_INET;
address.sin_port = htons(port);
inet_pton(AF_INET, ip, &address);
int sockfd = socket(PF_INET, SOCK_STREAM, 0);
int fdopt = setnonblocking(sockfd);
ret = connect(sockfd, (struct sockaddr*)&address, sizeof(address));
if (ret == 0) /* 连接成功了 */
{
/* 如果连接成功,则恢复 sockfd 的属性,并立即返回之 */
printf("connect whit server immediately\n");
fcntl(sockfd, F_SETFL, fdopt);
return sockfd;
}
else if (errno != EINPROGRESS) /* 如果错误类型不是 EINPROGRESS,说明发生了其他的错误,立即返回 */
{
/* 如果连接没有立即建立,那么只有当 errno 是 EINPROGRESS 时才表示连接还在进行,否则出错返回 */
printf("unblock connect not support\n");
return -1;
}
fd_set readfds;
fd_set writefds;
struct timeval timeout;
FD_ZERO(&readfds);
FD_SET(sockfd, &writefds);
timeout.tv_sec = time;
timeout.tv_usec = 0;
ret = select(sockfd + 1, NULL, &writefds, NULL, &timeout);
if (ret <= 0)
{
/* select 超时或者出错,立即返回 */
printf("connection time out\n");
close(sockfd);
return -1;
}
if (!FD_ISSET(sockfd, &writefds)) /* 如果 sockfd 不可写,说明连接失败了,立即返回 */
{
printf("no events on sockfd found\n");
close(sockfd);
return -1;
}
int error = 0;
socklen_t length = sizeof(error);
/* 调用 getsockopt 来获取并清除 sockfd 上的错误 */
if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &length) < 0)
{
printf("connection failed after select with the error: %d\n", error);
close(sockfd);
return -1;
}
/* 错误号不为 0 表示连接出错 */
if (error != 0)
{
printf("connection failed after select with the error: %d\n", error);
close(sockfd);
return -1;
}
/* 连接成功 */
printf("connection ready after select with the socket: %d\n", sockfd);
fcntl(sockfd, F_SETFL, fdopt);
return sockfd;
}
int main(int argc, char* argv[])
{
if (argc <= 2)
{
return 1;
}
const char* ip = argv[1];
int port = atoi(argv[2]);
int sockfd = unblock_connect(ip, port, 10);
if (sockfd < 0)
{
return 1;
}
close(sockfd);
return 0;
}
版权声明
本文为[m0_51551385]所创,转载请带上原文链接,感谢
https://blog.csdn.net/m0_51551385/article/details/124362567
边栏推荐
- MCU function signal generator, output four kinds of waveforms, adjustable frequency, schematic diagram, simulation and C program
- tcp_diag 内核相关实现 1 调用层次
- c语言在结构体传参时参数压栈问题
- 四层和八层电梯控制系统Proteus仿真设计,51单片机,附仿真和Keil C代码
- 1分钟看懂执行流程,永久掌握for循环(附for循环案例)
- flannel 原理 之 子网划分
- C语言p2选择分支语句详解
- kprobe 的 3 种使用
- C语言知识点精细详解——初识C语言【1】——你不能不知的VS2022调试技巧及代码实操【1】
- 一篇博客让你学会在vscode上编写markdown
猜你喜欢
循环队列的基本操作(实验)
线程同步、生命周期
【Servlet】Servlet 详解(使用+原理)
Want to be an architect? Tamping the foundation is the most important
OC 转 Swift 条件编译、标记、宏、 Log、 版本检测、过期提示
DS1302的电子万年历_51单片机,年月日、星期、时分秒、农历和温度,带闹钟,全套资料
电容
QT actual combat: Yunxi chat room
AT89C52单片机的频率计(1HZ~20MHZ)设计,LCD1602显示,含仿真、原理图、PCB与代码等
51单片机+LCD12864液晶显示的俄罗斯方块游戏,Proteus仿真、AD原理图、代码、论文等
随机推荐
C语言知识点精细详解——初识C语言【1】——你不能不知的VS2022调试技巧及代码实操【1】
八路抢答器系统51单片机设计【附Proteus仿真、C程序、原理图及PCB文件、元器件清单和论文等】
AT89C52单片机的频率计(1HZ~20MHZ)设计,LCD1602显示,含仿真、原理图、PCB与代码等
flannel 原理 之 子网划分
LM317的直流可调稳压电源Multisim仿真设计(附仿真+论文+参考资料)
LLVM - 生成 if-else 以及 PH
机器学习之逻辑回归(Logistic Regression)原理讲解和实例应用,果断收藏
全连接层的作用是什么?
初识STL
电容
外包干了四年,废了...
C语言知识点精细详解——初识C语言【1】
TLS/SSL 协议详解 (30) SSL中的RSA、DHE、ECDHE、ECDH流程与区别
ASEMI整流模块MDQ100-16在智能开关电源中的作用
一个月把字节,腾讯,阿里都面了,写点面经总结……
Multisim Simulation Design of DC adjustable regulated power supply of LM317 (with simulation + paper + reference)
flannel 原理 之 TUN模式
直流可调稳压电源的Proteus仿真设计(附仿真+论文等资料)
asp.net使用MailMessage发送邮件的方法
详解TCP的三次握手