非阻塞socket调用connect, epoll和select检查连接情况示例
转自http://www.cnblogs.com/yuxingfirst/archive/2013/03/08/2950281.html
我们知道,linux下socket编程有常见的几个系统调用:
对于服务器来说, 有socket(), bind(),listen(), accept(),read(),write()
对于客户端来说,有socket(),connect()
这里主要要讲的是客户端这边的connect函数。
对于客户端来说,需要打开一个套接字,然后与对端服务器连接,例如:
int main(int argc, char **argv)
{
struct sockaddr_in s_addr;
memset(&s_addr, , sizeof(s_addr));
s_addr.sin_family = AF_INET;
s_addr.sin_addr.s_addr = inet_addr("remote host");
s_addr.sin_port = htons(remote port);
socklen_t addr_len = sizeof(struct sockaddr);
int c_fd = socket(AF_INET, SOCK_STREAM, );
int ret = connect(c_fd, (struct sockaddr*)&s_addr, addr_len);
......
}
当connect上对端服务器之后,就可以使用该套接字发送数据了。
我们知道,如果socket为TCP套接字, 则connect函数会激发TCP的三次握手过程,而三次握手是需要一些时间的,内核中对connect的超时限制是75秒,就是说如果超过75秒则connect会由于超时而返回失败。但是如果对端服务器由于某些问题无法连接,那么每一个客户端发起的connect都会要等待75才会返回,因为socket默认是阻塞的。对于一些线上服务来说,假设某些对端服务器出问题了,在这种情况下就有可能引发严重的后果。或者在有些时候,我们不希望在调用connect的时候阻塞住,有一些额外的任务需要处理;
这种场景下,我们就可以将socket设置为非阻塞,如下代码:
int flags = fcntl(c_fd, F_GETFL, );
if(flags < ) {
return ;
}
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 < ) {
if( errno == EINPROGRESS ) {
break;
} else {
perror("connect fail'\n");
return ;
}
}
这个地方,我们很可能会判断如果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 = ;
tval.tv_usec = * ; //300毫秒
int ready_n;
if ((ready_n = select(c_fd + , &rset, &wset, NULL, &tval)) == ) {
close(c_fd); /* timeout */
errno = ETIMEDOUT;
perror("select timeout.\n");
return (-);
}
if (FD_ISSET(c_fd, &rset)) {
int error;
socklen_t len = sizeof (error);
if (getsockopt(c_fd, SOL_SOCKET, SO_ERROR, &error, &len) < ) {
cout << "getsockopt error." << endl;
return -;
}
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) < ) {
cout << "getsockopt error." << endl;
return -;
}
cout << "out fire." << error << endl;
}
return ;
} int epoll_version(int *fd) {
int c_fd = *fd;
int ep = epoll_create();
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[];
int status, err;
socklen_t len;
err = ;
len = sizeof (err);
int n = epoll_wait(ep, eventArr, , );
for (int i = ; 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;
}
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;
}
} } int main(int argc, char** argv) {
string ip = "127.0.0.1";
int port = ;
int c_fd, flags, ret;
struct sockaddr_in s_addr;
memset(&s_addr, , 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, )) < ) {
perror("create socket fail.\n");
exit();
}
flags = fcntl(c_fd, F_GETFL, );
if (flags < ) {
perror("get socket flags fail.\n");
return -;
} if (fcntl(c_fd, F_SETFL, flags | O_NONBLOCK) < ) {
perror("set socket O_NONBLOCK fail.\n");
return -;
}
ret = connect(c_fd, (struct sockaddr*) &s_addr, sizeof (struct sockaddr));
while (ret < ) {
if (errno == EINPROGRESS) {
break;
} else {
perror("connect remote server fail.\n");
printf("%d\n", errno);
exit();
}
}
//select_version(&c_fd);
epoll_version(&c_fd);
return ;
}
非阻塞socket调用connect, epoll和select检查连接情况示例的更多相关文章
- socket编程 —— 非阻塞socket (转)---例子已上传至文件中
在上一篇文章 <socket编程——一个简单的例子> http://blog.csdn.net/wind19/archive/2011/01/21/6156339.aspx 中写了一个简单 ...
- socket阻塞与非阻塞,同步与异步,select,pool,epool
概念理解 一.与I/O相关的五个重要概念 1. 第一个概念:用户空间与内核空间 1. 现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32次方) 2. ...
- 非阻塞socket与epoll
阻塞socket. –阻塞调用是指调用结果返回之前,当前线程会被挂起.函数只有在得到结果之后才会返回. –对于文件操作read,fread函数调用会将线程阻塞. –对于socket,accept与re ...
- Linux 网络编程七(非阻塞socket:epoll--select)
阻塞socket --阻塞调用是指调用结果返回之前,当前线程会被挂起.函数只有在得到结果之后才会返回. --对于文件操作 read,fread函数调用会将线程阻塞(平常使用read感觉不出来阻塞, 因 ...
- JAVA基础知识之网络编程——-基于NIO的非阻塞Socket通信
阻塞IO与非阻塞IO 通常情况下的Socket都是阻塞式的, 程序的输入输出都会让当前线程进入阻塞状态, 因此服务器需要为每一个客户端都创建一个线程. 从JAVA1.4开始引入了NIO API, NI ...
- linux网络编程中阻塞和非阻塞socket的区别
读操作 对于阻塞的socket,当socket的接收缓冲区中没有数据时,read调用会一直阻塞住,直到有数据到来才返 回.当socket缓冲区中的数据量小于期望读取的数据量时,返回实际读取的字节数.当 ...
- 阻塞和非阻塞socket的区别
读操作 对于阻塞的socket,当socket的接收缓冲区中没有数据时,read调用会一直阻塞住,直到有数据到来才返回.当socket缓冲区中的数据量小于期望读取的数据量时,返回实际读取的字节数.当s ...
- 【2018.08.13 C与C++基础】网络通信:阻塞与非阻塞socket的基本概念及简单实现
一.前言 最近在做Matalb/Simulink与C/C++的混合编程,主要是完成TCP.UDP.SerialPort等常见通信方式的中间件设计,为Simulink模型提供数据采集及解析模块. 问题在 ...
- 斐讯面试记录—阻塞Socket和非阻塞Socket
文章出自:http://blog.csdn.net/VCSockets/ 1.TCP中的阻塞Socket和非阻塞Socket 阻塞与非阻塞是对一个文件描述符指定的文件或设备的两种工作方式. 阻塞的意思 ...
随机推荐
- ajax请求!
ajax请求: var data ='{"useName":"'+name+'",}' $.ajax({ type:"post", url: ...
- c++11新的小猫腻
1.void*指针的使用,平时见得也很多了,至于为什么使用void* 指针,很多人有自己的见解,反正普通指针轻轻松松的转向void * 指针,但是void*指针转向其他的指针都要采用强制转换的. 2. ...
- LayaAir引擎——(九)
var h = new Array(); var j = new Array(); var xbCursor = 0; function xbinit() { xbinitName(); xbRect ...
- java的(PO,VO,TO,BO,DAO,POJO)解释
java的(PO,VO,TO,BO,DAO,POJO)解释 O/R Mapping 是 Object Relational Mapping(对象关系映射)的缩写.通俗点讲,就是将对象与关系数据库绑定, ...
- 使用WP8最新的AudioVideoCaptureDevice类制作录像应用
WP8出来好一段时间了,新出的AudioVideoCaptureDevice类自定义功能比WP7的CaptureSource强大的多,但网上比较全面的中文实例还比较少,分享一个最近做的小实例给大家参考 ...
- sql表结构和注释
SELECT 表名=case when a.colorder=1 then d.name else '' end, 表说明=case when a.colorder=1 then isnull(f.v ...
- jquery 操作
Jquery使用时要引用,引用时放在最前. Jquery: $代表选择器, $(document) ready(function(e){}):找到页面,页面加载完成后执行. JS选取元素操作内容操作属 ...
- 数据存储之Cookie和Web Storage。
Cookie Cookie,有时也用其复数形式Cookies,指某些网站为了辨别用户身份.进行session跟踪而储存在用户本地终端上的数据(通常经过加密).接下来就谈谈cookie的一些利弊,coo ...
- STM32中的PWM的频率和占空比的设置
转于http://blog.csdn.net/liming0931/article/details/8491468 下面的这个是stm32的定时器逻辑图,上来有助于理解: TIM3的ARR寄存器和 ...
- 临时解决系统中大量的TIME_WAIT连接
今天,偶然间发现后台服务与数据库之间有大量的TIME_WAIT的连接: [root@localhost logs]# netstat -an | grep TIME_WAIT tcp a.a.a.a: ...