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

并发服务器概念:

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

设计思路:

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

(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 20

int main(int argc, const char *argv[])
{
	//1.socket
	int 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.bind
	struct 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.listen
	ret = listen(iServer, 5);
	if(-1 == ret){
		puts("----------3、listen error!");
		return -1;
	}
	puts("----------3、listen ok!");

	//4.accept
	struct sockaddr_in stClient;//存放对方的主机信息
	socklen_t len = sizeof(struct sockaddr_in);
	char buf[BUF_SIZE] = {0};
	fd_set stFdr;//文件描述符集合表,大小1024
	FD_ZERO(&stFdr);//将文件描述符集合表中所有内容清零
	while(1){
		FD_SET(iServer, &stFdr);
		FD_SET(0, &stFdr);
		//select
		ret = select(iServer + 1, &stFdr, NULL, NULL, NULL);

		if(ret <= 0){
			continue;
		}

		printf("select ok, ret = %d\r\n", ret);

		//FD_ISSET
		if(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/send
			ret = recv(iClient, buf, BUF_SIZE, 0);
			if(ret <= 0){
				close(iClient);
				continue;
			}
			printf("----------recv data ok! buf = %s\r\n",buf);
			//send
			ret = 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、socket
	int iClient = 	socket(AF_INET, SOCK_STREAM, 0);
	if(-1 == iClient){
		puts("----------1、create socket error!");
		return -1;
	}
	puts("----------1、create socket ok!");

	//2、connect
	struct 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 recv
		ret = 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 20

int main(int argc, const char *argv[])
{
	//1.socket
	int 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.bind
	struct 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.listen
	ret = listen(iServer, 5);
	if(-1 == ret){
		puts("----------3、listen error!");
		return -1;
	}
	puts("----------3、listen ok!");

	//4.accept
	struct sockaddr_in stClient;//存放对方的主机信息
	socklen_t len = sizeof(struct sockaddr_in);
	char buf[BUF_SIZE] = {0};
	fd_set stFdr;//文件描述符集合表,大小1024
	FD_ZERO(&stFdr);//将文件描述符集合表中所有内容清零
	FD_SET(iServer, &stFdr);//iServer添加到原文件描述符集合表中
	int max = iServer;
	while(1)
	{
		//select
		fd_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 );//标准输入输出出错,所以下一个打开的文件一定是4
					FD_SET(iClient, &stFdr);
					//更新max
					if(max < iClient)
					{
						max = iClient;
					}
				}
				else
				{ // 与多个客户端保持连接
					//recv/send
					ret = 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、socket
	int iClient = 	socket(AF_INET, SOCK_STREAM, 0);
	if(-1 == iClient){
		puts("----------1、create socket error!");
		return -1;
	}
	puts("----------1、create socket ok!");

	//2、connect
	struct 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 recv
		ret = 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;
}

注:

以上代码可以实现:

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

测试结果如下图:

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Web APIs(获取元素+操作元素+节点操作)

目录 1.API 和 Web API 2.DOM导读 DOM树 3.获取元素 getElementById获取元素 getElementsByTagName获取元素 H5新增方法获取 获取特殊元素 4.事件基础 执行事件 操作元素 修改表单属性 修改样式属性 使用className修改样式属性 获取属性的值 设置属性的值 移除…

【多人协作】场景模拟(一)

文章目录 实现多人协作场景&#xff1a;操作流程1开发人员a和b克隆仓库到本地2在本地仓库建立分支并与远程分支建立链接3开发人员工作并提交代码4将合并dev分支与master分支 实现多人协作 多人协作开发是git的最核心也是最重要的操作。多人协作也就意味着同一时间里&#xff0…

基于springboot+vue+Mysql的音乐翻唱与分享平台

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

2024年3月 青少年等级考试机器人理论真题三级

202403 青少年等级考试机器人理论真题三级 第 1 题 流程图图例如下&#xff0c;与该图例功能对应的选项是&#xff1f;&#xff08; &#xff09; A&#xff1a;开始/结束 B&#xff1a;输入/输出 C&#xff1a;判断 D&#xff1a;处理 第 2 题 Arduino UNO/Nano主控板&am…

鸿蒙ArkUI开发:常用布局【弹性布局方向图】

弹性布局方向图 Flex({ direction: FlexDirection.Row }) FlexDirection.Row&#xff08;默认值&#xff09;&#xff1a;主轴为水平方向&#xff0c;子组件从起始端沿着水平方向开始排布FlexDirection.RowReverse&#xff1a;主轴为水平方向&#xff0c;子组件从终点端沿着F…

【机器学习300问】83、深度学习模型在进行学习时梯度下降算法会面临哪些局部最优问题?

梯度下降算法是一种常用的优化方法&#xff0c;用于最小化损失函数以训练模型。然而&#xff0c;在使用梯度下降算法时&#xff0c;可能会面临以下局部最优问题。 &#xff08;一&#xff09;非凸函数的局部极小值 问题描述&#xff1a;在复杂的损失函数中&#xff0c;如果目…

2023年上半年信息系统项目管理师——综合知识真题与答案解释(3)

2023年上半年信息系统项目管理师 ——综合知识真题与答案解释(3) 整个4月份都在忙处理我家所在楼的电梯托管工作&#xff0c;红头文件的5月期限&#xff0c;临时加入&#xff0c;人少琐事较多&#xff0c;从寻找电梯托管公司&#xff0c;整理总结托管公司资料&#xff0c;组织…

基于SpringBoot+微信小程序的订餐(点餐)配送系统设计与实现+毕业论文(12000字)

系统介绍 本微信小程序在线订餐系统管理员功能可以修改个人中心&#xff0c;用户管理&#xff0c;菜品分类管理&#xff0c;菜品信息管理&#xff0c;订单信息管理&#xff0c;取消订单管理&#xff0c;订单配送管理&#xff0c;菜品评价管理以及系统管理。微信小程序用户可以…

企业邮箱是什么?怎么申请一个企业邮箱

企业邮箱是什么&#xff1f;企业邮箱包含着许多企业需要的功能&#xff0c;包含统一创建签名、大容量存储、域名定制等功能&#xff0c;这些功能能够帮助企业更好地展示企业的专业形象以及更好得协作办公。本文将详细介绍企业邮箱的概念、特征和企业邮箱的申请步骤。 一、企业…

云原生技术解析

云原生的概念 云原生是一种软件架构和部署方法&#xff0c;旨在利用云计算的优势&#xff0c;以更灵活、可扩展和可靠的方式构建和部署应用程序。它主要关注在容器、微服务、自动化和持续交付等方面。 云原生技术是指以云计算作为基础&#xff0c;以平台和工具为依托&#xff0…

2024年视频号小店值不值得做?看完这五点,茅塞顿开!

大家好&#xff0c;我是电商糖果 视频号小店做为今年讨论度最高的电商项目之一&#xff0c;关于它值不值得做&#xff0c;在网上可以说讨论的非常激烈。 糖果做小店已经快两年了&#xff0c;按照我目前的经营状态来说&#xff0c;对这个平台还是非常满意的。 当然最满意的还…

JDK的串行收集器介绍与优化指南-01

JDK串行收集器概述 定义与背景 串行收集器&#xff08;Serial Collector&#xff09;是Java虚拟机&#xff08;JVM&#xff09;中的一种单线程垃圾收集器&#xff0c;它在垃圾收集过程中会暂停所有工作线程&#xff0c;直至收集完成。它适用于内存资源受限、对吞吐量要求不高…

【面试经典题】环形链表

个人主页&#xff1a;一代… 个人专栏&#xff1a;数据结构 在面试中我们经常会遇到有关链表的相关题目&#xff0c;面试官通常会对题目给出拓展 下面我就两个leetcode上的一个双指针的题目为例&#xff0c;并对其进行拓展 题目链接&#xff1a;环形链表 题目描述&#xf…

python内置函数exec()和eval()区别

在Python中&#xff0c;eval() 和 exec() 都是内置函数&#xff0c;用于执行存储在字符串或对象中的Python代码&#xff0c;但它们之间也有一些区别。 eval() 语法&#xff1a;eval(expression, globalsNone, localsNone) expression&#xff1a;需要求值的字符串表达式。可…

经典文献阅读之--LiDAR-based 4D Occupancy Completion and Forecasting(基于激光雷达的4D占用补全与预测)

0. 简介 本文介绍了基于激光雷达的4D占用补全与预测。场景补全与预测是自动驾驶汽车等移动智能体研究中的两个常见的感知问题。现有的方法独立地处理这两个问题&#xff0c;导致这两方面的感知是分开的。在《LiDAR-based 4D Occupancy Completion and Forecasting》中&#xf…

基于单片机的自动售货机系统

基于单片机的售货机系统 &#xff08;仿真&#xff0b;程序&#xff0b;设计报告&#xff09; 功能介绍 具体功能&#xff1a; 1.货物种类一共设有8种&#xff0c;这8种商品通过选择按键进行选择确认&#xff1b; 2.通过数量选择按键确定购买数量&#xff0c;价格规定为1-8…

Spring Boot日志

目录 一、日志概述 1、为什么要学习日志&#xff1f; 2、日志的用途 &#xff08;1&#xff09;系统监控 &#xff08;2&#xff09;数据采集 &#xff08;3&#xff09;日志审计 二、日志使用 1、打印日志 &#xff08;1&#xff09;在程序中得到日志对象 &#xf…

代码随想录训练营Day 27|理论基础、力扣 77. 组合

1.理论基础 题目链接/文章讲解&#xff1a;代码随想录 视频讲解&#xff1a;带你学透回溯算法&#xff08;理论篇&#xff09;| 回溯法精讲&#xff01;_哔哩哔哩_bilibili 来自代码随想录的网站&#xff1a; void backtracking(参数) {if (终止条件) {存放结果;return;}for (…

Linux 服务器配置共享文件夹(NFS)

一、准备三台 linux 服务器 三台服务器: manger:172.16.11.178 ap1:172.16.11.179 ap2:172.16.11.180 /root/serverfiles/ 为共享目录 二、配置步骤 1、在服务端01的机器上安装nfs和rpcbind程序 yum -y install nfs* yum -y install rpcbind* 2、在安装完nfs以及rpcb…