转自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检查连接情况示例的更多相关文章

  1. socket编程 —— 非阻塞socket (转)---例子已上传至文件中

    在上一篇文章 <socket编程——一个简单的例子> http://blog.csdn.net/wind19/archive/2011/01/21/6156339.aspx 中写了一个简单 ...

  2. socket阻塞与非阻塞,同步与异步,select,pool,epool

    概念理解 一.与I/O相关的五个重要概念 1. 第一个概念:用户空间与内核空间 1. 现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32次方) 2. ...

  3. 非阻塞socket与epoll

    阻塞socket. –阻塞调用是指调用结果返回之前,当前线程会被挂起.函数只有在得到结果之后才会返回. –对于文件操作read,fread函数调用会将线程阻塞. –对于socket,accept与re ...

  4. Linux 网络编程七(非阻塞socket:epoll--select)

    阻塞socket --阻塞调用是指调用结果返回之前,当前线程会被挂起.函数只有在得到结果之后才会返回. --对于文件操作 read,fread函数调用会将线程阻塞(平常使用read感觉不出来阻塞, 因 ...

  5. JAVA基础知识之网络编程——-基于NIO的非阻塞Socket通信

    阻塞IO与非阻塞IO 通常情况下的Socket都是阻塞式的, 程序的输入输出都会让当前线程进入阻塞状态, 因此服务器需要为每一个客户端都创建一个线程. 从JAVA1.4开始引入了NIO API, NI ...

  6. linux网络编程中阻塞和非阻塞socket的区别

    读操作 对于阻塞的socket,当socket的接收缓冲区中没有数据时,read调用会一直阻塞住,直到有数据到来才返 回.当socket缓冲区中的数据量小于期望读取的数据量时,返回实际读取的字节数.当 ...

  7. 阻塞和非阻塞socket的区别

    读操作 对于阻塞的socket,当socket的接收缓冲区中没有数据时,read调用会一直阻塞住,直到有数据到来才返回.当socket缓冲区中的数据量小于期望读取的数据量时,返回实际读取的字节数.当s ...

  8. 【2018.08.13 C与C++基础】网络通信:阻塞与非阻塞socket的基本概念及简单实现

    一.前言 最近在做Matalb/Simulink与C/C++的混合编程,主要是完成TCP.UDP.SerialPort等常见通信方式的中间件设计,为Simulink模型提供数据采集及解析模块. 问题在 ...

  9. 斐讯面试记录—阻塞Socket和非阻塞Socket

    文章出自:http://blog.csdn.net/VCSockets/ 1.TCP中的阻塞Socket和非阻塞Socket 阻塞与非阻塞是对一个文件描述符指定的文件或设备的两种工作方式. 阻塞的意思 ...

随机推荐

  1. HDU 4920 居然会超时

    题意:求两个n*n的矩阵相乘的结果,得出的每个元素%3: 分析:2000ms然后n的范围是800,我们自己估算的时间复杂度并不会超时,但是结果就是超时了. #include <cstdio> ...

  2. 简单C程序生成的汇编代码分析

    首先给出完整的C代码: int g(int x) { ; } int f(int x) { return g(x); } int main(void) { )+; } 使用命令:gcc –S –o h ...

  3. ios 改变push方向,可以把present改为push方式

    - (void)pop{    CATransition* transition = [CATransition animation];    transition.duration = 0.5;   ...

  4. Win10/UWP 让你的App使用上扫描仪

    UWP的扫描仪功能现在被微软划分到了[Windows Desktop Extensions for the UWP]中,如果要使用扫描仪扫描图片到自己的App中,首先我们要添加[Windows Des ...

  5. EasyUI 开发笔记(一)

    由于某些原因,在公司做的后台需要改成类似于Ext.js 形式的后台,主要看好其中的 框架布局,以及tab开页面和弹出式内部窗体. 后来看看,改成EasyUI,较Ext.js 库小很多,也便于公司的初级 ...

  6. LVM逻辑卷基本概念及LVM的工作原理

    这篇随笔将详细讲解Linux磁盘管理机制中的LVM逻辑卷的基本概念以及LVM的工作原理!!! 一.传统的磁盘管理 其实在Linux操作系统中,我们的磁盘管理机制和windows上的差不多,绝大多数都是 ...

  7. Kafka Topic ISR不全,个别Spark task处理时间长

    现象 Spark streaming读kafka数据做业务处理时,同一个stage的task,有个别task的运行时间比多数task时间都长,造成业务延迟增大. 查看业务对应的topic发现当topi ...

  8. C语言拾遗

    1. 没C++那么恶心的const C语言中的const修饰符用于修饰一个变量是const属性的.被C语言的const修饰的变量具有只读属性,并且不能被修改. const修饰的变量 != 常量,con ...

  9. 从一个数组中提取出第start位到第end位

    假设通过数组in来表示一个很大的数(in[0]表示最低bit),提取该数的第start位到第end位(计数起始位为0): #define MAX_BYTE_LEN ( 48 ) int getData ...

  10. Codeforces Round #325 (Div. 2) D bfs

    D. Phillip and Trains time limit per test 1 second memory limit per test 256 megabytes input standar ...