linux 设置connect 超时代码[select/epoll]
转载请注明来源:https://www.cnblogs.com/hookjc/
linux下socket编程有常见的几个系统调用:
对于服务器来说, 有socket(), bind(),listen(), accept(),read(),write()
对于客户端来说,有socket(),connect()
这里主要要讲的是客户端这边的connect函数。
对于客户端来说,需要打开一个套接字,然后与对端服务器连接,例如:
1 int main(int argc, char **argv)
2 {
3 struct sockaddr_in s_addr;
4 memset(&s_addr, 0, sizeof(s_addr));
5 s_addr.sin_family = AF_INET;
6 s_addr.sin_addr.s_addr = inet_addr("remote host");
7 s_addr.sin_port = htons(remote port);
8 socklen_t addr_len = sizeof(struct sockaddr);
9 int c_fd = socket(AF_INET, SOCK_STREAM, 0);
10 int ret = connect(c_fd, (struct sockaddr*)&s_addr, addr_len);
11 ......
12 }
当connect上对端服务器之后,就可以使用该套接字发送数据了。
我们知道,如果socket为TCP套接字, 则connect函数会激发TCP的三次握手过程,而三次握手是需要一些时间的,内核中对connect的超时限制是75秒,就是说如果超过75秒则connect会由于超时而返回失败。但是如果对端服务器由于某些问题无法连接,那么每一个客户端发起的connect都会要等待75才会返回,因为socket默认是阻塞的。对于一些线上服务来说,假设某些对端服务器出问题了,在这种情况下就有可能引发严重的后果。或者在有些时候,我们不希望在调用connect的时候阻塞住,有一些额外的任务需要处理;
这种场景下,我们就可以将socket设置为非阻塞,如下代码:
int flags = fcntl(c_fd, F_GETFL, 0);
if(flags < 0) {
return 0;
}
fcntl(c_fd, F_SETFL, flags | O_NONBLOCK);
当我们将socket设置为NONBLOCK后,在调用connect的时候,如果操作不能马上完成,那connect便会立即返回,此时connect有可能返回-1, 此时需要根据相应的错误码errno,来判断连接是否在继续进行。
当errno=EINPROGRESS时,这种情况是正常的,此时连接在继续进行,但是仍未完成;同时TCP的三路握手操作继续进行;后续只要用select/epoll去注册对应的事件并设置超时时间来判断连接否是连接成功就可以了。
int ret = connect(c_fd, (struct sockaddr*)&s_addr, addr_len);
while(ret < 0) {
if( errno == EINPROGRESS ) {
break;
} else {
perror("connect fail'\n");
return 0;
}
}
这个地方,我们很可能会判断如果ret小于0,就直接判断连接失败而返回了,没有根据errno去判断EINPROGRESS这个错误码。这里也是昨天在写份程序的时候遇到的一个坑。
使用非阻塞 connect 需要注意的问题是:
1. 很可能 调用 connect 时会立即建立连接(比如,客户端和服务端在同一台机子上),必须处理这种情况。
2. Posix 定义了两条与 select 和 非阻塞 connect 相关的规定:
1)连接成功建立时,socket 描述字变为可写。(连接建立时,写缓冲区空闲,所以可写)
2)连接建立失败时,socket 描述字既可读又可写。 (由于有未决的错误,从而可读又可写)
不过我同时用epoll也做了实验(connect一个无效端口,errno=110, errmsg=connect refused),当连接失败的时候,会触发epoll的EPOLLERR与EPOLLIN,不会触发EPOLLOUT。
当用select检测连接时,socket既可读又可写,只能在可读的集合通过getsockopt获取错误码。
当用epoll检测连接时,socket既可读又可写,只能在EPOLLERR中通过getsockopt获取错误码。
完整代码如下:
/*
* File: main.cpp
* Created on March 7, 2013, 5:54 PM
*/ #include <cstdlib>
#include <string>
#include <iostream> #include <sys/epoll.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/select.h>
#include <error.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h> using namespace std; struct so {
int fd;
string val;
}; int select_version(int *fd) {
int c_fd = *fd;
fd_set rset, wset;
struct timeval tval;
FD_ZERO(&rset);
FD_SET(c_fd, &rset);
wset = rset;
tval.tv_sec = 0;
tval.tv_usec = 300 * 1000; //300毫秒
int ready_n;
if ((ready_n = select(c_fd + 1, &rset, &wset, NULL, &tval)) == 0) {
close(c_fd); /* timeout */
errno = ETIMEDOUT;
perror("select timeout.\n");
return (-1);
}
if (FD_ISSET(c_fd, &rset)) {
int error;
socklen_t len = sizeof (error);
if (getsockopt(c_fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
cout << "getsockopt error." << endl;
return -1;
}
// cout << "in fire." << error << endl;
}
if (FD_ISSET(c_fd, &wset)) {
int error;
socklen_t len = sizeof (error);
if (getsockopt(c_fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
cout << "getsockopt error." << endl;
return -1;
}
// cout << "out fire." << error << endl;
}
return 0;
} int epoll_version(int *fd) {
int c_fd = *fd;
int ep = epoll_create(1024);
struct epoll_event event;
event.events = (uint32_t) (EPOLLIN | EPOLLOUT | EPOLLET);
struct so _data;
_data.fd = c_fd;
_data.val = "test";
event.data.ptr = (void*) &_data;
epoll_ctl(ep, EPOLL_CTL_ADD, c_fd, &event);
struct epoll_event eventArr[1000];
int status, err;
socklen_t len;
err = 0;
len = sizeof (err);
int n = epoll_wait(ep, eventArr, 20, 300); // 300ms
for (int i = 0; i < n; i++) {
epoll_event ev = eventArr[i];
int events = ev.events;
if (events & EPOLLERR) {
struct so* so_data = (struct so*) ev.data.ptr;
cout << so_data->val << ",err event fire." << endl;
status = getsockopt(c_fd, SOL_SOCKET, SO_ERROR, &err, &len);
cout << status << "," << err << endl;
return -1;
}
if (events & EPOLLIN) {
struct so* so_data = (struct so*) ev.data.ptr;
cout << so_data->val << ",in event fire." << endl;
status = getsockopt(c_fd, SOL_SOCKET, SO_ERROR, &err, &len);
cout << status << "," << err << endl;
}
if (events & EPOLLOUT) {
struct so* so_data1 = (struct so*) ev.data.ptr;
cout << so_data1->val << ",out event fire." << endl;
}
}
return 0;
} int main(int argc, char** argv) {
string ip = "127.0.0.1";
int port = 25698;
int c_fd, flags, ret;
struct sockaddr_in s_addr;
memset(&s_addr, 0, sizeof (s_addr));
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(port);
s_addr.sin_addr.s_addr = inet_addr(ip.c_str()); if ((c_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("create socket fail.\n");
exit(0);
}
flags = fcntl(c_fd, F_GETFL, 0);
if (flags < 0) {
perror("get socket flags fail.\n");
return -1;
} if (fcntl(c_fd, F_SETFL, flags | O_NONBLOCK) < 0) {
perror("set socket O_NONBLOCK fail.\n");
return -1;
}
ret = connect(c_fd, (struct sockaddr*) &s_addr, sizeof (struct sockaddr));
if (ret < 0) {
if (errno != EINPROGRESS) { exit(0);
}
}
if (epoll_version(&c_fd) < 0)//select_version(&c_fd);
{
perror("epoll connect remote server fail.\n");
printf("%d\n", errno);
exit(0);
}
// send data
// recv data
return 0;
}
来源:python脚本自动迁移
linux 设置connect 超时代码[select/epoll]的更多相关文章
- linux 设置connect 超时
转载请注明来源:https://www.cnblogs.com/hookjc/ 将一个socket 设置成阻塞模式和非阻塞模式,使用fcntl方法,即: 设置成非阻塞模式: 先用fcntl的F_GET ...
- Linux下connect超时处理【总结】
1.前言 最近在写一个测试工具,要求快速的高效率的扫描出各个服务器开放了哪些端口.当时想了一下,ping只能检测ip,判断服务器的网络是连通的,而不能判断是否开放了端口.我们知道端口属于网络的应用层, ...
- Linux下connect超时处理
1.前言 最近在写一个测试工具,要求快速的高效率的扫描出各个服务器开放了哪些端口.当时想了一下,ping只能检测ip,判断服务器的网络是连通的,而不能判断是否开放了端口.我们知道端口属于网络的传输层, ...
- linux网络编程 IO多路复用 select epoll
本文以我的小型聊天室为例,对于服务器端的代码,做了三次改进,我将分别介绍阻塞式IO,select,epoll . 一:阻塞式IO 对于聊天室这种程序,我们最容易想到的是在服务器端accept之后,然后 ...
- iOS 设置connect超时
NSLock *theLock; [theLock lock]; int fd, error; struct sockaddr_in addr; ))<) { cout<<" ...
- linux下connect超时时间探究
最近在linux做服务器开发的时候,发现了一个现象:服务器在启动的时候调用了 connect 函数,因为连接了一个不可用的端口,导致connect最后报出了 “Connection timed out ...
- VC socket Connect 超时时间设置
设置connect超时很简单,CSDN上也有人提到过使用select,但却没有一个令人满意与完整的答案.偶所讲的也正是select函数,此函数集成在winsock1.1中,简单点讲,"作用使 ...
- C Socket编程之Connect超时 (转)
网络编程中socket的分量我想大家都很清楚了,socket也就是套接口,在套接口编程中,提到超时的概念,我们一下子就能想到3个:发送超时,接收超时,以及select超时(注:select函数并不是只 ...
- 设置linux中tcp默认的20秒connect超时时间(转)
无论你用任何语言或者是网络库,你都可以设置网络操作的超时时间,特别是connect.read.write的超时时间. 你可以在代码中把超时时间设置任意大小值,但是connect方法会有一点特殊. co ...
随机推荐
- [高数]高数部分-Part II 导数与微分
Part II 导数与微分 回到总目录 Part II 导数与微分 一元函数微分的定义 一元函数定义注意点 基本求导公式 基本求导方法 复合函数求导 隐函数求导 对数求导法 反函数求导 参数方程求导 ...
- 图像处理opencv-Rect 排序、合并[转]
opencv进行rect检测时,当检测到多个rect,组成rect vector之后,有些rect是由一个区域误分割得到的, 可以按照某种规格将这些rect合并为一个rect.比如按照x,y,widt ...
- 【计理01组03号】Java基础知识
简单数据类型的取值范围 byte:8 位,1 字节,最大数据存储量是 255,数值范围是 −128 ~ 127. short:16 位,2 字节,最大数据存储量是 65536,数值范围是 −32768 ...
- 生成对抗网络GAN与DCGAN的理解
作者在进行GAN学习中遇到的问题汇总到下方,并进行解读讲解,下面提到的题目是李宏毅老师机器学习课程的作业6(GAN) 一.GAN 网络上有关GAN和DCGAN的讲解已经很多,在这里不再加以赘述,放几个 ...
- Log4j2日志框架集成Slf4j日志门面
1.说明 本文介绍使用日志门面Slf4j打印日志, 底层日志实现使用Log4j2框架, 方便以后切换底层日志实现, Log4j2可以替换成Logback等. 2.依赖管理 在pom.xml依赖管理中导 ...
- Jenkins Hackfest 用户体验文档报告
Jenkins 技术文档是我们项目的重要组成部分,因为它是正确使用 Jenkins 的关键.好的文档可以指导用户,并鼓励选择好的实现方式.这是用户体验的关键部分.在最近的 Jenkins UI/UX ...
- 二维数组与稀疏数组的转换---dataStructures
首先我们看一个需求 在11 * 11 的五子棋的棋盘中 我们使用0代表十字交叉点也是无效的数据 用1代表黑棋 用2代表蓝棋 那么所看到的棋盘如下 改用数字显示后就如一下样式 现在我们需要将怎个棋盘存储 ...
- MMI开机时间偏长
Mini版本开机时间长 Mini版本开机时间长1. Problem Description:2. Analysis:3. Solution:4. Summary: 1. Problem Descr ...
- 读 Linux 像读小说「GitHub 热点速览 v.22.03」
本周特推选取了一个画风有点意思的 Linux 代码带读项目 flash-linux0.11-talk,希望有趣的文风能带你读完 Linux 代码.当然画风可以增加阅读体验,彩色标记也是一种学习方法-- ...
- [Raspberry Pi] 入门使用
今天开始介绍Raspberry Pi(简称RPi,下同)入门的一些基础知识. 第1部分: 安装RPi 1.1 从 http://www.raspberrypi.org/downloads 下载RPi ...