网络编程:服务器模型-并发服务器-多进程

并发服务器概念:

并发服务器同一时刻可以处理多个客户机的请求

设计思路:

并发服务器是在循环服务器基础上优化过来的

(1)每连接一个客户机,服务器立马创建子进程或者子线程来跟新的客户机通信 (accept之后的),服务器不会与客户端进行通信!!!

(2)IO多路复用技术

1、多进程实现并发服务器

思想:

主进程专门用于连接多个客户端的请求,若有一路客户端连接进来,主进程就创建一个子进程,用该子进程来处理该客户端的业务数据。

回顾:创建进程

 #include <sys/types.h>

 #include <unistd.h>

    pid_t fork(void);
    功能:创建一个子进程
    参数:无
    返回值:pid_t就是int类型的别名
        返回值大于0,代表此时是父进程,该值的含义为创建成功的子进程的ID号
        返回值等于0,代表此时是子进程
        返回值小于0,创建失败可以perror

源代码:

tcp_server.c 

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>//sockaddr_in
#include <unistd.h>
#include <arpa/inet.h>   // 包含 inet_addr 函数的声明 
#include <sys/select.h>
#include <sys/time.h>
#define BUF_SIZE 20int main(int argc, const char *argv[])
{//1.socketint iServer = socket(AF_INET, SOCK_STREAM, 0);if(-1 == iServer){puts("----------1、create socket error!");return -1;}printf("----------1、create socket ok! iServer:%d", iServer);//0,1,2标准输入输出出错,iServer为3//2.bindstruct sockaddr_in stServer;stServer.sin_family = AF_INET;//第一个成员stServer.sin_port = htons(8888);//第二个成员stServer.sin_addr.s_addr = inet_addr("127.0.0.1");//将点分十进制ip地址转换为32位无符号整数int ret = bind(iServer, (struct sockaddr *)&stServer, sizeof(struct sockaddr));if(-1 == ret){puts("----------2、bind error!");return -1;}puts("----------2、bind ok!");//3.listenret = listen(iServer, 5);if(-1 == ret){puts("----------3、listen error!");return -1;}puts("----------3、listen ok!");//4.acceptstruct sockaddr_in stClient;//存放对方的主机信息socklen_t len = sizeof(struct sockaddr_in);char buf[BUF_SIZE] = {0};fd_set stFdr;//文件描述符集合表,大小1024FD_ZERO(&stFdr);//将文件描述符集合表中所有内容清零while(1){FD_SET(iServer, &stFdr);FD_SET(0, &stFdr);//selectret = select(iServer + 1, &stFdr, NULL, NULL, NULL);if(ret <= 0){continue;}printf("select ok, ret = %d\r\n", ret);//FD_ISSETif(FD_ISSET(0, &stFdr)){memset(buf, 0, BUF_SIZE);fgets(buf, BUF_SIZE, stdin);printf("fgets ok, data = %s\r\n", buf);}if(FD_ISSET(iServer, &stFdr)){int iClient = accept(iServer, (struct sockaddr *)&stClient, &len);if(-1 == iClient){continue;//当前客户端出错转向下一个客户端	}printf("----------4、accept ok! iClient = %d\r\n",iClient );//标准输入输出出错,所以下一个打开的文件一定是3//5.recv/sendret = recv(iClient, buf, BUF_SIZE, 0);if(ret <= 0){close(iClient);continue;}printf("----------recv data ok! buf = %s\r\n",buf);//sendret = send(iClient, buf, BUF_SIZE, 0);if(ret <= 0){close(iClient);continue;}printf("----------send data ok! %s\r\n",buf);//close(iClient);//断开当前客户端}}return 0;
}

tcp_client.c

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>#define BUF_SIZE 20
//main函数参数,如果需要键入IP则给定即可
int main(int argc, const char *argv[])
{//1、socketint iClient = 	socket(AF_INET, SOCK_STREAM, 0);if(-1 == iClient){puts("----------1、create socket error!");return -1;}puts("----------1、create socket ok!");//2、connectstruct sockaddr_in stServer;stServer.sin_family = AF_INET;stServer.sin_port = htons(8888);//stServer.sin_addr.s_addr = inet_addr("192.168.15.71");stServer.sin_addr.s_addr = inet_addr("127.0.0.1");int ret = connect(iClient, (struct sockaddr *)&stServer, sizeof(struct sockaddr_in));if(-1 == ret){puts("----------2、connect error!");return -1;}puts("----------2、connect ok!");char buf[BUF_SIZE] = {0};while(1){//gets();//char *fgets(char *s, int size, FILE *stream);fgets(buf, BUF_SIZE, stdin);//更安全,边界检查//3、send recvret = send(iClient, buf, BUF_SIZE, 0);if(-1 == ret){puts("----------3、send data error!");}printf("----------3、send data ok! buf = %s\r\n",buf);//recv//函数原型:ssize_t recv(int sockfd, void *buf, size_t len, int flags);memset(buf, 0, BUF_SIZE);ret = recv(iClient, buf, BUF_SIZE, 0);if(-1 == ret){puts("----------4、recv error!");return -1;}printf("----------4、recv data ok! buf = %s\r\n",buf);}close(iClient);return 0;
}

思考:

多进程并发服务器的缺点:每连接一个客户端,就为其创建子进程,客户端数量比较大时,服务器的运 行效率就会变低。

注:

以上代码只能实现:

        ①客户端连接到服务器端,只能发送一条数据,之后发送不成功

        ②服务器端可以检测标准输入给自己

测试结果如下图:

2、多进程实现并发服务器-优化版本

tcp_server.c 

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>//sockaddr_in
#include <unistd.h>
#include <arpa/inet.h>   // 包含 inet_addr 函数的声明 
#include <sys/select.h>
#include <sys/time.h>
#define BUF_SIZE 20int main(int argc, const char *argv[])
{//1.socketint iServer = socket(AF_INET, SOCK_STREAM, 0);if(-1 == iServer){puts("----------1、create socket error!");return -1;}printf("----------1、create socket ok! iServer:%d", iServer);//0,1,2标准输入输出出错,iServer为3//2.bindstruct sockaddr_in stServer;stServer.sin_family = AF_INET;//第一个成员stServer.sin_port = htons(9999);//第二个成员stServer.sin_addr.s_addr = inet_addr("127.0.0.1");//将点分十进制ip地址转换为32位无符号整数int ret = bind(iServer, (struct sockaddr *)&stServer, sizeof(struct sockaddr));if(-1 == ret){puts("----------2、bind error!");return -1;}puts("----------2、bind ok!");//3.listenret = listen(iServer, 5);if(-1 == ret){puts("----------3、listen error!");return -1;}puts("----------3、listen ok!");//4.acceptstruct sockaddr_in stClient;//存放对方的主机信息socklen_t len = sizeof(struct sockaddr_in);char buf[BUF_SIZE] = {0};fd_set stFdr;//文件描述符集合表,大小1024FD_ZERO(&stFdr);//将文件描述符集合表中所有内容清零FD_SET(iServer, &stFdr);//iServer添加到原文件描述符集合表中int max = iServer;while(1){//selectfd_set stFdrTmp = stFdr;	//定义临时文件描述符集合表ret = select(max + 1, &stFdrTmp, NULL, NULL, NULL);if(ret <= 0){printf("select error!\r\n");continue;}printf("select ok, ret = %d\r\n", ret);int i = 0;for(i = 0; i < max + 1; i++){if(FD_ISSET(i, &stFdrTmp)){	 // 循环判断哪个文件描述符被置位//操作if(i == iServer){ // i == 3, 操作int iClient = accept(iServer, (struct sockaddr *)&stClient, &len);if(-1 == iClient){continue;//当前客户端出错转向下一个客户端	}printf("----------4、accept ok! iClient = %d\r\n",iClient );//标准输入输出出错,所以下一个打开的文件一定是4FD_SET(iClient, &stFdr);//更新maxif(max < iClient){max = iClient;}}else{ // 与多个客户端保持连接//recv/sendret = recv(i, buf, BUF_SIZE, 0);if(ret > 0){printf("recv:%s\r\n", buf);send(i, buf, BUF_SIZE, 0);}else{close(i);FD_CLR(i, &stFdr);}}}}}return 0;
}

tcp_client.c

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>#define BUF_SIZE 20
//main函数参数,如果需要键入IP则给定即可
int main(int argc, const char *argv[])
{//1、socketint iClient = 	socket(AF_INET, SOCK_STREAM, 0);if(-1 == iClient){puts("----------1、create socket error!");return -1;}puts("----------1、create socket ok!");//2、connectstruct sockaddr_in stServer;stServer.sin_family = AF_INET;stServer.sin_port = htons(9999);//stServer.sin_addr.s_addr = inet_addr("192.168.15.71");//stServer.sin_addr.s_addr = inet_addr("192.168.15.71");stServer.sin_addr.s_addr = inet_addr("127.0.0.1");int ret = connect(iClient, (struct sockaddr *)&stServer, sizeof(struct sockaddr_in));if(-1 == ret){puts("----------2、connect error!");return -1;}puts("----------2、connect ok!");char buf[BUF_SIZE] = {0};while(1){//gets();//char *fgets(char *s, int size, FILE *stream);fgets(buf, BUF_SIZE, stdin);//更安全,边界检查//3、send recvret = send(iClient, buf, BUF_SIZE, 0);if(-1 == ret){puts("----------3、send data error!");}printf("----------3、send data ok! buf = %s\r\n",buf);//recv//函数原型:ssize_t recv(int sockfd, void *buf, size_t len, int flags);memset(buf, 0, BUF_SIZE);ret = recv(iClient, buf, BUF_SIZE, 0);if(-1 == ret){puts("----------4、recv error!");return -1;}printf("----------4、recv data ok! buf = %s\r\n",buf);}close(iClient);return 0;
}

注:

以上代码可以实现:

        ①多个客户端与服务器连接 并 发送&回显数据

测试结果如下图:

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

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

相关文章

SpringBoot报空指针错:java.lang.NullPointerException

虽然报空指针错误的原因可能有很多种&#xff0c;但是我还是写上我的报错原因&#xff0c;以此与各位共勉~ 在这里提前说一句&#xff0c;AI虽然强大&#xff0c;但是还是要谨慎使用啊(血的教训)~ 这里先截图我错误的地方&#xff1a; 前端能成功传进来值&#xff0c;后台控制…

图像质量评价指标:了解图像质量的度量方式

图像质量评价指标&#xff1a;了解图像质量的度量方式 在图像处理和计算机视觉领域&#xff0c;评价图像质量的准确性对于许多应用至关重要。通过合适的评价指标&#xff0c;我们可以量化图像的质量&#xff0c;从而更好地了解图像处理算法的效果和改进空间。本文将介绍图像质…

英语学习笔记12——名词所有格的运用

Whose is this … ? This is my/your/his/her … 这……是谁的&#xff1f;这是我的 / 你的 / 他的 / 她的…… Whose is that … ? That is my/your/his/her … 那……是谁的&#xff1f;那是我的 / 你的 / 他的 / 她的…… 词汇 Vocabulary father n. 爸爸 口语&#xf…

2024统计建模成品论文39页(附带完整数据集和代码)

2024统计建模成品论文完整版一等奖论文【1.5w字全网最佳】2024统计建模大赛高质量成品论文39页配套完整代码运行全套数据集https://www.jdmm.cc/file/2710661/

在idea中使用vue

一、安装node.js 1、在node.js官网&#xff08;下载 | Node.js 中文网&#xff09;上下载适合自己电脑版本的node.js压缩包 2、下载完成后进行解压并安装&#xff0c;一定要记住自己的安装路径 一直点击next即可&#xff0c;这部选第一个 3、安装成功后&#xff0c;按住winR输入…

如何使用 ArcGIS Pro 计算容积率

容积率是指地上建筑物的总面积与用地面积的比率&#xff0c;数值越小越舒适&#xff0c;这里为大家介绍一下如何使用ArcGIS Pro 计算容积率&#xff0c;希望能对你有所帮助。 数据来源 教程所使用的数据是从水经微图中下载的建筑和小区数据&#xff0c;除了建筑和小区数据&am…

【408精华知识】速看!各种排序的大总结!

文章目录 一、插入排序&#xff08;一&#xff09;直接插入排序&#xff08;二&#xff09;折半插入排序&#xff08;三&#xff09;希尔排序 二、交换排序&#xff08;一&#xff09;冒泡排序&#xff08;二&#xff09;快速排序 三、选择排序&#xff08;一&#xff09;简单选…

【Arduino】数字I/O的使用

目录 1、引脚工作模式 2、写入引脚digitaWrite&#xff08;&#xff09; 3、读取引脚digitalRead(pin); 4、示例 跑马灯 1、引脚工作模式 Arduino通过pinMode()设置引脚的io工作模式&#xff0c;一共有4种模式 工作模式 Mode 说明 输出模式 OUTPUT 引脚为低阻抗状态&…

YOLOv9改进策略目录 | 包含卷积、主干、检测头、注意力机制、Neck上百种创新机制

&#x1f451; YOLOv9有效涨点专栏目录 &#x1f451; 专栏视频介绍&#xff1a;包括专栏介绍、得到的项目文件、模型二次创新、权重文件的使用问题&#xff0c;点击即可跳转。 前言 Hello&#xff0c;各位读者们好 本专栏自开设两个月以来已经更新改进教程50余篇其中包含Re…

自动攻丝机进出料激光检测 进料出料失败报警循环手动及关闭报警退出无限循环

/**************进料检测********************/ /***缺料无限次循环 手动退出 超时报警*******/ void check_Pon() // { zstatus0; //报警计数器归零 Signauto1; …

Pytorch 与 Tensorflow:深度学习的主要区别(1)

引言 目前&#xff0c;Python 深度学习领域已经涌现出多个由科技界巨头如 Google、Facebook 和 Uber 等公司公开发布的框架&#xff0c;这些框架旨在帮助开发者构建先进的计算架构。对于刚接触这一领域的你来说&#xff0c;深度学习是计算机科学中的一个分支&#xff0c;它通过…

如何查看centos7是否安装nginx

要查看 CentOS 7 系统上是否安装了 Nginx&#xff0c;您可以使用多种方法来检查。以下是一些常见的方法&#xff1a; 通过 RPM 包管理器查询 在 CentOS 系统上&#xff0c;可以使用 RPM 包管理器来查询已安装的软件包。要查看是否安装了 Nginx&#xff0c;您可以在终端中运行以…

【深度学习目标检测】二十六、基于深度学习的垃圾检测系统-含数据集、GUI和源码(python,yolov8)

设计垃圾检测系统的意义在于多个方面&#xff0c;这些方面不仅关乎环境保护和城市管理&#xff0c;还涉及到技术进步和社会效益。以下是设计垃圾检测系统的主要意义&#xff1a; 环境保护与资源回收&#xff1a; 垃圾检测系统能够有效地识别不同种类的垃圾&#xff0c;帮助人们…

工作玩手机监测识别摄像机

工作场所的员工玩手机已经成为了一种常见的现象&#xff0c;特别是在办公室、生产车间等地方。而这种现象不仅仅影响了员工的工作效率&#xff0c;还可能会对工作安全造成一定的隐患。为了监测和识别员工玩手机的情况&#xff0c;工作玩手机监测识别摄像机应运而生。工作玩手机…

旧物回收小程序开发:打造绿色生活,共筑美好未来

随着环保意识的逐渐增强&#xff0c;我们越来越意识到旧物回收的重要性。为了响应这一趋势&#xff0c;我们精心研发了一款旧物回收小程序&#xff0c;旨在通过科技的力量&#xff0c;让每个人都能够轻松参与到旧物回收的行动中来&#xff0c;共同为地球环保贡献一份力量。 一…

第187题| 快速学会“阿贝尔定理”| 无穷级数(十五)|武忠祥老师每日一题

解题思路&#xff1a;这道题没有告诉我们是多少&#xff0c;没办法求出收敛半径&#xff0c;所以我们只能根据题目给的两个条件来解题&#xff08;选项代入法&#xff09;。 1.x-1&#xff0c;说明收敛的中心点是1&#xff0c;观察下列选项&#xff0c;显然答案在C和D之中。 …

【数据结构】详解栈且实现

一.栈 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端 称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵守后进先出LIFO&#xff08;Last In First Out&#xff09;的原则。 压栈&#xff1a;…

【鸿蒙开发】第二十四章 IPC与RPC进程间通讯服务

1 IPC与RPC通信概述 IPC&#xff08;Inter-Process Communication&#xff09;与RPC&#xff08;Remote Procedure Call&#xff09;用于实现跨进程通信&#xff0c;不同的是前者使用Binder驱动&#xff0c;用于设备内的跨进程通信&#xff0c;后者使用软总线驱动&#xff0c;…

【笔记】EF_PNN获取及运营商名称显示(待完善)

问题背景 当设备无法成功解析EONS(PNN)的值(即SIM卡EF文件内容),则会用次优先级的NITZ去refresh了SPN。(问题代码如下,是通过Phone对象拿到plmn为空) 运营商名称一般显示优先级:Eons > NITZ > XML OPL id 0 对应的是PNN第一条 功能逻辑 (定制)当卡中的spn为空…

HTML炫酷的相册

目录 写在前面 HTML简介 完整代码 代码分析 系列推荐 写在最后 写在前面 本期小编给大家带来一个炫酷的旋转相册&#xff0c;快来解锁属于你的独家记忆吧&#xff01; HTML简介 HTML&#xff08;全称为超文本标记语言&#xff09;是一种用于创建网页结构和内容的标记语…