IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> Linux下的网络编程--笔记 -> 正文阅读

[网络协议]Linux下的网络编程--笔记

交换机与路由器:
交换机:用于局域网内网的数据转发
路由器:用于连接局域网和外网

IP地址:

  1. IP地址是Internet中主机的标识
  2. Internet中的主机要与别的机器通信必须具有一个IP地址
  3. IP地址为32位(IPv4)或者128位(IPv6)
  4. 每个数据包都必须携带目的IP地址和源IP地址,路由器依靠此信息为数据包选择路由
  5. 表示形式:常用点分形式,如202.38.64.10,最后都会转换为一个32位的无符号整数。 192.168.1.31 C类 , 点分十进制IP
    C0A8011F 4*8 = 32位

IP分类:

IP地址根据网络号和主机号来分,分为ABC三类及特殊地址DE全0和全1的都保留不用

  1. A类:
    (1.0.0.0-126.0.0.0)(默认子网掩码:255.0.0.0或 0xFF000000)第一个字节为网络号,后三个字节为主机号。该类IP地址的最前面为“0”,所以地址的网络号取值于1~126之间。一般用于大型网络。

  2. B类:
    (128.0.0.0-191.255.0.0)(默认子网掩码:255.255.0.0或0xFFFF0000)前两个字节为网络号,后两个字节为主机号。该类IP地址的最前面为“10”,所以地址的网络号取值于128~191之间。一般用于中等规模网络。

  3. C类:
    (192.0.0.0-223.255.255.0)(子网掩码:255.255.255.0或 0xFFFFFF00)前三个字节为网络号,最后一个字节为主机号。该类IP地址的最前面为“110”,所以地址的网络号取值于192~223之间。一般用于小型网络。

  4. 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相与为主机号

  • 子网掩码:

  1. 子网掩码:是一个32位的整数,作用是将某一个IP划分成网络地址和主机地址;目的是合理的利用IP资源;
  2. 子网掩码长度是和IP地址长度完全一样的32bit的二进制数组成;
  3. 前半部分全1(连续1),后半部分全0;
  • 例如:
    C类地址,同一网段最多可以连接多少个主机???
    192.168.1.0 - 192.168.1.255 => 256
    (最多可以连接主机的个数为254,除网络地址0,广播地址255)
  • 网络概念:
    网络号相同的主机组成一个局域网,局域网可以理解为是一个小型网络。
    若干个小型网络组合在一起可以组合成大型网络 => 以太网
    Inetnet

【1】OSI模型与TCP/IP协议体系结构

网络的体系结构

  1. 网络采用分而治之的方法设计,将网络的功能划分为不同的模块,以分层的形式有机组合在一起。
  2. 每层实现不同的功能,其内部实现方法对外部其他层次来说是透明的。每层向上层提供服务,同时使用下层提供的服务
  3. 网络体系结构即指网络的层次结构和每层所使用协议的集合
  4. 两类非常重要的体系结构:OSI与TCP/IP
    OSI开放系统互联模型
    1. OSI模型是一个理想化的模型,尚未有完整的实现
    2. OSI模型共有七层

OSI模型

OSI模型是最理想的模型

  1. 物理层:传输的是bit流(0与1一样的数据),物理信号,没有格式
  2. 链路层:格式变为帧(把数据分成包,一帧一帧的数据进行发送)
  3. 网络层:路由器中是有算法的,ip,(主机到主机)(路由的转发)
  4. 传输层:端口号,数据传输到具体那个进程程序 (端到端)
  5. 会话层:通信管理,负责建立或者断开通信连接
  6. 表示层:确保一个系统应用层发送的消息可以被另一个系统的应用层读取,编码转换,数据解析,管理数据加密,解密;
  7. 应用层:指定特定应用的协议,文件传输,文件管理,电子邮件等。

【2】 TCP/IP协议族

  1. 应用层 TFTPHTTP,SNMP,FTP,SMTP,DNS,Telnet
  2. 传输层 TCPUDP
  3. 网络层 IPICMP,RIP,OSPF,BGP,IGMP
  4. 网络接口与物理层 SLIP,CSLIP,PPP,ARPRARP,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)

  1. 流式套接字(SOCK_STREAM) TCP

    1. 提供了一个面向连接、可靠的数据传输服务,
    2. 数据无差错、无重复的发送且按发送顺序接收。内设置流量控制,
    3. 避免数据流淹没慢的接收方。数据被看作是字节流,无长度限制。
  2. 数据报套接字(SOCK_DGRAM) UDP

    1. 提供无连接服务。数据包以独立数据包的形式被发送
    2. 不提供无差错保证,数据可能丢失或重复,
    3. 顺序发送,可能乱序接收。
  3. 原始套接字(SOCK_RAW)

    1. 可以对较低层次协议如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编程流程

  • 服务器端:

    1. socket(),创建套接字文件,用于连接 sockfd(有一个属性默认是阻塞)
    2. bind(), 绑定,把socket()函数返回的文件描述符和IP、端口号进行绑定;
    3. listen(), (监听)将socket()返回的文件描述符的属性,由主动变为被动;
    4. accept(), 阻塞函数,阻塞等待客户端的连接请求,如果有客户端连接,则accept()函数返回,返回一个用于通信的套接字文件;
    5. recv(), 接收客户端发来的数据;
    6. send(), 发送数据;
    7. close(), 关闭文件描述符;连接、通信
  • 客户端:

    1. socket(),创建套接字文件,既用于连接,也用于通信;
      完成一个结构体的填充
    2. connect(); 用于发起连接请求;
    3. send(), 发送数据;
    4. recv(), 接收数据;
    5. 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编程流程:
    服务器端:

  1. socket(),返回一个文件描述符,用于通信
    对于TCP是先运行服务器,完成结构体的填充sockaddr_in

  2. bind(); 绑定
    服务器需要将自己的IP和端口号与用于通信的文件描述付进行绑定

  3. recvfrom(), 接收数据

  4. sendto(), 发送数据
    可以把sendto替换成send但是需要在前面加connect用于确定send要将数据发给谁

  5. 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模型的特点

  1. 阻塞式IO :最简单、最常用;效率低
    阻塞I/O 模式是最普遍使用的I/O 模式,大部分程序使用的都是阻塞模式的I/O 。
    缺省情况下(及系统默认状态),套接字建立后所处于的模式就是阻塞I/O 模式。
    前面学习的很多读写函数在调用过程中会发生阻塞。
    ? 读操作中的read、recv、recvfrom
    ? 写操作中的write、send
    ? 其他操作:accept、connect

    • 读阻塞
      以read函数为例:
      进程调用read函数从套接字上读取数据,当套接字的接收缓冲区中还没有数据可读,函数read将发生阻塞。
      它会一直阻塞下去,等待套接字的接收缓冲区中有数据可读。
      经过一段时间后,缓冲区内接收到数据,于是内核便去唤醒该进程,通过read访问这些数据。
      如果在进程阻塞过程中,对方发生故障,那这个进程将永远阻塞下去。
    1. 写阻塞
      在写操作时发生阻塞的情况要比读操作少。主要发生在要写入的缓冲区的大小小于要写入的数据量的情况下。
      这时,写操作不进行任何拷贝工作,将发生阻塞。
      一旦发送缓冲区内有足够的空间,内核将唤醒进程,将数据从用户缓冲区中拷贝到相应的发送数据缓冲区。
      UDP不用等待确认,没有实际的发送缓冲区,所以UDP协议中不存在发送缓冲区满的情况,在UDP套接字上执行的写操作永远都不会阻塞。
  2. 非阻塞式IO :可以处理多路IO;需要轮询,浪费CPU资源
    ? 当我们将一个套接字设置为非阻塞模式,我们相当于告诉了系统内核:“当我请求的I/O 操作不能够马上完成,你想让我的进程进行休眠等待的时候,不要这么做,请马上返回一个错误给我。”
    ? 当一个应用程序使用了非阻塞模式的套接字,它需要使用一个循环来不停地测试是否一个文件描述符有数据可读(称做polling)。
    ? 应用程序不停的polling 内核来检查是否I/O操作已经就绪。这将是一个极浪费CPU 资源的操作。
    ? 这种模式使用中不普遍。

    例如:
    Recv函数最后一个参数写为0,为阻塞,写为MSG_DONTWAIT:表示非阻塞。
    非阻塞,循环检测,是否有数据发过来,轮询消耗CPU资源。

  3. O多路复用 :服务器可以响应多个客户端发来的数据。

  4. 信号驱动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

  • 其基本流程是
  1. 先构造一张有关文件描述符的表(集合、数组);
  2. 将你关心的文件描述符加入到这个表中;
  3. 然后调用一个函数。 select / poll
  4. 当这些文件描述符中的一个或多个已准备好进行I/O操作的时候
    该函数才返回(阻塞)。
  5. 判断是哪一个或哪些文件描述符产生了事件(IO操作);
  6. 做对应的逻辑处理;
  • 注意:
    ****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多路复用 (并发服务器)

  • 想实现服务器处理多个客户端连接请求或数据收发的话,(实现并发
  1. 多进程的方式;
  2. 多线程的方式;
  3. IO多路复用
    1. select
      ? 一个进程最多只能监听1024个文件描述符 (千级别)
      ? select被唤醒之后需要重新轮询一遍驱动的poll函数,效率比较低(消耗CPU资源);
      ? select每次会清空表,每次都需要拷贝用户空间的表到内核空间,效率低(一个进程04G,03G是用户态,3G~4G是内核态,拷贝是非常耗时的);
    2. poll
      ? 优化文件描述符个数的限制;(根据poll函数第一个函数的参数来定,如果监听的事件为1个,则结构体数组的大小为1,如果想监听100个,那么这个结构体数组的大小就为100,由程序员自己来决定)
      ? poll被唤醒之后需要重新轮询一遍驱动的poll函数,效率比较低
      ? poll不需要重新构造文件描述符表,只需要从用户空间向内核空间拷贝一次数据即可
    3. epoll
      ? 监听的最大的文件描述符没有个数限制(理论上,取决与你自己的系统)
      ? epoll当有事件产生被唤醒之后,文件描述符主动调用callback(回调函数)函数直接拿到唤醒的文件描述符,不需要轮询,效率高
      ? epoll不需要重新构造文件描述符表,只需要从用户空间向内核空间拷贝一次数据即可.
  • 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`: 超时检测
		毫秒级的:如果填10001秒
		 如果-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>
    1. int epoll_create(int size);
      功能:创建红黑树根节点(创建epoll实例)
      返回值:成功时返回epoll文件描述符,失败时返回-1。

    2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
      功能:控制epoll属性
      epfd:epoll_create函数的返回句柄。
      op:表示动作类型。有三个宏 来表示:
      EPOLL_CTL_ADD:注册新的fdepfd
      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; / *用户数据变量* /
      };
      //等待事件到来

    3. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
      功能:等待事件的产生,类似于select的用法
      epfd:句柄;
      events:用来从内核得到事件的集合;
      maxevents:表示每次能处理事件最大个数;
      timeout:超时时间,毫秒,0立即返回,-1阻塞

      //成功时返回发生事件的文件描述数,失败时返回-1
      
  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-08-28 09:42:41  更:2021-08-28 09:44:23 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 21:47:31-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码