阻塞socket。
  –阻塞调用是指调用结果返回之前,当前线程会被挂起。函数只有在得到结果之后才会返回。
  –对于文件操作read,fread函数调用会将线程阻塞。
  –对于socket,accept与recv、recvfrom函数调用会将线程阻塞。
  –为了避免整个进程被阻塞后挂起,所以在阻塞模式下,往往需要采用多线程技术。
  –一个进程中可并发的线程总数是有限的,在处理大量客户端sokcet连接(比如上万个client socket),通过线程并发处理socket并不方便,效率也不高。
 
非阻塞socket。
  –非阻塞调用是指调用立刻返回。
  –在非阻塞模式下,accept与recv、recvfrom函数调用会立刻返回。
  –在nonblocking状态下调用accept函数,如果没有客户端socket连接请求,那么accept函数返回-1,同时errno值为11。
  –在nonblocking状态下调用recv、recvfrom函数,如果没有数据,函数返回-1,同时errno值为11。如果socket已经关闭,函数返回0。
  –在nonblocking状态下对一个已经关闭的socket调用send函数,将引发一个SIGPIPE信号,进程必须捕捉这个信号,因为SIGPIPE系统默认的处理方式是关闭进程。
 
fcntl函数调用
fcntl函数可以将文件或者socket描述符设置为阻塞或者非阻塞状态
int fcntl(int fd, int cmd, ... /* arg */ );
参数fd为要设置的文件描述符或者socket。
参数cmd, F_GETFL为得到目前状态, F_SETFL为设置状态。
宏定义O_NONBLOCK代表非阻塞,0代表阻塞。
返回值为描述符当前状态。
 
 
fcntl函数调用设置非阻塞例子
int opts = fcntl(st, F_GETFL);
if (opts < )
{
printf("fcntl failed %s\n", strerror(errno));
}
opts = opts | O_NONBLOCK;
if (fcntl(st, F_SETFL, opts) < )
{
printf("fcntl failed %s\n", strerror(errno));
}
fcntl函数调用设置阻塞例子
if (fcntl(st, F_SETFL, ) < )
{
printf("fcntl failed %s\n", strerror(errno));
}
在非阻塞模式下,如何能知道accept与recv有数据返回呢?
epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。
 
 
epoll的系统调用函数:
  –epoll_create
•epoll_create用来创建一个epoll文件描述符。
  –epoll_ctl
•epoll_ctl用来添加/修改/删除需要侦听的文件描述符及其事件。
  –epoll_wait。
•epoll_wait接收发生在被侦听的描述符上的,用户感兴趣的IO事件。
  –epoll文件描述符用完后,需要用close关闭。
  –每次添加/修改/删除文件描述符都需要调用epoll_ctl,所以要尽量少地调用epoll_ctl。
 
 
epoll_create:
  –int epoll_create(int size);
  –epoll_create创建一个epoll的句柄。
  –参数size指定epoll所支持的最大句柄数。
  –函数会返回一个新的epoll句柄,之后的所有操作将通过这个句柄来进行操作。
  –在用完句柄之后,需要用close()来关闭这个创建出来的epoll句柄。
 
 
epoll_ctl:
  –int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
  –参数epfd是epoll_create()的返回值。
  –参数op表示动作,用三个宏来表示:
•EPOLL_CTL_ADD:注册新的fd到epfd中;
•EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
•EPOLL_CTL_DEL:从epfd中删除一个fd;
  –参数fd是需要监听的socket描述符。
  –参数event通知内核需要监听什么事件。
 
struct epoll_event结构如下:
typedef union epoll_data {
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t; struct epoll_event {
__uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
events定义:
  –EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
  –EPOLLOUT:表示对应的文件描述符可以写;
  –EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
  –EPOLLERR:表示对应的文件描述符发生错误;
  –EPOLLHUP:表示对应的文件描述符被挂断;
  –EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的;
  –EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里
 
 
关于ET、LT两种工作模式:
LT(level triggered)是缺省的工作方式,并且同时支持block和no-block socket.
  –在LT模式中,内核通知一个文件描述符是否就绪了,然后可以对这个就绪的fd进行IO操作。
  –如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。
 
 
ET (edge-triggered)是高速工作方式,只支持no-block socket。
  –在ET模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。
  –ET模式会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了。
  –如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知。
ET和LT的区别:
  –LT事件不会丢弃,而是只要读buffer里面有数据可以让用户读,则不断的通知你。
  –ET则只在事件发生之时通知。可以简单理解为LT是水平触发,而ET则为边缘触发。
  –LT模式只要有事件未处理就会触发,而ET则只在高低电平变换时(即状态从1到0或者0到1)触发。
epoll_wait:
  –int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
  –参数epfd是epoll_create()的返回值。
  –参数events是一个epoll_event*的指针,当epoll_wait这个函数操作成功之后,epoll_events里面将储存所有的读写事件。
  –参数maxevents是当前需要监听的所有socket句柄数。
  –参数timeout是 epoll_wait的超时,为0的时候表示马上返回,为-1的时候表示一直等下去,直到有事件范围,正整数表示等这么长的时间。
  –一般如果网络主循环是单独的线程的话,可以用-1来等,这样可以保证一些效率,如果是和主逻辑在同一个线程的话,则可以用0来保证主循环的效率。
epoll_wait范围之后应该是一个循环,遍历所有的事件。
 
 
 
 
epoll例子

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h> ssize_t socket_recv(int st)
{
char buf[];
memset(buf, , sizeof(buf));
ssize_t rc = recv(st, buf, sizeof(buf), );
if (rc <= )
{
printf("recv failed %s\n", strerror(errno));
} else
{
printf("recv %s\n", buf);
send(st, buf, rc, );
}
return rc;
} int socket_accept(int listen_st)
{
struct sockaddr_in client_addr;
socklen_t len = sizeof(client_addr);
memset(&client_addr, , sizeof(client_addr));
int client_st = accept(listen_st, (struct sockaddr *) &client_addr, &len);
if (client_st < )
printf("accept failed %s\n", strerror(errno));
else
printf("accept by %s\n", inet_ntoa(client_addr.sin_addr));
return client_st;
} void setnonblocking(int st) //将socket设置为非阻塞
{
int opts = fcntl(st, F_GETFL);
if (opts < )
{
printf("fcntl failed %s\n", strerror(errno));
}
opts = opts | O_NONBLOCK;
if (fcntl(st, F_SETFL, opts) < )
{
printf("fcntl failed %s\n", strerror(errno));
}
} int socket_create(int port)
{
int st = socket(AF_INET, SOCK_STREAM, );
int on = ;
if (setsockopt(st, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -)
{
printf("setsockopt failed %s\n", strerror(errno));
return ;
}
struct sockaddr_in addr;
memset(&addr, , sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(st, (struct sockaddr *) &addr, sizeof(addr)) == -)
{
printf("bind port %d failed %s\n", port, strerror(errno));
return ;
}
if (listen(st, ) == -)
{
printf("listen failed %s\n", strerror(errno));
return ;
}
return st;
} int main(int arg, char *args[])
{
if (arg < )
return -;
int iport = atoi(args[]);
int listen_st = socket_create(iport);
if (listen_st == )
return -; struct epoll_event ev, events[]; //声明epoll_event结构体的变量,ev用于注册事件,数组用于回传要处理的事件
int epfd = epoll_create(); //生成用于处理accept的epoll专用的文件描述符
setnonblocking(listen_st); //把socket设置为非阻塞方式
ev.data.fd = listen_st; //设置与要处理的事件相关的文件描述符
ev.events = EPOLLIN | EPOLLERR | EPOLLHUP; //设置要处理的事件类型
epoll_ctl(epfd, EPOLL_CTL_ADD, listen_st, &ev); //注册epoll事件 int st = ;
while ()
{
int nfds = epoll_wait(epfd, events, , -); //等待epoll事件的发生
if (nfds == -)
{
printf("epoll_wait failed %s\n", strerror(errno));
break;
} int i;
for (i = ; i < nfds; i++)
{
if (events[i].data.fd < )
continue; if (events[i].data.fd == listen_st) //监测到一个SOCKET用户连接到了绑定的SOCKET端口,建立新的连接。
{
st = socket_accept(listen_st);
if (st >= )
{
setnonblocking(st);
ev.data.fd = st;
ev.events = EPOLLIN | EPOLLERR | EPOLLHUP; //设置要处理的事件类型
epoll_ctl(epfd, EPOLL_CTL_ADD, st, &ev);
continue;
}
}
if (events[i].events & EPOLLIN) //socket收到数据
{
st = events[i].data.fd;
if (socket_recv(st) <= )
{
close(st);
events[i].data.fd = -;
}
}
if (events[i].events & EPOLLERR) //socket错误
{
close(st);
events[i].data.fd = -;
} if (events[i].events & EPOLLHUP) //socket错误
{
close(st);
events[i].data.fd = -;
}
}
}
close(epfd);
return ;
}
 

搜索

复制

非阻塞socket与epoll的更多相关文章

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

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

  2. Linux - 非阻塞socket编程处理EAGAIN错误

            在linux进行非阻塞的socket接收数据时经常出现Resource temporarily unavailable,errno代码为11(EAGAIN),这表明你在非阻塞模式下调用 ...

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

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

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

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

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

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

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

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

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

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

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

    阻塞socket和非阻塞socket 建立连接阻塞方式下,connect首先发送SYN请求道服务器,当客户端收到服务器返回的SYN的确认时,则connect返回.否则的话一直阻塞.非阻塞方式,conn ...

  9. 非阻塞socket调用connect, epoll和select检查连接情况示例

    转自http://www.cnblogs.com/yuxingfirst/archive/2013/03/08/2950281.html 我们知道,linux下socket编程有常见的几个系统调用: ...

随机推荐

  1. CodeForces - 154C:Double Profiles (hash+排序)

    You have been offered a job in a company developing a large social network. Your first task is conne ...

  2. 如果两个人,两台电脑同时登录同一个帐号,同时对同一个账单提交,账单同时被服务器处理,那服务器应该先处理谁的,或者怎么规避这个问题。 非单点登录,重定向,stoken拦截器的问题

    方法一:给用户设置个状态 服务器端坐标记,比如数据库中增加一列,标识是否登陆,登录时先判断这个就行了,不过要考虑非正常退出的情况 http 方法二:在用户表里面 多加一个状态字段,登录成功 改变状态  ...

  3. VMware12版虚拟机怎么安装win7系统(详细教程

    转自:http://jingyan.baidu.com/article/cd4c29791fcf1b756e6e6034.html VMware12版虚拟机怎么安装win7系统(详细教程) 现 在很多 ...

  4. 学习动态性能表(13)--v$open_cursor

    学习动态性能表 第13篇--V$OPEN_CURSOR  2007.6.8 本视图列出session打开的所有cursors,很多时候都将被用到,比如:你可以通过它查看各个session打开的curs ...

  5. Operating System-进程/线程内部通信-管程(Monitor)介绍,实现以及应用

    本文主要内容: 管程(Monitor)介绍 管程实现 管程应用 一.管程(Monitor)介绍 1.1 管程 前一篇文章介绍了信号量以及使用,信号量已经提供了一个方便且高效的进程同步机制,但是信号量有 ...

  6. MySQL 用户权限详细汇总(转)

    1,MySQL权限体系 MySQL 的权限体系大致分为5个层级: 全局层级: 全局权限适用于一个给定服务器中的所有数据库.这些权限存储在mysql.user表中.GRANT ALL ON .和REVO ...

  7. (转)AppCan中调用系统浏览器打开网页

    <!DOCTYPE html> <html> <head> <style>body{ background:#fff; font-size:30px;} ...

  8. [转] Linux 查找文件内容

    Linux查找文件内容的常用命令方法. 从文件内容查找匹配指定字符串的行: $ grep "被查找的字符串" 文件名例子:在当前目录里第一级文件夹中寻找包含指定字符串的.in文件g ...

  9. 【转】 Pro Android学习笔记(七八):服务(3):远程服务:AIDL文件

    目录(?)[-] 在AIDL中定义服务接口 根据AIDL文件自动生成接口代码 文章转载只能用于非商业性质,且不能带有虚拟货币.积分.注册等附加条件.转载须注明出处:http://blog.csdn.n ...

  10. 通过phpMyAdmin拿webshell

    general_log默认为关闭的,root权限开启后,general_log_file会保存所有的查询语句 所以可以开启general_log,然后设置general_log_file为一个php文 ...