linux并发服务器 —— IO多路复用(八)

半关闭、端口复用

半关闭只能实现数据单方向的传输;当TCP 接中A向 B 发送 FIN 请求关闭,另一端 B 回应ACK 之后 (A 端进入 FIN_WAIT_2 状态),并没有立即发送 FIN 给 A,A 方处于半连接状态 (半开关),此时 A 可以接收 B 发送的数据,但是 A 已经不能再向 B 发送数据

close不会影响到其他进程,shutdown会影响到其他进程;

网络信息相关的命令

netstat

        -a 所有的Socket

        -p 正在所用socket的程序名称

        -n 直接使用IP地址,不通过域名服务器

端口复用

1. 防止服务器重启时之前绑定的端口还没释放

2. 程序突然退出而系统没有释放端口

IO多路复用简介

I/O多路复用使程序可以同时监听多个文件描述符,提高程序性能;select/poll/epoll

阻塞等待:不占用CPU宝贵时间;但同一时刻只能处理一个操作,效率低。

非阻塞,忙轮询:提高了程序执行效率;但会占用更多的CPU资源。

select/poll:委托内核进行检测,但仍需要进行遍历

epoll:同样委托内核,但无需进行遍历

select

主旨思想:

1. 构造关于文件描述符的列表,将要监听的文件描述符添加到表中

2. 调用系统函数,监听该列表中的文件描述符,知道描述符中的一个/多个进行了I/O操作,函数才返回(该函数是阻塞的,且该函数对于文件描述符的检测是由内核完成的)

3. 返回时,告诉进程有多少描述符要进行I/O操作

返回值: 失败 - -1,成功 - 检测到的描述符个数

#include <iostream>
#include <cstdio>
#include <arpa/inet.h>
#include <cstdlib>
#include <string.h>
#include <sys/select.h>
#include  <unistd.h>using namespace std;int main(){// 创建socketint lfd = socket(PF_INET , SOCK_STREAM , 0);struct sockaddr_in saddr;saddr.sin_port = htons(9999);saddr.sin_family = AF_INET;saddr.sin_addr.s_addr = INADDR_ANY;// 绑定bind(lfd , (struct sockaddr*)&saddr , sizeof(saddr));// 监听listen(lfd , 8);fd_set rdset , tmp;FD_ZERO(&rdset);FD_SET(lfd , &rdset);int maxfd = lfd+1;while(1){tmp = rdset;// 调用select 系统检测int ret = select(maxfd+1 , &tmp , NULL , NULL , NULL);if(ret == -1){perror("select");exit(-1);}else if(ret == 0){continue;}else{// 检测到了文件描述符的数据发生了改变if(FD_ISSET(lfd , &tmp)){// 有客户端连接进来struct sockaddr_in caddr;socklen_t len = sizeof(caddr);int cfd = accept(lfd , (struct sockaddr*)&caddr , &len);// 添加进去FD_SET(cfd , &rdset);maxfd = maxfd>cfd?maxfd:cfd+1;}for(int i = lfd+1 ; i <= maxfd ; i++){if(FD_ISSET(i , &tmp)){// 说明客户端发来了数据char buf[1024];int len = read(i , buf , sizeof(buf));if(len == -1){perror("read");exit(-1);}else if(len == 0){cout<<"client close..."<<endl;close(i);FD_CLR(i,&rdset);}else{cout<<"发来了数据:"<<buf<<endl;write(i , buf , strlen(buf)+1);}}}}}close(lfd);return 0;
}

poll

#include <iostream>
#include <cstdio>
#include <arpa/inet.h>
#include <cstdlib>
#include <string.h>
#include <poll.h>
#include  <unistd.h>using namespace std;int main(){// 创建socketint lfd = socket(PF_INET , SOCK_STREAM , 0);struct sockaddr_in saddr;saddr.sin_port = htons(9999);saddr.sin_family = AF_INET;saddr.sin_addr.s_addr = INADDR_ANY;// 绑定bind(lfd , (struct sockaddr*)&saddr , sizeof(saddr));// 监听listen(lfd , 8);// 初始化检测文件描述符数组struct pollfd fds[1024];for(int i = 0 ; i<1024 ; i++){fds[i].fd = -1;fds[i].events = POLLIN;}fds[0].fd = lfd;int nfds = 0;while(1){// poll 系统检测int ret = poll(fds , nfds+1 , -1);if(ret == -1){perror("poll");exit(-1);}else if(ret == 0){continue;}else{// 检测到了文件描述符的数据发生了改变if(fds[0].revents & POLLIN){// 有客户端连接进来struct sockaddr_in caddr;socklen_t len = sizeof(caddr);int cfd = accept(lfd , (struct sockaddr*)&caddr , &len);// 添加进去for(int i = 1 ; i < 1024 ; i++){if(fds[i].fd == -1){fds[i].fd = cfd;fds[i].events = POLLIN;break;}}nfds = nfds>cfd?nfds:cfd;}for(int i = 1 ; i <= nfds ; i++){if(fds[i].revents & POLLIN){// 说明客户端发来了数据char buf[1024];int len = read(fds[i].fd , buf , sizeof(buf));if(len == -1){perror("read");exit(-1);}else if(len == 0){cout<<"client close..."<<endl;close(fds[i].fd);fds[i].fd = -1;}else{cout<<"发来了数据:"<<buf<<endl;write(fds[i].fd , buf , strlen(buf)+1);}}}}}close(lfd);return 0;
}

epoll

内核,红黑树记录要检测的文件描述符,避免了用户态到内核态的数据拷贝开销;

内核,双链表存放数据改变的文件描述符

#include <iostream>
#include <cstdio>
#include <arpa/inet.h>
#include <cstdlib>
#include <string.h>
#include <sys/epoll.h>
#include  <unistd.h>using namespace std;int main(){// 创建socketint lfd = socket(PF_INET , SOCK_STREAM , 0);struct sockaddr_in saddr;saddr.sin_port = htons(9999);saddr.sin_family = AF_INET;saddr.sin_addr.s_addr = INADDR_ANY;// 绑定bind(lfd , (struct sockaddr*)&saddr , sizeof(saddr));// 监听listen(lfd , 8);// 创建epoll实例int epfd = epoll_create(100);// 添加监听文件描述符struct epoll_event epev;epev.events = EPOLLIN;epev.data.fd = lfd;epoll_ctl(epfd , EPOLL_CTL_ADD , lfd , &epev);struct epoll_event epevs[1024];while(1){int ret = epoll_wait(epfd , epevs , 1024 , -1);if(ret == -1){perror("epoll");exit(-1);}cout<<ret<<"个发生了改变"<<endl;for(int i = 0 ; i<ret ; i++){int curfd = epevs[i].data.fd;if(curfd == lfd){// 监听的文件描述符有客户端连接struct sockaddr_in caddr;socklen_t len = sizeof(caddr);int cfd = accept(lfd , (struct sockaddr*)&caddr , &len);epev.events = EPOLLIN;epev.data.fd = cfd;epoll_ctl(epfd , EPOLL_CTL_ADD , cfd , &epev);}else{// 有数据到达char buf[1024];int len = read(curfd , buf , sizeof(buf));if(len == -1){perror("read");exit(-1);}else if(len == 0){cout<<"client close..."<<endl;epoll_ctl(epfd , EPOLL_CTL_DEL , curfd , NULL);close(curfd);}else{cout<<"发来了数据:"<<buf<<endl;write(curfd , buf , strlen(buf)+1);}}}}close(lfd);close(epfd);return 0;
}

epoll的两种工作模式

LT模式 - 水平触发

默认的工作模式,支持block/no-block;,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的 fd 进行IO操作。如果你不作任何操作,内核还是会继续通知你

#include <iostream>
#include <cstdio>
#include <arpa/inet.h>
#include <cstdlib>
#include <string.h>
#include <sys/epoll.h>
#include  <unistd.h>using namespace std;int main(){// 创建socketint lfd = socket(PF_INET , SOCK_STREAM , 0);struct sockaddr_in saddr;saddr.sin_port = htons(9999);saddr.sin_family = AF_INET;saddr.sin_addr.s_addr = INADDR_ANY;// 绑定bind(lfd , (struct sockaddr*)&saddr , sizeof(saddr));// 监听listen(lfd , 8);// 创建epoll实例int epfd = epoll_create(100);// 添加监听文件描述符struct epoll_event epev;epev.events = EPOLLIN;epev.data.fd = lfd;epoll_ctl(epfd , EPOLL_CTL_ADD , lfd , &epev);struct epoll_event epevs[1024];while(1){int ret = epoll_wait(epfd , epevs , 1024 , -1);if(ret == -1){perror("epoll");exit(-1);}cout<<ret<<"个发生了改变"<<endl;for(int i = 0 ; i<ret ; i++){int curfd = epevs[i].data.fd;if(curfd == lfd){// 监听的文件描述符有客户端连接struct sockaddr_in caddr;socklen_t len = sizeof(caddr);int cfd = accept(lfd , (struct sockaddr*)&caddr , &len);epev.events = EPOLLIN;epev.data.fd = cfd;epoll_ctl(epfd , EPOLL_CTL_ADD , cfd , &epev);}else{// 有数据到达char buf[5];int len = read(curfd , buf , sizeof(buf));if(len == -1){perror("read");exit(-1);}else if(len == 0){cout<<"client close..."<<endl;epoll_ctl(epfd , EPOLL_CTL_DEL , curfd , NULL);close(curfd);}else{cout<<"发来了数据:"<<buf<<endl;write(curfd , buf , strlen(buf)+1);}}}}close(lfd);close(epfd);return 0;
}

ET模式 - 边沿触发

告诉工作方式,只支持no-block,在这种模式下,当描述符从未就绪变为就绪时,内核通过epol告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了。但是请注意如果一直不对这个 fd 作IO操作,内核不会发送更多的通知 (only once) 。但是缓冲区中的数据不会丢失

ET模式效率比LT模式高,ET模式下必须使用非阻塞套接口,避免由于一个描述符的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死;

要设置边沿触发

1. 需要在epoll_event中设置EPOLLET

2. 

UDP通信实现 - 无需多进程/多线程的并发实现

// server
#include <iostream>
#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
using namespace std;int main(){// 创建socketint fd = socket(PF_INET , SOCK_DGRAM , 0);if(fd == -1){perror("socket");exit(-1);}// 绑定struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(9999);addr.sin_addr.s_addr = INADDR_ANY;int ret = bind(fd ,(struct sockaddr*) &addr , sizeof(addr));if(ret == -1){perror("bind");exit(-1);}// 通信while(1){// 接收数据char buf[128];char ip[16];struct sockaddr_in caddr;socklen_t len = sizeof(caddr);int num = recvfrom(fd , buf , sizeof(buf) , 0 , (struct sockaddr*)&caddr , &len);string s1 = "IP: ";s1 += inet_ntop(AF_INET , &caddr.sin_addr.s_addr , ip , sizeof(ip));string s2 = "Port: ";s2 += ntohs(caddr.sin_port);cout<<s1<<" "<<s2<<endl;cout<<"rcv data: "<<buf<<endl;sendto(fd , buf , strlen(buf)+1 , 0 , (struct sockaddr*)&caddr , len);}close(fd);return 0;
}
// client
#include <iostream>
#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
using namespace std;int main(){// 创建socketint fd = socket(PF_INET , SOCK_DGRAM , 0);if(fd == -1){perror("socket");exit(-1);}struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(9999);inet_pton(AF_INET , "127.0.0.1" , &addr.sin_addr.s_addr);// 通信int num = 0;socklen_t len = sizeof(addr);while(1){char buf[128];sprintf(buf , "hello 647 %d" , num++);sendto(fd , buf , strlen(buf)+1 , 0 , (struct sockaddr*)&addr , len);// 接收数据int num = recvfrom(fd , buf , sizeof(buf) , 0 , NULL , NULL);cout<<"rcv data: "<<buf<<endl;sleep(1);}close(fd);return 0;
}

广播和组播 - 只能使用UDP

广播 - 向子网中多台计算机发送消息,每个广播消息都包含一个特殊的IP地址,这个IP中子网内主机标志部分的二进制全部为1;

1. 只能在局域网中使用

2. 客户端需要绑定服务器广播使用的端口,才能接收到广播消息

组播 (多播) - 标识一组IP接口,是在单播和广播之间的一种折中方案,多播数据包只由对其感兴趣的接口接收;

1. 组播可以用于局域网和广域网

2. 客户端需要加入多播组才能接收到

本地套接字通信

作用:用于进程间的通信;实现流程和网络套接字类似,一般采用TCP的通信流程

服务器端1. 创建监听的套接字int lfd = socket(AF_UNIX/AF_LOCAL , SOCK_STREAM , 0);2. 监听套接字绑定本地的套接字文件struct sockaddr_un addr;bind(lfd, addr, len); // 绑定成功后sun_path中的套接字文件会自动生成3. 监听4. 等待并接受客户端请求5. 通信6. 关闭连接
客户端1. 创建通信的套接字2. 绑定本地IP端口3. 连接服务器4. 通信5. 关闭连接
// 服务端
#include <iostream>
#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/un.h>
using namespace std;int main(){unlink("server.sock");// 创建监听套接字int lfd = socket(AF_LOCAL , SOCK_STREAM , 0);if(lfd == -1){perror("socket");exit(-1);}// 绑定本地套接字文件struct sockaddr_un addr;addr.sun_family = AF_LOCAL;strcpy(addr.sun_path , "server.sock");int ret = bind(lfd , (struct sockaddr*)&addr , sizeof(addr));if(ret == -1){perror("bind");exit(-1);}// 监听ret = listen(lfd , 100);if(ret == -1){perror("listen");exit(-1);}// 等待客户端连接struct sockaddr_un caddr;socklen_t len = sizeof(caddr);int cfd = accept(lfd , (struct sockaddr*)&caddr , &len);if(cfd == -1){perror("accept");exit(-1);}cout<<"客户端文件:"<<caddr.sun_path<<endl;// 通信while(1){char buf[128];int len = recv(cfd , buf , sizeof(buf) , 0);if(len ==  -1){perror("recv");exit(-1);}else if(len == 0){cout<<"client close..."<<endl;break;}else{cout<<"recv data: "<<buf<<endl;send(cfd , buf , len , 0);}}close(cfd);close(lfd);return 0;
}
// 客户端
#include <iostream>
#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/un.h>
using namespace std;int main(){unlink("client.sock");// 创建监听套接字int cfd = socket(AF_LOCAL , SOCK_STREAM , 0);if(cfd == -1){perror("socket");exit(-1);}// 绑定本地套接字文件struct sockaddr_un addr;addr.sun_family = AF_LOCAL;strcpy(addr.sun_path , "client.sock");int ret = bind(cfd , (struct sockaddr*)&addr , sizeof(addr));if(ret == -1){perror("bind");exit(-1);}// 连接服务器struct sockaddr_un saddr;saddr.sun_family = AF_LOCAL;strcpy(saddr.sun_path , "server.sock");ret = connect(cfd , (struct sockaddr*)&saddr , sizeof(saddr));if(ret == -1){perror("connect");exit(-1);}// 通信int num = 0;while(1){char buf[128];sprintf(buf , "hello 647 %d\n" , num++);send(cfd , buf , strlen(buf)+1 , 0);int len = recv(cfd , buf , sizeof(buf) , 0);if(len ==  -1){perror("recv");exit(-1);}else if(len == 0){cout<<"Server close..."<<endl;break;}else{cout<<"recv data: "<<buf<<endl;}sleep(1);}close(cfd);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/69478.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

你知道用Woof创建的Linux吗?

Quirky 8.2 已发布&#xff0c;它是 Puppy Linux 的姊妹项目&#xff0c;是用一份叫 Woof 的定制工具创建的 Linux 发行。 新版本 Quirky 8.2 运行在 64 位的 x86 计算机上&#xff0c;主要提供了针对以前的 8.x 版本的增量改进。 Quirky Linux 8.2 x86_64 的代号是Xerus&…

Python爬虫:下载小红书无水印图片、视频

该代码只提供学习使用&#xff0c;该项目是基于https://github.com/JoeanAmier/XHS_Downloader的小改动 1.下载项目 git clone https://github.com/zhouayi/XHS_Downloader.git2.找到需要下载的文章的ID 写入main.py中 3.下载 python main.py最近很火的莲花楼为例<嘿嘿…

LeetCode 周赛上分之旅 #44 同余前缀和问题与经典倍增 LCA 算法

⭐️ 本文已收录到 AndroidFamily&#xff0c;技术和职场问题&#xff0c;请关注公众号 [彭旭锐] 和 BaguTree Pro 知识星球提问。 学习数据结构与算法的关键在于掌握问题背后的算法思维框架&#xff0c;你的思考越抽象&#xff0c;它能覆盖的问题域就越广&#xff0c;理解难度…

成都瀚网科技有限公司:抖店怎么开通直播?

随着互联网和移动支付的快速发展&#xff0c;越来越多的人选择开设自己的抖音商店。抖音作为国内最受欢迎的短视频平台之一&#xff0c;拥有庞大的用户基础&#xff0c;成为众多创业者青睐的平台。那么&#xff0c;如何经营自己的抖音店铺呢&#xff1f;下面将从几个方面为您介…

Si24R2F+畜牧 耳标测体温开发资料

Si24R2F是针对IOT应用领域推出的新款超低功耗2.4G内置NVM单发射芯片。广泛应用于2.4G有源活体动物耳标&#xff0c;带实时测温计步功能。相较于Si24R2E&#xff0c;Si24R2F增加了温度监控、自动唤醒间隔功能&#xff1b;发射功率由7dBm增加到12dBm&#xff0c;距离更远&#xf…

k8s 搭建基于session模式的flink集群

1.flink集群搭建 不废话直接上代码&#xff0c;都是基于官网的&#xff0c;在此记录一下 Kubernetes | Apache Flink flink-configuration-configmap.yaml apiVersion: v1 kind: ConfigMap metadata:name: flink-configlabels:app: flink data:flink-conf.yaml: |jobmanager…

postgresql-条件表达式

postgresql-条件表达式 简单Case表达式搜索Case表达式缩写函数总结 简单Case表达式 select e.first_name , e.last_name , e.department_id , case e.department_id when 90 then 管理when 60 then 开发else 其他end as "部门" from cps.public.employees e ;-- 统…

Vue笔记

第一章&#xff1a;Vue环境搭建 1.搭建Vue环境 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><!-- 1.引入Vue.js--><script src"1.vue.js"></scr…

软件生命周期及流程

软件生命周期&#xff1a; 软件生命周期(SDLC&#xff0c;Systems Development Life Cycle)是软件开始研制到最终被废弃不用所经历的各个阶段. 需求分析阶段--输出需求规格说明书&#xff08;原型图&#xff09; 测试介入的晚--回溯成本高 敏捷开发模型&#xff1a; 从1990年…

STM32CUBEMX_创建时间片轮询架构的软件框架

STM32CUBEMX_创建时间片轮询架构的软件框架 说明&#xff1a; 1、这种架构避免在更新STM32CUBEMX配置后把用户代码清除掉 2、利用这种时间片的架构可以使得代码架构清晰易于维护 创建步骤&#xff1a; 1、使用STM32CUBEMX创建基础工程 2、新建用户代码目录 3、构建基础的代码框…

OpenLdap +PhpLdapAdmin + Grafana docker-compose部署安装

目录 一、OpenLdap介绍 二、PhpLdapAdmin介绍 三、使用docker-compose进行安装 1. docker-compose.yml 2. grafana配置文件 3. provisioning 四、安装openldap、phpldapadmin、grafana 五、配置OpenLDAP 1. 登陆PhpLdapAdmin web管理 2. 需要注意的细节 内容介绍参考…

Java作业3

1.下面代码的运行结果是&#xff08;C&#xff09; public static void main(String[] args){String s;System.out.println("s"s);}A.代码编程成功&#xff0c;并输出”s” B.代码编译成功&#xff0c;并输出”snull” C.由于String s没有初始化&#xff0c;代码不…

python基础运用例子

python基础运用例子 1、⼀⾏代码交换 a , b &#xff1a;a, b b, a2、⼀⾏代码反转列表 l[::-1]3、合并两个字典 res {**dict1, **dict2}**操作符合并两个字典for循环合并dict(a, **b) 的方式dict(a.items() b.items()) 的方式dict.update(other_dict) 的方式 4、⼀⾏代码列…

【多尺度双域引导网络:Pan-sharpening】

Multi-Scale Dual-Domain Guidance Network for Pan-sharpening &#xff08;用于泛锐化的多尺度双域引导网络&#xff09; 全色锐化的目标是在纹理丰富的全色图像的指导下&#xff0c;通过超分辨低空间分辨率多光谱图像&#xff08;LRMS&#xff09;的对应物产生高空间分辨率…

语音特征提取与预处理

导入相关包 import librosa import librosa.display import soundfile as sf import numpy as np import matplotlib.pyplot as plt from playsound import playsound 语音读取与显示 file_path test1.wav data, fs librosa.load(file_path, srNone, monoTrue) librosa.d…

数学建模--整数规划匈牙利算法的Python实现

目录 1.算法流程简介 2.算法核心代码 3.算法效果展示 1.算法流程简介 #整数规划模型--匈牙利算法求解 """ 整数规划模型及概念&#xff1a;规划问题的数学模型一般由三个因素构成 决策变量 目标函数 约束条件&#xff1b;线性规划即以线性函数为目标函数&a…

2024腾讯校招后端面试真题汇总及其解答(三)

21【算法题】反转链表 题目: 给定单链表的头节点 head ,请反转链表,并返回反转后的链表的头节点。 示例 1: 输入:head = [1,2,3,4,5] 输出:[5,4,3,2,1]示例 2: 输入:head = [1,2] 输出:[2,1]示例 3: 输入:head = [] 输出:[]提示: 链表中节点的数目范围是 [0, 5…

GPT转换工具:轻松将MBR转换为GPT磁盘

为什么需要将MBR转换为GPT&#xff1f; 众所周知&#xff0c;Windows 11已经发布很长时间了。在此期间&#xff0c;许多老用户已经从Windows 10升级到Windows 11。但有些用户仍在运行Windows 10。对于那些想要升级到Win 11的用户来说&#xff0c;他们可能不确定Win 11应该使…

LeetCode 热题 100——找到字符串中所有字母异位词(滑动窗口)

题目链接 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 题目解析 该题目的意思简而言之就是说&#xff0c;从s字符串中寻找与p字符串含有相同字符(次数和种类均相同)的子串&#xff0c;并且将他们的首字符下标集合进数组中进行返回。 滑动窗口解…

大数据Flink(七十三):SQL的滚动窗口(TUMBLE)

文章目录 SQL的滚动窗口(TUMBLE) SQL的滚动窗口(TUMBLE) 滚动窗口定义:滚动窗口将每个元素指定给指定窗口大小的窗口。滚动窗口具有固定大小,且不重叠。例如,指定一个大小为 5 分钟的滚动窗口。在这种情况下,Flink 将每隔 5 分钟开启一个新的窗口,其中每一条数都会划…