最新要闻
- 不怕买不到首发!京东官宣可提前抢苹果iPhone新品
- 检测新途径!27年有望实现尿液宫颈癌HPV筛查
- 10岁男孩落水从山西漂到河北:仰面朝上 “教科书”式自救
- 美国半导体行业协会总裁:没有一个国家可以扭转芯片供应链 半导体行业需要中国
- 6999元最强“争气机”!华为Mate 60 Pro南糯紫图赏
- 杜锋:波多黎各喜欢出手三分会有长篮板 我们每个人都要努力去抢
- 梦到虫子有什么寓意(梦到虫子预示着什么)
- 美方无理扣留、盘查、遣返中国留学生,中方回应!
- 关塔那摩监狱前囚犯揭美军虐囚:放狗咬 剥夺睡眠
- 贵州4地入选现代流通战略支点城市布局建设名单
- 王梓旭加盟吉林男篮
- 聊天记录太占内存?微信存储空间清理教程来了
- 湖北通报6起党员干部和公职人员酒驾醉驾典型案例
- 默认使用微软搜索 微软停止Chrome弹窗广告
- “文明夜长沙 一起零酒驾” 长沙市第八届文明交通百日零违法挑战赛启动
- 皇马33岁巨星再次开炮:有些球员去沙特只为钱,这是反足球
手机
又是一年开学季丨无缝接驳 直通高校 武汉贴心迎新
科锐国际廖萍: 数字化人才将成为VUCA时代企业人才战略的核心
- 又是一年开学季丨无缝接驳 直通高校 武汉贴心迎新
- 科锐国际廖萍: 数字化人才将成为VUCA时代企业人才战略的核心
- 郑州地铁3号线二期通过初期运营前安全评估
- 筑牢基层安全“防火墙”盘龙区金辰街道这么做
- 中金关注政策组合拳的长期积极影响
- 军士选晋,为战选才!
家电
26.高并发服务器
26.高并发服务器
阻塞函数在阻塞期间若收到信号,会被信号终端,errno设置为EINTR,这个错误不应该看成一个错误。
while (1){cfd = accept();while (1){n = read(cfd, buf, sizeof(buf));if (n <= 0){break;}}}
- 解决办法1:
将cfd设置为非阻塞: fcntl
假如有多个客户端连接请求,cfd只会保留最后一个文件描述符的值
- 解决方法2:
使用多进程: 让父进程监听接受新的连接, 子进程处理新的连接(接收和发送数据);
(相关资料图)
父进程还负责回收子进程
- 处理流程:
1.创建socket,得到一个监听的文件描述符lfd---socket()2.将lfd和IP和端口port进行绑定-----bind();3.设置监听----listen()4.进入
while (1){ //等待有新的客户端连接到来 cfd = accept(); //fork一个子进程,让子进程去处理数据 pid = fork(); if (pid < 0) { exit(-1); } else if (pid > 0) { //关闭通信文件描述符cfd close(cfd); } else if (pid == 0) { //关闭监听文件描述符 close(lfd); //收发数据 while (1) { //读数据 n = read(cfd, buf, sizeof(buf)); if (n <= 0) { break; } //发送数据给对方 write(cfd, buf, n); } close(cfd); //下面的exit必须有,防止子进程再去创建子进程 exit(0); }}close(lfd);
这段代码是一个简化版本的基于fork()
的TCP服务器程序。它不断地接受客户端的连接请求,然后通过fork()
创建子进程来处理每个客户端的数据通信。
现在,让我们详细解析这段代码:
外层无限循环:
while (1)
这是一个无限循环,意味着服务器会持续地等待新的客户端连接。
接受客户端连接:
cfd = accept();
accept()
函数等待并接受一个客户端连接,然后返回一个新的文件描述符cfd
用于与该客户端通信。创建子进程处理连接:
pid = fork();
fork()
函数创建一个新的进程。这个新进程是原始进程的一个复制品。这里的目的是允许子进程处理新客户端的连接,而父进程则继续等待更多的客户端连接。错误处理:
if (pid < 0){ exit(-1);}
如果
fork()
失败,则返回-1,这时,程序会退出。父进程操作:
else if (pid > 0){ //关闭通信文件描述符cfd close(cfd);}
如果
pid
大于0,表示我们当前处于父进程。父进程将关闭与该特定客户端的连接文件描述符cfd
,因为它将由子进程处理。子进程操作:
else if (pid == 0){ //关闭监听文件描述符 close(lfd); //收发数据 while (1) { //读数据 n = read(cfd, buf, sizeof(buf)); if (n <= 0) { break; } //发送数据给对方 write(cfd, buf, n); } close(cfd); //下面的exit必须有, 防止子进程再去创建子进程 exit(0);}
如果
pid
等于0,表示我们现在处于子进程。子进程首先关闭监听文件描述符lfd
,然后进入一个循环,不断地从客户端读取数据并立即将其写回。如果读取的数据长度为0或小于0(表示客户端断开连接或发生错误),子进程将退出循环,关闭与客户端的连接文件描述符cfd
,然后正常退出。关闭监听文件描述符:
close(lfd);
这段代码实际上永远不会被执行到,因为它位于外层无限循环的外部。
总结:这是一个使用fork()
的多进程TCP服务器的简化示例,它可以同时处理多个客户端连接。当有新的客户端连接时,服务器会创建一个新的子进程专门用于处理该客户端的通信。这种方式可以实现多任务,但如果有大量客户端连接,则可能会导致系统资源的过度使用。实际的生产环境中可能会选择使用线程或事件驱动模型来处理大量的并发连接。
还需要添加的功能: 父进程使用SIGCHLD信号完成对子进程的回收注意点: accept或者read函数是阻塞函数,会被信号打断,此时不应该视为一个错误。errno=EINTR
父子进程能够共享的:文件描述符(子进程复制父进程的文件描述符)mmap共享映射区
问题:1.子线程能否关闭lfd?子线程不能关闭监听文件描述符lfd,原因是子线程和主线程共享文件描述符而不是复制的。2.主线程能否关闭cfd?主线程不能关闭cfd,主线程和子线程共享一个cfd,而不是复制的,close之后cfd就会被真正关闭。3.多个子线程共享cfd,会有什么问题发生?
struct INFO{int cfd;pthread_t threadID;struct sockaddr_in client;};struct INFO info[100];//初始化INFO数组for (i = 0; i < 100; i++){info[i].cfd = -1;}for (i = 0; i < 100; i++){if (info[i].cfd == -1){//这块内存可以使用}}if (i == 100){//拒绝接受新的连接close(cfd);}
作业:1.改进多进程版本的服务器代码。父进程使用SIGCHLD信号完成对子进程的回收。2.改进多线程版本的服务器。
多线程版本的服务器开发流程:
{1 创建socket, 得到一个监听的文件描述符lfd-- - socket()2 将lfd和IP和端口port进行绑定---- - bind();3 设置监听----listen()4 while (1){//接受新的客户端连接请求cfd = accept();//创建一个子线程pthread_create(&threadID, NULL, thread_work, &cfd);//设置线程为分离属性pthread_detach(threadID);}close(lfd);}
子线程执行函数:
void* thread_work(void* arg){//获得参数: 通信文件描述符int cfd = *(int*)arg;while (1){//读数据n = read(cfd, buf, sizeof(buf));if (n <= 0){break;}//发送数据write(cfd, buf, n);}close(cfd);}
问题:1.子线程能否关闭lfd?子线程不能关闭监听文件描述符lfd,原因是子线程和主线程共享文件描述符而不是复制的。2.主线程能否关闭cfd?主线程不能关闭cfd,主线程和子线程共享一个cfd,而不是复制的,close之后cfd就会被真正关闭.3.多个子线程共享cfd,会有什么问题发生?
如何支持多个客户端---支持多并发的服务器
由于accept和read函数都会阻塞,如当read的时候,不能调用accept接受新的连接,当accept阻塞等待的时候不能read读数据.
第一种方案: 使用多进程,可以让父进程接受新连接,让子进程处理与客户端通信
思路: 让父进程accept接受新连接,然后fork子进程,让子进程处理通信,子进程处理完成后退出,父进程使用SIGCHLD信号回收子进程.
代码实现:
第二种方案: 使用多线程,让主线程接受新连接,让子线程处理与客户端通信; 使用多线程要将线程设置为分离属性,让线程在退出之后自己回收资源。
思考:如何不使用多进程或者多线程完成多个客户端的连接请求
可以将accept和read函数设置为非阻塞,调用fcntl函数可以将文件描述符设置为非阻塞, 让后再while循环中忙轮询。
//多进程版本的网络服务器#include #include #include #include #include #include #include #include #include "wrap.h"int main(){//创建socketint lfd = Socket(AF_INET, SOCK_STREAM, 0);//绑定struct sockaddr_in serv;bzero(&serv, sizeof(serv));serv.sin_family = AF_INET;serv.sin_port = htons(8888);serv.sin_addr.s_addr = htonl(INADDR_ANY);Bind(lfd, (struct sockaddr *)&serv, sizeof(serv));//设置监听Listen(lfd, 128);pid_t pid;int cfd;char sIP[16];socklen_t len;struct sockaddr_in client;while(1){//接受新的连接len = sizeof(client);memset(sIP, 0x00, sizeof(sIP));cfd = Accept(lfd, (struct sockaddr *)&client, &len);printf("client:[%s] [%d]\n", inet_ntop(AF_INET, &client.sin_addr.s_addr, sIP, sizeof(sIP)), ntohs(client.sin_port));//接受一个新的连接, 创建一个子进程,让子进程完成数据的收发操作pid = fork();if(pid<0){perror("fork error");exit(-1);}else if(pid>0){//关闭通信文件描述符cfdclose(cfd);}else if(pid==0){//关闭监听文件描述符close(lfd);int i=0;int n;char buf[1024];while(1){//读数据n = Read(cfd, buf, sizeof(buf));if(n<=0){printf("read error or client closed, n==[%d]\n", n);break;}//printf("client:[%s] [%d]\n", inet_ntop(AF_INET, &client.sin_addr.s_addr, sIP, sizeof(sIP)), ntohs(client.sin_port));printf("[%d]---->:n==[%d], buf==[%s]\n", ntohs(client.sin_port), n, buf);//将小写转换为大写for(i=0; i
#include #include #include #include #include #include #include #include void perr_exit(const char *s){perror(s);exit(-1);}int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr){int n;again:if ((n = accept(fd, sa, salenptr)) < 0) {if ((errno == ECONNABORTED) || (errno == EINTR))goto again;elseperr_exit("accept error");}return n;}int Bind(int fd, const struct sockaddr *sa, socklen_t salen){ int n;if ((n = bind(fd, sa, salen)) < 0)perr_exit("bind error"); return n;}int Connect(int fd, const struct sockaddr *sa, socklen_t salen){ int n;if ((n = connect(fd, sa, salen)) < 0)perr_exit("connect error"); return n;}int Listen(int fd, int backlog){ int n;if ((n = listen(fd, backlog)) < 0)perr_exit("listen error"); return n;}int Socket(int family, int type, int protocol){int n;if ((n = socket(family, type, protocol)) < 0)perr_exit("socket error");return n;}ssize_t Read(int fd, void *ptr, size_t nbytes){ssize_t n;again:if ( (n = read(fd, ptr, nbytes)) == -1) {if (errno == EINTR)goto again;elsereturn -1;}return n;}ssize_t Write(int fd, const void *ptr, size_t nbytes){ssize_t n;again:if ( (n = write(fd, ptr, nbytes)) == -1) {if (errno == EINTR)goto again;elsereturn -1;}return n;}int Close(int fd){ int n;if ((n = close(fd)) == -1)perr_exit("close error"); return n;}/*参三: 应该读取的字节数*/ssize_t Readn(int fd, void *vptr, size_t n){size_t nleft; //usigned int 剩余未读取的字节数ssize_t nread; //int 实际读到的字节数char *ptr;ptr = vptr;nleft = n;while (nleft > 0) {if ((nread = read(fd, ptr, nleft)) < 0) {if (errno == EINTR)nread = 0;elsereturn -1;} else if (nread == 0)break;nleft -= nread;ptr += nread;}return n - nleft;}ssize_t Writen(int fd, const void *vptr, size_t n){size_t nleft;ssize_t nwritten;const char *ptr;ptr = vptr;nleft = n;while (nleft > 0) {if ( (nwritten = write(fd, ptr, nleft)) <= 0) {if (nwritten < 0 && errno == EINTR)nwritten = 0;elsereturn -1;}nleft -= nwritten;ptr += nwritten;}return n;}static ssize_t my_read(int fd, char *ptr){static int read_cnt;static char *read_ptr;static char read_buf[100];if (read_cnt <= 0) {again:if ( (read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) {if (errno == EINTR)goto again;return -1;} else if (read_cnt == 0)return 0;read_ptr = read_buf;}read_cnt--;*ptr = *read_ptr++;return 1;}ssize_t Readline(int fd, void *vptr, size_t maxlen){ssize_t n, rc;char c, *ptr;ptr = vptr;for (n = 1; n < maxlen; n++) {if ( (rc = my_read(fd, &c)) == 1) {*ptr++ = c;if (c == "\n")break;} else if (rc == 0) {*ptr = 0;return n - 1;} elsereturn -1;}*ptr = 0;return n;}int tcp4bind(short port,const char *IP){ struct sockaddr_in serv_addr; int lfd = Socket(AF_INET,SOCK_STREAM,0); bzero(&serv_addr,sizeof(serv_addr)); if(IP == NULL){ //如果这样使用 0.0.0.0,任意ip将可以连接 serv_addr.sin_addr.s_addr = INADDR_ANY; }else{ if(inet_pton(AF_INET,IP,&serv_addr.sin_addr.s_addr) <= 0){ perror(IP);//转换失败 exit(1); } } serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(port); Bind(lfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)); return lfd;}
#ifndef __WRAP_H_#define __WRAP_H_#include #include #include #include #include #include #include #include void perr_exit(const char *s);int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr);int Bind(int fd, const struct sockaddr *sa, socklen_t salen);int Connect(int fd, const struct sockaddr *sa, socklen_t salen);int Listen(int fd, int backlog);int Socket(int family, int type, int protocol);ssize_t Read(int fd, void *ptr, size_t nbytes);ssize_t Write(int fd, const void *ptr, size_t nbytes);int Close(int fd);ssize_t Readn(int fd, void *vptr, size_t n);ssize_t Writen(int fd, const void *vptr, size_t n);ssize_t my_read(int fd, char *ptr);ssize_t Readline(int fd, void *vptr, size_t maxlen);int tcp4bind(short port,const char *IP);#endif
关键词:
26.高并发服务器
Python名称空间和作用域,闭包函数
业务安全情报第22期 | 不法分子为何盗刷企业短信?
Induction of Design Pattern
减税降费接连出台 积极财政政策加力提效
【财经分析】多空博弈促债市继续盘整 10年期国债利率料上行“有顶”
【新华500】新华500指数(989001)7日跌1.45%
【财经分析】多空博弈促债市继续盘整 10年期国债利率料上行“有顶”
【金融街发布】外汇局:截至8月末外储规模为31601亿美元
不怕买不到首发!京东官宣可提前抢苹果iPhone新品
检测新途径!27年有望实现尿液宫颈癌HPV筛查
10岁男孩落水从山西漂到河北:仰面朝上 “教科书”式自救
美国半导体行业协会总裁:没有一个国家可以扭转芯片供应链 半导体行业需要中国
6999元最强“争气机”!华为Mate 60 Pro南糯紫图赏
沪硅产业:大基金提前终止减持股份计划
从三花猫到癌症治疗:新兴的表观遗传学有哪些机会?
广州海事局启动防热带气旋Ⅲ级响应防范“苏拉”
队报:维拉蒂接近加盟卡塔尔球队阿拉比 德拉克斯勒也可能离队
8月31日汇市观潮:欧元、英镑和澳元技术分析
AIGC说真相|那些年美国扔下的集束炸弹
福建提升防台风应急响应为Ⅱ级 启动防暴雨Ⅳ级应急响应
“谢谢警察叔叔!这是我第一次坐警车!”
跨国零售企业在华业务保持增长
杜锋:波多黎各喜欢出手三分会有长篮板 我们每个人都要努力去抢
三年级上册语文第五单元作文范文
降息了,A股却下跌了?市场在酝酿新的方向!
米干怎么吃 米干怎么吃法
卓越商企服务:主航道业务稳步增长 新赛道构建第二增长曲线
又是一年开学季丨无缝接驳 直通高校 武汉贴心迎新
小鹏最意难平旗舰SUV 新款G9谍照亮相:白内+双风冷无线充
湖南省委政法委:凝心铸魂筑牢根本 实干担当促进发展
先进数通(300541.SZ):主要业务领域,均与华为建立了深入的合作关系
梦到虫子有什么寓意(梦到虫子预示着什么)
美方无理扣留、盘查、遣返中国留学生,中方回应!
国产机器人龙头市场份额 新兴产业成为工业机器人应用新阵地
灵台县职业中等专业学校军训服、校服采购项目中标(成交)结果公告
科锐国际廖萍: 数字化人才将成为VUCA时代企业人才战略的核心
关塔那摩监狱前囚犯揭美军虐囚:放狗咬 剥夺睡眠
windows云电脑
友邦人寿加码银保业务有成效,偿付能力指标待改善
宁夏回族自治区党委原副书记、银川市委原书记姜志刚被“双开”
新突破!广州东部公铁联运枢纽首发汽车专列
直播带货的铠甲与软肋
贵州4地入选现代流通战略支点城市布局建设名单
工商银行喜结良缘金条30克价格今天多少一克(2023年08月31日)
东莞农商行上半年营收净利微增,资产质量继续保持平稳
越素越美!
衡东:800万元助学贷款助学子圆梦
做好垃圾分类,共建绿美花都
打出三张牌激发美食康养文旅消费热
正式成立!泉州市第一医院与上海九院医疗技术协作中心揭牌!
武汉市烟草局:守护成长 “未”爱护航
科创芯片ETF华安(588290)早盘震荡走强,已涨3.02%,盘中成交额破亿元
郑州地铁3号线二期通过初期运营前安全评估
筑牢基层安全“防火墙”盘龙区金辰街道这么做
椰子水价格暴涨4000%!供不应求 订单量上涨数倍
金华一批铁路相关项目有新进展
朱保全回应印力接管万科旗下商业项目进度:目前进展顺利
“共建‘一带一路’十周年:回顾与展望”国际研讨会在霍尔果斯成功举办
以系统观念深入开展主题教育
周末带孩子去哪里玩
山东高速建材集团开展生产车间 “岗位大练兵、技能大比武”活动
79平两房卧室这样设计,绝对是小户型的模仿典范!
这场“零距离”企业恳谈会越开越“有味”
自然资源部:台风“苏拉”逼近 海浪红色预警、风暴潮橙色预警
方便面、汉堡都是垃圾食品?还真不是,什么是垃圾食品呢?
云天化(600096.SH):上半年净利润26.78亿元 同比下降22.74%
20家上市车企披露半年报,超半数净利润增长
中金关注政策组合拳的长期积极影响
开学啦!
军士选晋,为战选才!
魏巍谁是最可爱的人原文选材(魏巍谁是最可爱的人原文)
深圳国际累计完成发行81亿元境内公司债券
叶公好龙的故事和寓意(叶公好龙的故事)
王梓旭加盟吉林男篮
宁夏:新型电力 减碳有利
股价暴跌44%!市值一夜蒸发6050亿元!越南造车新势力被“打回原形”?
冯磊任上海市气象局党组书记、局长
韩旭接班人确定!200多斤打内线,狂拿31分,澳大利亚没脾气
聊天记录太占内存?微信存储空间清理教程来了
湖北通报6起党员干部和公职人员酒驾醉驾典型案例
年假没休完会清零吗
合众人寿河南分公司开展清廉金融文化教育培训活动
216万余人次!暑期洛阳文博机构参观人数创新高
老年性黄斑变性——老年人视力的沉默杀手
中国服饰控股(01146)附属认购1000万元理财产品
默认使用微软搜索 微软停止Chrome弹窗广告
全球第三!市值超过蔚小理!VinFast凭什么?
郑州银建地产100%股权及相关债权被挂牌 底价7953.76万元
恒大财富,成“老赖”!
危地马拉选举委员会宣布阿雷瓦洛赢得总统选举
众志成城 防汛救灾丨河北受灾地区多措并举确保学生秋季如期开学
开通倒计时!广汕高铁开出首趟试运行列车
重庆农村商业银行(03618):重庆市国资委拟将其持有的重庆水务环境控股集团80%股权无偿划转至重庆渝富控股集团
给未来的自己歌词完整版 给未来的自己歌词
李先生加州牛肉面 官银号店(对于李先生加州牛肉面 官银号店简单介绍)
A股午评:三大指数冲高回落 华为手机产业链、半导体板块涨幅居前
盘点易建联商业版图
“文明夜长沙 一起零酒驾” 长沙市第八届文明交通百日零违法挑战赛启动
武汉黄陂村史馆里传新风,文明实践育新人