Socket编程-IO模型

1、首先IO模型的内容。

        感觉可以简单理解为:我们写代码时,在基础的  IO 操作上做了一些其他的策略,根据策略的不同,一般有阻塞IO和非阻塞IO

1、阻塞IO

        就是在操作的时候,比如网络通信中,某一线程使用下面这三个函数接收数据的时候,都有flags参数,就可以设定成非阻塞 MSG_DONTWAIT,这样就不会将本线程的运行卡在这个函数这里,可以进行其他的操作了。

 ssize_t recv(int sockfd, void *buf, size_t len, int flags);ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

       这也有问题,因为本线程既然调用这个函数了,就是用它获取信息的,现在设定成非阻塞后。没有信息直接就做别的事情,怎么去继续读取信息呢?

        这就只能来回的循环这一步,来进行查询信息是否已经从内核态读取到了用户态,这样次数多了,也是浪费CPU资源。所以用select和epoll等来解决这个问题。

        (这个IO的话,我理解上它是我们调用函数比如recv,read这样的系统调用函数时,会对内核进行访问读取数据,当有数据准备好时,从内核空间拷贝到用户空间或者说会直接读取到我们在程序中自己设定的存储空间中。

简单说就是这样的过程   : 从    磁盘---》内核空间 -----》用户空间(程序存储区)

比如  read( int fd, void *buf  , size_t count);    //buf  就是我们设定的程序存储空间。fd的话,就是磁盘中的文件对应的文件描述符 / 或者网络通信链接的connfd等

        2、怎么实现线程安全

1、首先,线程安全这个问题(基本上就是指对共享资源的访问是安全的),只要提到就一定是发生在多线程的情况下,并且这个问题是针对多个线程访问同一共享资源的情况(线程共享堆区,静态变量,全局变量,还有文件系统的东西等), 当多个线程对同一资源进行“写”操作的时候,或者说对同一资源进行改动的时候,会不会出现问题。(而我们使用多线程就是为了能够执行更多的任务,做更多的功能,提高效率,且线程之间一定需要有隔离,不能乱掺和其他线程的工作,

        比如我们其中一个线程对配置文件进行读取操作,读取后使用配置信息进行工作。

但是,在我们读取完之前,另外一个线程对配置文件进行了更新迭代!!!,这种情况下,其他正在读取配置文件的线程,可能就读取不到需要的信息,也就不能进行接下来的工作,,这多线程不就崩了(其他能解决的方法很多,比如循环读取验证配置信息,但是这样不是更加浪费时间资源在这个配置文件的读取校验上)。

       互斥锁解决线程安全的问题: 

        使用互斥锁就是挺好使的一个机制(使用互斥锁对共享资源进行上锁,或者说,更细节一些的,我们在每一个可能会导致多线程访问共享资源的点上都使用互斥锁去给共享资源上个锁,这样就安全了,一定记得解锁

接Socket网络编程-池化的思路-CSDN博客

        互斥锁的使用信息上篇内容写过,需要注意的一个点就是,当上锁后,这个锁到解锁的代码区域里,如果访问到贡献资源,就是自己单独访问,其他线程都需要等待本线程访问完释放这个共享资源才行。

        其他的线程如果访问不到,运行到pthread_cond_wait时,这个函数本身就会自动给这个线程解锁,被条件变量阻塞在这里,等到被唤醒后,再次去进行资源访问,(如果还不行可能就还需要阻塞在这里,等下一次唤醒),不过一般这时候就已经可以访问贡献资源了。

2、对栈区和寄存器的访问是独有的。

2、Reactor模型

C++实现算法的一些巧妙点-CSDN博客

1、Socket函数的具体应用过程

这里以读事件做示例,核心功能就是使用服务器实现读操作,使用 epoll_wait  监听socket等就绪事件(一般就是文件描述符,sockfd关联的客户端链接)

        (1)、其中比较特别的就是,我们使用线程时,进行任务处理的就是pthread_create 的回调函数;这里我们就需要注册这个函数。

        还需要注册监听的事件(将相应的事件加到epollfd中,epoll_ctl())。

(注册,简单说提前设定好函数功能,和监听的事件,当代码运行到相应的位置时,会去调用或者处理这些事件)下面文章也有介绍Linux 信号和信号量小记-CSDN博客

         (2)另外呢就是accept这个函数,之前也写过;这个函数只从listen 管理的两个队列(全链接和半链接队列)中,从全连接队列取出一个已经链接的客户端(fd),生成一个新的文件描述符,具体如下。

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

        accept()系统调用:

        用于基于连接的套接字类型(SOCK_STREAM, SOCK_SEQPACKET)。它从监听套接字sockfd的阻塞连接队列中提取第一个连接请求,创建一个新的连接套接字,并返回一个引用该套接字的新文件描述符。新创建的套接字未处于监听状态。原始套接字sockfd不受此调用的影响。(从sockfd关联的的队列中,提取第一个链接请求  再创建新的套接字,这个新的也需要我们设置添加进监听集合中,这个新的套接字就代表原来的链接请求。)

        select ()函数:

select()允许程序监视多个文件描述符,直到一个或多个文件描述符“准备好”进行某类I/O操作。如果可以执行相应的I/O操作(例如,read(2)或足够小的write(2))而不阻塞,则认为文件描述符已准备就绪(到底什么时候算就绪,后面还得研究清楚

        这个应该是和内核空间(链接的文件描述符的读写缓冲区的设定有关,达到条件可读,可写)接Socket网络编程-池化的思路-CSDN博客这篇里面有写,每个sockfd创建的时候都有读写缓冲区。(类似我们写一个char * buf[  1024]   ,可以设定字节的大小,比如内容超过50字节可读,内容小于100字节,可写入等)----> 这个具体问题,后面找找源码看看。

 2、select示例程序

        这是一个服务器简单代码,只是将一个客户端发过来的消息,转发给其他的客户端(类似我们的消息群发)

int main(int argc, char *argv[]){// ./serverARGS_CHECK(argc,3);int sockFd = socket(AF_INET,SOCK_STREAM,0);ERROR_CHECK(sockFd,-1,"socket");int optval = 1;int ret = setsockopt(sockFd,SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(int));ERROR_CHECK(ret,-1,"setsockopt");struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(atoi(argv[2]));addr.sin_addr.s_addr = inet_addr(argv[1]);ret = bind(sockFd,(struct sockaddr *)&addr,sizeof(addr));ERROR_CHECK(ret,-1,"bind");ret = listen(sockFd,10);ERROR_CHECK(ret,-1,"listen");fd_set rdset;//单纯地去保存就绪的fdfd_set monitorSet;//使用一个单独的监听集合FD_ZERO(&monitorSet);FD_SET(sockFd,&monitorSet);char buf[4096] = {0};int netFdArr[10] = {0};int curConn = 0;//服务端不再监听标准输入 //服务端的功能:处理新的客户端 监听sockFd//处理客户端发送的消息的转发 
//这里使用monitor做所有文件描述符的监听集合,使用rdset 只监听已经就绪的链接fd(就绪的标准)
//就是accept从listen的已连接队列中取到的链接fd,然后将其加入select监听中,看看是可读还是可写while(1){memcpy(&rdset,&monitorSet,sizeof(fd_set));select(20,&rdset,NULL,NULL,NULL);
//select  能够监视我们需要监视的文件描述符的变化情况——可读写或是异常。if(FD_ISSET(sockFd,&rdset)){netFdArr[curConn]= accept(sockFd,NULL,NULL); //此时从listen的全链接队列取出一个,生成新的netfdAddr,这时这个链接的fd还不知道有没有就绪ERROR_CHECK(netFdArr[curConn],-1,"accept");FD_SET(netFdArr[curConn],&monitorSet);  //这里加入总的监听集合中,因为rdset,是从总的监听集合的来的,所以需要先加进去,再转移给rdset,使用select再进行监听。printf("new connect is accepted!,curConn = %d\n", curConn);++curConn; }for(int i = 0;i < curConn; ++i){if(FD_ISSET(netFdArr[i],&rdset)){   //这个循环只是查找已经读写就绪的fd,之后循环将数据转发给除了发送方以外的客户端bzero(buf,sizeof(buf));recv(netFdArr[i],buf,sizeof(buf),0);//这里从当前的链接客户端 读取信息for(int j = 0; j < curConn; ++j){     //这里循环查询,将信息转发给除了自己以外的客户端if(j == i){continue;}send(netFdArr[j],buf,strlen(buf),0);}}}}close(sockFd);
}

3、模型代码使用epoll

EPOLLIN  : 可读

EPOLLOUT:可写

过程就是 

                使用epoll_ctl将服务端关联的sockfd 添加监听的时候,就相当于将所有的链接请求加入到了监听中。而epoll_wait 就是返回这些被监听的链接中,已经就绪的链接(是一个链表结构);这些链接fd也可以使用,就是需要判断是不是sockfd链接,如果是别的文件描述符,就出错了。

             这是一个简单的测试案例,

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <error.h>
#include <sys/epoll.h>#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>#include <iostream>
#include <string>using std::string;
using std::cin;
using std::cout;
using std::endl;void test()
{// 1、创建监听服务器的套接字int listenfd = socket(AF_INET,SOCK_STREAM, 0);if(listenfd==-1){perror("socket");return;}struct sockaddr_in serveraddr;memset(&serveraddr, 0, sizeof(serveraddr));serveraddr.sin_family = AF_INET;serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");serveraddr.sin_port = htons(8888);socklen_t length = sizeof(serveraddr);//端口重用int opt = 1;int setAddResusetRet = setsockopt(listenfd, SOL_SOCKET,SO_REUSEADDR, &opt, sizeof(opt));if(-1 == setAddResusetRet){perror("set sockoptADDR is error");return ;}int setPortReuseRet = setsockopt(listenfd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));if(-1 == setPortReuseRet){perror("set sockoptPORT is error");return ;}//2、服务器绑定网络地址信息if(::bind(listenfd,(struct sockaddr *)&serveraddr, sizeof(serveraddr))<0){perror("bind");return;}printf("server is listenning ...\n");//3、让服务器开始监听//listenfd 跟所有的新连接打交道if(::listen(listenfd, 128) < 0){perror("listen");close(listenfd);return;}int efd = epoll_create1(0);   //底层实现实现使用红黑树,还会有个就绪链表struct epoll_event ev;ev.events = EPOLLIN | EPOLLOUT;   //注册的epoll事件类型,可读和可写ev.data.fd = listenfd;//epoll 要进行监听操作:对listenfd的读写事件进行监听////Reactor :注册读就绪事件int ret = ::epoll_ctl(efd,EPOLL_CTL_ADD,listenfd,&ev);    //将socket的listenfd加入epoll的监听中(efd)//不过这个接口套接字添加到监听中后是在什么地位还不清楚//更像是加入了一个接口(门户),可以通过它可以链接各个客户端的sockfd//accept从listen的全链接队列取出已经链接的请求事件if(ret < 0){perror("epoll_ctl1");close(listenfd);close(efd);return ;}//就绪事件队列设置;struct epoll_event * evtList = (struct epoll_event *)malloc(1024 *sizeof(struct epoll_event)); while(1){int ready = ::epoll_wait(efd,evtList,1024,3000);    //evtlist 就是epoll_wait 执行后,得到的可用客户端链接文件描述符的列表,//和select功能一样,起到一个监听的作用//在整个结构中,这里是只起到监听功能的和用链表结构表示出来已经就绪的客户端链接for(int idx = 0; idx < ready; ++idx){if((evtList[idx].data.fd == listenfd) && (evtList[idx].events & EPOLLIN)){  //判断是否时sockfd就绪,还是其他的文件描述符就绪//这里表示有就绪的链接fd,使用accept获取新连接,且可读(这里有个问题,就是evtlist 就是返回的可用文件描述符,//为什么不直接拿来用呢,非要经过accept的处理,还没有具体弄清楚过程后面再写)int peerfd = accept(listenfd,NULL,NULL);  //TCPConnection connect(peerfd)//将新链接添加到epoll的监听实例中struct epoll_event evt;evt.data.fd = peerfd;evt.events = EPOLLIN | EPOLLOUT |EPOLLERR;ret = ::epoll_ctl(efd,EPOLL_CTL_ADD,peerfd,&evt);if(ret < 0){perror("epoll_ctl");continue;}//新链接到来之后的处理printf(">> new connection has connected , fd = %d\n",peerfd);//>> 此处可以记录日志,使用log4CPP 完成//个性化定制 ==》事件处理器//也可以调用线程去安排处理别的任务等}else{char buff[128] = {0};if(evtList[idx].events& EPOLLIN){int fd = evtList[idx].data.fd;int ret = ::recv(fd,buff,sizeof(buff),0);if(ret > 0){//表示获取到的数据大于0,可能是序列化的数据//对应用层数据进行分析//拿到最终数据后,进行业务逻辑处理////处理完后,是否需要返回给客户ret = send(fd ,buff, strlen(buff),0);}else if(ret == 0){printf("connection has closed \n");struct epoll_event ev;ev.data.fd = fd;epoll_ctl(efd,EPOLL_CTL_DEL,fd, &ev);    //将fd和其事件信息从监听结构中删除//可以记录日志信息//或者其他工作信息}}}}}close(listenfd);
}int main()
{test();return 0;
}

   //本来想这一次写详细些Reactor模型的池化和其他东西,但是越写思考的东西越多,下一篇再写

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

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

相关文章

最大公约数和最小公倍数

1. 最大公约数 给定两个整数&#xff0c;求这两个数的最大公约数 暴力求解&#xff1a; 从较小的那个数开始&#xff0c;依次递减&#xff0c;直到某个数能够同时被整除 //暴力求解 int main() {int a 0;int b 0;scanf("%d %d", &a, &b);int i 0;int min …

代码随想录 Leetcode142. 环形链表 II

题目&#xff1a; 代码(首刷看解析 2024年1月13日&#xff09;&#xff1a; class Solution { public:ListNode *detectCycle(ListNode *head) {if (head nullptr) return nullptr;ListNode* fast head;ListNode* slow head;while (true) {if(fast->next nullptr || fa…

git-生成证书、公钥、私钥、error setting certificate verify locations解决方法

解决方法 方法1-配置证书、公钥、私钥打开Git Bash设置名称和邮箱执行&#xff0c;~/.ssh执行&#xff0c;ssh-keygen -t rsa -C "这是你的邮箱"&#xff0c;如图&#xff1a;进入文件夹可以看到用记事本之类的软件打开id_rsa.pub文件&#xff0c;并且复制全部内容。…

社区团购配送超市与小程序的共赢之路

对于社区服务来说&#xff0c;搭建一个小程序可以提供更加便捷、高效的服务&#xff0c;提升用户体验。下面我们将详细介绍如何通过乔拓云第三方平台搭建一个社区团购小程序。 首先&#xff0c;你需要打开乔拓云第三方平台&#xff0c;这是一个专门为小程序开发提供的平台。在浏…

哪些代码是 Code Review 中的大忌?—— 以 Python 为例

Code Review 首要达成的结果是更好的可读性。 在此基础上才是进一步发现项目的 Bug、处理性能优化上的问题。 因为&#xff0c;编码是给人看的&#xff0c;不是给计算机&#xff08;Coding for human, NOT computer&#xff09;。 一. 滥用缩写命名 Overusing abbreviation …

【LV12 DAY17-18 中断处理】

GPX1_1是外部中断9 EINT9 查询可知其中断ID是57 所以需要进行人为修正lr的地址 sub lr&#xff0c;lr&#xff0c;#4 //iqr异常处理程序 irq_handler: //IRQ异常后LR保存的地址是被IRQ打断指令的下一条再下一条指令的地址&#xff0c;所以我们需要人为进行修正一下sub LR,L…

泛微OA-Ecology8表单中填充用友U8数据

文章目录 1、需求及效果1.1 需求1.2 效果 2、思路及实现步骤2.1 思路2.2 实现步骤 3.结语 1、需求及效果 1.1 需求 在OA中填写表单中时候&#xff0c;比如物料号还需要从U8中查找后才能填写&#xff0c;非常的麻烦。想要在填写表单的时候可以搜索&#xff0c;并且带出其他的关…

如何查看串口号和波特率?

serialport引入后&#xff0c;设备也接上了&#xff0c;一直不知道串口号和波特率去哪里找&#xff0c;当时这个问题困扰了我很久 将设备的线插入到电脑上的插口(串口)桌面的【此电脑】上右击选择管理&#xff0c;打开【设备管理器】在【端口】中找到对应的端口&#xff0c;如果…

【linux】软链接创建(linux的快捷方式创建)

软连接的概念 类似于windows系统中的快捷方式。有的文件目录很长或者每次使用都要找很不方便&#xff0c;于是可以用类似windows的快捷方式的软链接在home&#xff08;初始目录类似于桌面&#xff09;上创建一些软链接方便使用。 软链接的语法 ln -s 参数1 参数2 参数1&#…

智慧园区数字孪生智能可视运营平台解决方案:PPT全文82页,附下载

关键词&#xff1a;智慧园区解决方案&#xff0c;数字孪生解决方案&#xff0c;数字孪生应用场景及典型案例&#xff0c;数字孪生可视化平台&#xff0c;数字孪生技术&#xff0c;数字孪生概念&#xff0c;智慧园区一体化管理平台 一、基于数字孪生的智慧园区建设目标 1、实现…

SpringMVC零基础入门 - 概述、入门搭建、PostMan的使用(常见数据类型的传输)、REST风格编程

SpringMVC零基础入门 - 概述、入门搭建、PostMan的使用(常见数据类型的传输)、REST风格编程 SpringMVC是隶属于Spring框架的一部分&#xff0c;主要是用来进行Web开发&#xff0c;是对Servlet进行了封装SpringMVC是处于Web层的框架&#xff0c;所以其主要的作用就是用来接收前…

解决“win11无法识别U盘“问题

在15.6寸笔记本上插上U盘&#xff0c;有时候出现U盘无法识别的现象&#xff0c;出现这种问题的原因有许多&#xff0c;比如U盘的格式不被当前电脑支持、电脑的USB接口电压过低、没有安装U盘驱动等等。     若是U盘格式不支持&#xff0c;则把U盘改成电脑能够识别的格式&#…

字符串处理(将字符串中符合十六进制数据格式的数字和字符按照其对应的十进制数值进行累加) C语言xdoj704

题目描述&#xff1a; 输入由数字和字符构成的字符串&#xff08;不包含空格&#xff09;&#xff0c;将字符串中符合十六进制数据格式的数字和字符按照其对应的十进制数值进行累加&#xff0c;并输出累加结果&#xff0c;如果字符串中不含有任何满足十六进制格式的字符&#x…

CES 2024丨引领变革,美格智能为智能终端带来生成式AI能力

作为电子行业的“风向标”&#xff0c;CES 2024&#xff08;国际消费电子展&#xff09;于1月9日至12日在美国拉斯维加斯举办。本届展会可谓是AI的盛宴&#xff0c;芯片、AI PC、智能家居、汽车科技、消费电子等领域与AI相关的前沿成果接连发布&#xff0c;引领人工智能领域的科…

6.2 声音编辑工具GoldWave5简介(8)

2&#xff0e;降噪 如果声卡的质量不太好&#xff0c;在录音的过程中免不了会掺杂一些杂音&#xff0c;比如&#xff1a;电流声、爆破声等&#xff0c;此时就需要进行降噪处理。 (1) 选择【效果】|【波波器】|【噪声减小】命令&#xff0c;打开“降噪”对话框。如图6-2-14所示…

Shell编程自动化之Shell数学运算与条件测试

一、Shell数学运算 1.Shell常见的算术运算符号 序号算术运算符号意义1、-、*、/、%加、减、乘、除、取余2**幂运算3、–自增或自减4&&、||、&#xff01;与、或、非5、!相等、不相等&#xff0c;也可写成6、、-、*、/、%赋值运算符&#xff0c;a1相等于aa1 2.Shell常…

C语言之字符串和指针

目录 用数组实现的字符串和用指针实现的字符串 █用数组实现的字符串str █用指针实现的字符串ptr 注意 用数组和指针实现字符串的不同点 字符串数组 用数组实现的字符串的数组——二维数组 用指针实现的字符串数组——指针数组 注意 字符串和指针有着紧密的联系&#…

TikTok系列算法定位还原x-ss-stub

TikTok的x系列的算法比较有名,很多粉丝也问过,之前没有深入研究,本人工作量也比较大。 我们上次说到TikTok的x-ss-stub的算法就是ccmd5标准库算的,今天要讲细致点,表面这个结论本不是直接将数据md5那么来的,是经过一系列分析来的 上图是上次截图的,这次我们分析整个定位…

node(express.js创建项目)+连接mysql数据库

1.node npm的安装 2.express的安装 全局安装:npm install express -gnpm install -g express-generator// ps: 4.0版本把generator分离出来了&#xff0c;需要单独安装3.创建express项目 express 项目名称 cd 项目名称 npm install npm start4.项目中安装数据库 npm install…

C语言督学营(高级阶段)

文章目录 高级阶段19.C语言语法进阶1.条件运算符、逗号运算符(1)条件运算符 / 三目运算符   ? :(2)逗号运算符   , 2.自增自减运算符3.位运算符&#xff1a;按位或、按位异或、按位取反(1)逻辑与、按位与、左移、右移(2)有符号数右移 vs 无符号数右移(3)按位与、按位或、按位…