交换机与路由器:
交换机:用于局域网内网的数据转发
路由器:用于连接局域网和外网
IP地址:
- IP地址是Internet中主机的标识
- Internet中的主机要与别的机器通信必须具有一个IP地址
- IP地址为32位(IPv4)或者128位(IPv6)
- 每个数据包都必须携带目的IP地址和源IP地址,路由器依靠此信息为数据包选择路由
- 表示形式:常用点分形式,如202.38.64.10,最后都会转换为一个32位的无符号整数。 192.168.1.31 C类 , 点分十进制IP
C0A8011F 4*8 = 32位
IP分类:
IP地址根据网络号和主机号来分,分为A
、B
、C
三类及特殊地址D
、E
。 全0和全1的都保留不用。
-
A类:
(1.0.0.0-126.0.0.0)(默认子网掩码:255.0.0.0或 0xFF000000)第一个字节为网络号,后三个字节为主机号。该类IP地址的最前面为“0”,所以地址的网络号取值于1~126之间。一般用于大型网络。 -
B类:
(128.0.0.0-191.255.0.0)(默认子网掩码:255.255.0.0或0xFFFF0000)前两个字节为网络号,后两个字节为主机号。该类IP地址的最前面为“10”,所以地址的网络号取值于128~191之间。一般用于中等规模网络。 -
C类:
(192.0.0.0-223.255.255.0)(子网掩码:255.255.255.0或 0xFFFFFF00)前三个字节为网络号,最后一个字节为主机号。该类IP地址的最前面为“110”,所以地址的网络号取值于192~223之间。一般用于小型网络。 -
D类:
是多播地址。该类IP地址的最前面为“1110”,所以地址的网络号取值于224~239之间。一般用于多路广播用户
E类:是保留地址。该类IP地址的最前面为“1111”,所以地址的网络号取值于240~255之间。
IP=网络号+主机号;
理论IP地址范围:A类:1.0.0.0 - 126.255.255.255
B类:128.0.0.0 - 191.255.255.255
C类:192.0.0.0 - 223.255.255.255
D类:224.0.0.0 - 239.255.255.255
-
怎样把IP拆分成网络号和主机号
网络号:IP与子网掩码相与结果为网络号
主机号:子网掩码取反,与IP相与为主机号 -
子网掩码:
- 子网掩码:是一个32位的整数,作用是将某一个IP划分成网络地址和主机地址;目的是合理的利用IP资源;
- 子网掩码长度是和IP地址长度完全一样的32bit的二进制数组成;
- 前半部分全1(连续1),后半部分全0;
- 例如:
C类地址,同一网段最多可以连接多少个主机???
192.168.1.0 - 192.168.1.255 => 256
(最多可以连接主机的个数为254,除网络地址0,广播地址255) - 网络概念:
网络号相同的主机组成一个局域网,局域网可以理解为是一个小型网络。
若干个小型网络组合在一起可以组合成大型网络 => 以太网
Inetnet
【1】OSI模型与TCP/IP协议体系结构
网络的体系结构
- 网络采用分而治之的方法设计,将网络的功能划分为不同的模块,以分层的形式有机组合在一起。
- 每层实现不同的功能,其内部实现方法对外部其他层次来说是透明的。每层向上层提供服务,同时使用下层提供的服务
- 网络体系结构即指网络的层次结构和每层所使用协议的集合
- 两类非常重要的体系结构:OSI与TCP/IP
OSI开放系统互联模型- OSI模型是一个理想化的模型,尚未有完整的实现
- OSI模型共有七层
OSI模型
OSI模型是最理想的模型
- 物理层:传输的是bit流(0与1一样的数据),物理信号,没有格式
- 链路层:格式变为帧(把数据分成包,一帧一帧的数据进行发送)
- 网络层:路由器中是有算法的,ip,(主机到主机)(路由的转发)
- 传输层:端口号,数据传输到具体那个进程程序 (端到端)
- 会话层:通信管理,负责建立或者断开通信连接
- 表示层:确保一个系统应用层发送的消息可以被另一个系统的应用层读取,编码转换,数据解析,管理数据加密,解密;
- 应用层:指定特定应用的协议,文件传输,文件管理,电子邮件等。
【2】 TCP/IP协议族
- 应用层 TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet
- 传输层 TCP,UDP
- 网络层 IP,ICMP,RIP,OSPF,BGP,IGMP
- 网络接口与物理层 SLIP,CSLIP,PPP,ARP,RARP,MTU ISO2110,IEEE802.1,EEE802.
【3】UDP TCP 协议相同点
都存在于传输层(*****)
-
TCP :全双工、 面向连接、可靠
TCP(即传输控制协议):
是一种面向连接的传输层协议,它能提供高可靠性通信(即数据无误、数据无丢失、
数据无失序、数据无重复到达的通信)
适用情况:- 适合于对传输质量要求较高,以及传输大量数据的通信。
- 在需要可靠数据传输的场合,通常使用TCP协议
- MSN/QQ等即时通讯软件的用户登录账户管理相关的功能通常采用TCP协议
-
UDP : 无连接,不可靠
UDP(User Datagram Protocol)用户数据报协议,是不可靠的无连接的协议。
在数据发送前,因为不需要进行连接,所以可以进行高效率的数据传输。
适用情况:- 发送小尺寸数据(如对DNS服务器进行IP地址查询时)
- 在接收到数据,给出应答较困难的网络中使用UDP。
- 适合于广播/组播式通信中。
- MSN/QQ/Skype等即时通讯软件的点对点文本通讯以及音视频通讯通常采用UDP协议
- 流媒体、VOD、VoIP、IPTV等网络多媒体服务中通常采用UDP方式进行实时数据传输
【4】三种socket
sockfd = 3
是一个编程接口;返回一种特殊的文件描述符 (everything in Unix is a file)
-
流式套接字(SOCK_STREAM) TCP
- 提供了一个面向连接、可靠的数据传输服务,
- 数据无差错、无重复的发送且按发送顺序接收。内设置流量控制,
- 避免数据流淹没慢的接收方。数据被看作是字节流,无长度限制。
-
数据报套接字(SOCK_DGRAM) UDP
- 提供无连接服务。数据包以独立数据包的形式被发送
- 不提供无差错保证,数据可能丢失或重复,
- 顺序发送,可能乱序接收。
-
原始套接字(SOCK_RAW)
- 可以对较低层次协议如IP、ICMP直接访问。
【5】端口号(vi /etc/services)
-
为了区分一台主机接收到的数据包应该转交给哪个进程来进行处理,使用端口号来区别
-
TCP端口号与UDP端口号独立
-
端口号一般由IANA (Internet Assigned Numbers Authority) 管理
众所周知端口:1~1023
(1~255之间为众所周知端口,
256~1023端口通常由UNIX系统占用)
已登记端口:1024~49151
动态或私有端口:49152~65535
一般使用:6666 8888 7777 9999 10000 10001(5000+)
sin_port
不同类型CPU的主机中,内存存储多字节整数序列有两种方法,
称为主机字节序(HBO): -
小端序(little-endian) - 低序字节存储在低地址
-
大端序(big-endian)- 高序字节存储在低地址
网络中传输的数据必须按网络字节序,即大端字节序
在大部分PC机上,当应用进程将整数送入socket前,
需要转化成网络字节序;当应用进程从socket取出整数后, 要转化成小端字节序。
-
主机字节序转化网络字节序?
-
小端 转 大端
inet_addr()
-
由主机字节序转化为网络字节序(大端),返回转换后的地址。
in_addr_t inet_addr(const char *strptr);
inet_ntoa()
-
将32位网络字节序二进制地址转换成点分十进制的字符串。
char *inet_ntoa(stuct in_addr inaddr);
-
主机字节序到网络字节序
u_long htonl (u_long hostlong);
u_short htons (u_short short);
-
网络字节序到主机字节序
u_long ntohl (u_long hostlong);
u_short ntohs (u_short short); //short 2字节
【6】 TCP编程流程
-
服务器端:
socket()
,创建套接字文件,用于连接sockfd
(有一个属性默认是阻塞)bind()
, 绑定,把socket()
函数返回的文件描述符和IP、端口号进行绑定;listen()
, (监听)将socket()
返回的文件描述符的属性,由主动变为被动;accept(),
阻塞函数,阻塞等待客户端的连接请求,如果有客户端连接,则accept()
函数返回,返回一个用于通信的套接字文件;recv()
, 接收客户端发来的数据;send()
, 发送数据;close()
, 关闭文件描述符;连接、通信
-
客户端:
socket()
,创建套接字文件,既用于连接,也用于通信;
完成一个结构体的填充connect()
; 用于发起连接请求;send()
, 发送数据;recv()
, 接收数据;close()
, 关闭文件描述符;
1.socket()
NAME
socket - create an endpoint for communication
SYNOPSIS
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
功能: 创建套接字文件
参数:
domain:协议族
AF_UNIX, AF_LOCAL 用于本地通信
AF_INET IPv4 Internet protocols
AF_INET6 IPv6 Internet protocols
type:协议类型
SOCK_STREAM TCP
SOCK_DGRAM UDP
protocol:
一般情况下写0
系统默认自动帮助匹配对应协议
传输层:IPPROTO_TCP、IPPROTO_UDP、IPPROTO_ICMP
网络层:htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL)
返回值:
成功: 返回一个特殊文件描述符;
失败: -1
2.bind()
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能: 绑定,将socket()返回值和IP/端口号进行绑定;
(以什么样的形式去绑定?就是填充第二个结构体,把端口号和IP填充到这个结构体中)
参数:
sockfd: 是socket()函数的返回值;
const struct sockaddr *addr:
struct sockaddr是结构体类型,是一个通用结构体;
struct sockaddr {
sa_family_t sa_family; // 2个字节typedef unsigned short int sa_family_t; //
char sa_data[14]; // 14字节
}
整个结构体大小为16个字节
(程序员每次填充的时候填充自己的结构体,将自己的结构体强转成通用的结构体,因为有机器之间通信,有本地通信。struct sockaddr_in是Internet的结构体,本地通信还会有本地通信所要填充的结构体sockaddr_un,每种协议都有自己需要填充的一个结构体,如果每种协议都有自己的函数接口的话,函数接口太多,没办法记忆,为了做到统一性,填充的填充自己的结构体,传值的时候传struct sockaddr,那么就需要把自己填充的sockaddr_in强制转换成struct sockaddr形式)
******在填充的时候填充struct sockaddr_in ;
struct sockaddr_in {
unsigned short sin_family; //协议IPv4,2个字节
unsigned short sin_port; //端口号 ,2个字节
struct in_addr sin_addr;
struct in_addr {
__be32 s_addr; //IP地址,4个字节
};
/* Pad to size of `struct sockaddr'. */
unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -
sizeof(unsigned short int) - sizeof(struct in_addr)];
//8个字节
};
addrlen:
结构体的大小;
sizeof(serveraddr);
返回值:
-1 失败
3.listen()
int listen(int sockfd, int backlog);
功能: 用于监听,将主动套接字变为被动套接字;
参数:
sockfd: socket()的返回值
backlog:客户端同时(1s)连接服务器的最大个数;
(队列1:保存正在连接)
(队列2,连接上的客户端)
返回值:
失败 -1
4.accept()
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
accept(sockfd,NULL,NULL);
阻塞函数,阻塞等待客户端的连接请求,如果有客户端连接,
则accept()函数返回,返回一个用于通信的套接字文件;
参数:
sockfd
addr: 如果不需要关心具体是哪一个客户端,那么可以填NULL;
addrlen:如果不需要关心具体是哪一个客户端,那么可以填NULL;
返回值:
文件描述符;
acceptfd;
5.recv()
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
功能: 接收数据
参数:
sockfd: acceptfd ;
buf 存放位置
len 大小
flags 一般填0,相当于read()函数
MSG_DONTWAIT
返回值:
< 0 失败出错
==0 表示客户端退出
>0 成功接收的字节个数
6.send()
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
功能:发送数据
参数:
sockfd
buf
len
flags 如果填0,相当于write();
7.close()
【5】UDP
-
无连接,不可靠的传输协议;
UDP编程流程:
服务器端:
-
socket(),返回一个文件描述符,用于通信
对于TCP是先运行服务器,完成结构体的填充sockaddr_in -
bind(); 绑定
服务器需要将自己的IP和端口号与用于通信的文件描述付进行绑定 -
recvfrom(), 接收数据
-
sendto(), 发送数据
可以把sendto替换成send但是需要在前面加connect用于确定send要将数据发给谁 -
close(sockfd);
客户端:
1. socket(), 返回一个文件描述符,用于通信
2. 填充结构体,(目的是告诉程序,数据要发送给谁,及填充结构体的时候填充serveraddr)
3. sendto()
4. recvfrom()
5. close();send recv sendto recvfrom
【6】
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
第5/6参数,明确接收哪个客户端发来的数据;
【7】
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
第5/6参数,明确数据要发送给谁;
- 备注:
- 对于TCP是先运行服务器,客户端才能运行。
- 对于UDP来说,服务器和客户端运行顺序没有先后,因为是无连接,所以服务器和客户端谁先开始,没有关系。
- 一个服务器可以同时连接多个客户端。想知道是哪个客户端登录,可以在服务器代码里面打印IP和端口号。
- UDP,客户端当使用send的时候,上面需要加connect,这个connect不是代表连接的作用,而是指定客户端即将要发送给谁数据。这样就不需要使用sendto而用send就可以。
- 在TCP里面,也可以使用recvfrom和sendto,使用的时候将后面的两个参数都写为NULL就OK。
【1】UNIX/Linux下主要的四种IO模型的特点
-
阻塞式IO :最简单、最常用;效率低
阻塞I/O 模式是最普遍使用的I/O 模式,大部分程序使用的都是阻塞模式的I/O 。
缺省情况下(及系统默认状态),套接字建立后所处于的模式就是阻塞I/O 模式。
前面学习的很多读写函数在调用过程中会发生阻塞。
• 读操作中的read、recv、recvfrom
• 写操作中的write、send
• 其他操作:accept、connect- 读阻塞
以read函数为例:
进程调用read函数从套接字上读取数据,当套接字的接收缓冲区中还没有数据可读,函数read将发生阻塞。
它会一直阻塞下去,等待套接字的接收缓冲区中有数据可读。
经过一段时间后,缓冲区内接收到数据,于是内核便去唤醒该进程,通过read访问这些数据。
如果在进程阻塞过程中,对方发生故障,那这个进程将永远阻塞下去。
- 写阻塞
在写操作时发生阻塞的情况要比读操作少。主要发生在要写入的缓冲区的大小小于要写入的数据量的情况下。
这时,写操作不进行任何拷贝工作,将发生阻塞。
一旦发送缓冲区内有足够的空间,内核将唤醒进程,将数据从用户缓冲区中拷贝到相应的发送数据缓冲区。
UDP不用等待确认,没有实际的发送缓冲区,所以UDP协议中不存在发送缓冲区满的情况,在UDP套接字上执行的写操作永远都不会阻塞。
- 读阻塞
-
非阻塞式IO :可以处理多路IO;需要轮询,浪费CPU资源
• 当我们将一个套接字设置为非阻塞模式,我们相当于告诉了系统内核:“当我请求的I/O 操作不能够马上完成,你想让我的进程进行休眠等待的时候,不要这么做,请马上返回一个错误给我。”
• 当一个应用程序使用了非阻塞模式的套接字,它需要使用一个循环来不停地测试是否一个文件描述符有数据可读(称做polling)。
• 应用程序不停的polling 内核来检查是否I/O操作已经就绪。这将是一个极浪费CPU 资源的操作。
• 这种模式使用中不普遍。例如:
Recv函数最后一个参数写为0,为阻塞,写为MSG_DONTWAIT:表示非阻塞。
非阻塞,循环检测,是否有数据发过来,轮询消耗CPU资源。 -
O多路复用 :服务器可以响应多个客户端发来的数据。
-
信号驱动IO :异步通知模式,需要底层驱动的支持
【2】fcntl
通过该函数设置文件描述符的属性
int fcntl(int fd, int cmd, long arg);
int flag;
flag = fcntl(0, F_GETFL); // 1.获取该文件描述符的原属性
flag |= O_NONBLOCK; //2. 修改对应的位
fcntl(0, F_SETFL, flag); // 3. 写回去
【3】I/O多路复用
应用程序中同时处理多路输入输出流,若采用阻塞模式,将得不到预期的目的;
若采用非阻塞模式,对多个输入进行轮询,但又太浪费CPU时间;
若设置多个进程,分别处理一条数据通路,将新产生进程间的同步与通信问题,使程序变得更加复杂;
比较好的方法是使用I/O多路复用。其基本思想是:
• 先构造一张有关描述符的表,然后调用一个函数。
• 当这些文件描述符中的一个或多个已准备好进行I/O时函数才返回。
• 函数返回时告诉进程那个描述符已就绪,可以进行I/O操作。
select poll epoll
- 其基本流程是:
- 先构造一张有关文件描述符的表(集合、数组);
- 将你关心的文件描述符加入到这个表中;
- 然后调用一个函数。 select / poll
- 当这些文件描述符中的一个或多个已准备好进行I/O操作的时候
该函数才返回(阻塞)。 - 判断是哪一个或哪些文件描述符产生了事件(IO操作);
- 做对应的逻辑处理;
- 注意:
****select函数返回之后,会自动将除了产生事件的文件描述符以外的位全部清空;
这样当你想下一次监听其它的事件的话,都被清空了,就监听不到了,所以写代码的时候,要定义一个临时的集合,所以在调用select之前要把原有的readfds付给临时tempfds。
任务:
我想检测是键盘事件(标准输入 文件描述如为0 ),
还是鼠标事件(文件描述符是/dev/input/mouse2);
【4】select
/* According to POSIX.1-2001 */
#include <sys/select.h>
/* According to earlier standards */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
功能:select用于监测是哪个或哪些文件描述符产生事件;
参数:nfds: 监测的最大文件描述个数
(这里是个数,使用的时候注意,与文件中最后一次打开的文件
描述符所对应的值的关系是什么?)
readfds: 读事件集合; //读(用的多)
writefds: 写事件集合; //NULL表示不关心
exceptfds:异常事件集合;
timeout: 超时检测 1
如果不做超时检测:传 NULL
select返回值: <0 出错
>0 表示有事件产生;
如果设置了超时检测时间:&tv
select返回值:
<0 出错
>0 表示有事件产生;
==0 表示超时时间已到;
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
void FD_CLR(int fd, fd_set *set); //将set集合中的fd清除掉
int FD_ISSET(int fd, fd_set *set); //判断fd是否存在于set集合中
void FD_SET(int fd, fd_set *set); //将fd加入到集合中
void FD_ZERO(fd_set *set); //清空集合
【1】IO多路复用 (并发服务器)
- 想实现服务器处理多个客户端连接请求或数据收发的话,(实现并发)
- 多进程的方式;
- 多线程的方式;
- IO多路复用
- select
• 一个进程最多只能监听1024个文件描述符 (千级别)
• select被唤醒之后需要重新轮询一遍驱动的poll函数,效率比较低(消耗CPU资源);
• select每次会清空表,每次都需要拷贝用户空间的表到内核空间,效率低(一个进程04G,03G是用户态,3G~4G是内核态,拷贝是非常耗时的); - poll
• 优化文件描述符个数的限制;(根据poll函数第一个函数的参数来定,如果监听的事件为1个,则结构体数组的大小为1,如果想监听100个,那么这个结构体数组的大小就为100,由程序员自己来决定)
• poll被唤醒之后需要重新轮询一遍驱动的poll函数,效率比较低
• poll也需要重新构造文件描述符表 - epoll
• 监听的最大的文件描述符没有个数限制(理论上,取决与你自己的系统)
• epoll当有事件产生被唤醒之后,文件描述符主动调用callback(回调函数)函数直接拿到唤醒的文件描述符,不需要轮询,效率高
• epoll不需要重新构造文件描述符表,只需要从用户空间向内核空间拷贝一次数据即可.
- select
int poll
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
功能:这个函数是某些Unix系统提供的用于执行与select()函数同等功能的函数
参数:
`struct pollfd *fds`
关心的文件描述符数组struct pollfd fds[N];
每当调用这个函数之后,系统不会清空这个数组,操作起来比较方便
nfds: nfds_t类型的参数,用于标记数组fds中的结构体元素的总数量;
每当调用这个函数之后,系统不会清空这个数组,操作起来比较方便。
`timeout`: 超时检测
毫秒级的:如果填1000,1秒
如果-1,阻塞
返回值:
>0:返回已就绪的文件描述符数
==0:超时
-1: poll函数调用失败,同时会自动设置全局变量errno;
创建一个结构体数组
struct pollfd fds[2];
将你关心的文件描述符加入到结构体成员中
struct pollfd {
int fd; // 关心的文件描述符;
short events; // 关心的事件,读
short revents; // 如果产生事件,
则会自动填充该成员的值(当文件描述符产生事件之后,
这个函数会自动的把读事件或者写事件填充到这个结构体revents的成员里面.
如果产生读事件,就给revents一个读事件,如果写事件,一样就给revents一个写事件。
)
};
// 键盘
fds[0].fd = 0;
fds[0].events = POLLIN; (POLLIN表示读事件)
//鼠标
fds[1].fd = mouse1_fd;
fds[1].events = POLLIN;
把关心的文件描述符添加到集合当中,把关心的事件也要添加进来。
3. 调用poll函数
如果返回表示有事件产生;
poll(fds,2,1000)
4. 判断具体是哪个文件描述符产生了事件
if(fds[0].revents == POLLIN)
{ ....
}
- .
epoll
• 监听的最大的文件描述符没有个数限制(理论上,取决与你自己的系统)
•epoll
当有事件产生被唤醒之后,文件描述符主动调用callback(回调函数)函数直接拿到唤醒的文件描述符,不需要轮询,效率高
•epoll
不需要重新构造文件描述符表,只需要从用户空间向内核空间拷贝一次数据即可.
注意:
Epoll
处理高并发,百万级,不关心底层怎样实现,只需要会调用就OK。
解释:Epoll
的底层实现如上图:(了解)
• (红黑树,是特殊的二叉树,Epoll怎样能监听很多个呢?首先创建树的根节点,每个根节点都是一个fd以结构体的形式存储(节点里面包含了一些属性,包含callback函数),对于树来说可以随意挂接节点。
• 链表,当某一个文件描述符产生事件后,会自动调用callback函数,通过回调callback函数来找到链表对应的事件(读时间还是写事件),链表为事件链表。
- 3个功能函数:
#include <sys/epoll.h>
-
int epoll_create(int size);
功能:创建红黑树根节点(创建epoll实例)
返回值:成功时返回epoll文件描述符,失败时返回-1。 -
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
功能:控制epoll
属性
epfd:epoll_create
函数的返回句柄。
op
:表示动作类型。有三个宏 来表示:
EPOLL_CTL_ADD
:注册新的fd
到epfd
中
EPOLL_CTL_MOD
:修改已注册fd
的监听事件
EPOLL_CTL_DEL
:从epfd
中删除一个fd
FD
:需要监听的fd。
event
:告诉内核需要监听什么事件
EPOLLIN:
表示对应文件描述符可读
EPOLLOUT:
可写
EPOLLPRI
:有紧急数据可读;
EPOLLERR
:错误;
EPOLLHUP
:被挂断;
EPOLLET
:触发方式,电平触发;
ET
模式:表示状态的变化;
返回值:成功时返回0,失败时返回-1
typedef union epoll_data {
void* ptr;(无效)
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
struct epoll_event {
uint32_t events; / * Epoll事件* /
epoll_data_t data; / *用户数据变量* /
};
//等待事件到来
-
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
功能:等待事件的产生,类似于select的用法
epfd
:句柄;
events
:用来从内核得到事件的集合;
maxevents
:表示每次能处理事件最大个数;
timeout
:超时时间,毫秒,0立即返回,-1阻塞//成功时返回发生事件的文件描述数,失败时返回-1
-