嵌入式学习记录5.20(TCP并发服务器)

目录

一. TCP并发服务器

二 .多进程实现TCP并发服务器

2.1流程框架

2.2具体实现代码

三. 多线程实现并发服务器

3.1流程框架

3.2具体实现

一. TCP并发服务器

1> 由于循环服务器使用时,只能等到上一个客户端处理结束后,才能处理下一个客户端

2> 原因是:accept函数是阻塞函数,而数据收发也是阻塞任务,这两个任务目前是顺序执行,当一个任务阻塞时,另一个任务只能等待

3> 为了解决上述顺序执行的多个阻塞任务,让多个阻塞任务可以并发执行,我们可以引入多进程或多线程实现多任务并发执行

4> 多进行实现原理:父进程可以用于接受客户端的连接请求,并创建出一个子进程用于通信

子进程只负责完成跟客户端的通信

5> 多线程实现原理:主线程可以用于接受客户端的连接请求,并创建出一个分支线程用于通信

分支线程只负责完成跟客户端的通信

二 .多进程实现TCP并发服务器

2.1流程框架

//定义信号处理函数
void handler(int signo)
{if(signo == SIGCHLD){while(waitpid(-1, NULL, WNOHANG) != 0);    }
}//将SIGCHLD信号绑定到信号处理函数中
signal(SIGCHLD, handler);sfd = socket();           //创建用于通信的套接字文件描述符
bind();                      //绑定ip地址和端口号
listen();                 //将套接字设置成被动监听状态while(1)
{newfd = accept();             //阻塞等待客户端连接请求,并为其创建一个新的用于通信的套接字问津描述符pid = fork();             //创建子进程用于处理客户端if(pid > 0){//父进程close(newfd);    }elseif(pid == 0){//跟当前客户端进行通信close(sfd);           //关闭sfdrecv();              //阻塞读取消息send();              //发送消息close(newfd);                //关闭套接字exit();                 //退出进程}
}
close(sfd);                      //关闭监听

2.2具体实现代码

#include<myhead.h>
#define SER_PORT 8888              //服务器端口号
#define SER_IP "192.168.125.113"   //服务器ip地址//定义信号处理函数
void handler(int signo)
{//判断要处理的信号if(signo == SIGCHLD){while(waitpid(-1, NULL, WNOHANG) != 0);}
}int main(int argc, const char *argv[])
{//将子进程的SIGCHLD(17)信号//当子进程退出时,会向其父进程发送该信号if(signal(SIGCHLD, handler) == SIG_ERR){perror("signal error");return -1;}//1、为通信创建一个端点int sfd = socket(AF_INET, SOCK_STREAM, 0);//参数1:说明使用的是ipv4通信域//参数2:说明使用的是TCP面向连接的通信方式//参数3:由于参数2中已经指定通信方式,填0即可if(sfd == -1){perror("socket error");return -1;}printf("socket success sfd = %d\n", sfd);   //3//调用端口号快速重用函数int reuse = 1;if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1){perror("setsockopt error");return -1;}//2、绑定ip和端口号//2.1 准备地址信息结构体struct sockaddr_in sin;sin.sin_family = AF_INET;    //通信域sin.sin_port = htons(SER_PORT);    //端口号sin.sin_addr.s_addr = inet_addr(SER_IP);  //ip地址//2.2 绑定工作if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) ==-1){perror("bind error");return -1;}printf("bind success\n");//3、将套接字设置成被动监听状态if(listen(sfd, 128)==-1){perror("listen error");return -1;}printf("listen success\n");//4、阻塞等待客户端的连接//4.1 定义用于接受客户端信息的容器struct sockaddr_in cin;socklen_t addrlen = sizeof(cin);int newfd = -1;             //客户端套接字变量while(1){   //父进程newfd = accept(sfd, (struct sockaddr*)&cin, &addrlen);if(newfd == -1){perror("accept error");return -1;}printf("[%s:%d]:发来连接请求\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port));pid_t pid = fork();          //创建子进程if(pid > 0){//父进程体//关闭newfdclose(newfd);}else if(pid == 0){//关闭sfdclose(sfd);//5、与客户端进行相互通信char rbuf[128] = "";            //读取消息内容的容器while(1){//清空容器bzero(rbuf, sizeof(rbuf));//从套接字中读取数据//int res = read(newfd, rbuf, sizeof(rbuf));int res = recv(newfd, rbuf, sizeof(rbuf), 0);if(res == 0){printf("客户端已经下线\n");break;}//将读取的消息展示出来printf("[%s:%d]:%s\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), rbuf);//将收到的消息处理一下,回复给客户端strcat(rbuf, "*_*");//讲消息发送给客户端//write(newfd, rbuf, strlen(rbuf));send(newfd, rbuf, strlen(rbuf), 0);printf("发送成功\n");}//6、关闭套接字close(newfd);//退出子进程exit(EXIT_SUCCESS);}else{perror("fork error");return -1;}}close(sfd);return 0;
}

三.多线程实现并发服务器

3.1流程框架


//定义线程体函数
void *deal_cli_msg(void *arg)
{//跟当前客户端进行通信recv();              //阻塞读取消息send();              //发送消息close(newfd);                //关闭套接字pthread_exit(NULL);            //退出线程
}sfd = socket();           //创建用于通信的套接字文件描述符
bind();                      //绑定ip地址和端口号
listen();                 //将套接字设置成被动监听状态while(1)
{newfd = accept();             //阻塞等待客户端连接请求,并为其创建一个新的用于通信的套接字问津描述符pthread_create(&tid, NULL, deal_cli_msg, &info);         //创建分支线程用于跟客户端进行通信pthread_detach(tid);                      //将线程设置成分离态
}
close(sfd);                      //关闭监听

3.2具体实现代码

#include<myhead.h>
#define SER_PORT 8888              //服务器端口号
#define SER_IP "192.168.125.113"   //服务器ip地址//定义一个结构体类型,用于向线程体函数传递参数
struct Info
{int newfd; struct sockaddr_in cin;
};//定义线程体函数
void * deal_cli_msg(void *arg)
{//解析传递进来的数据int newfd = ((struct Info*)arg)->newfd;struct sockaddr_in cin = ((struct Info*)arg)->cin;//5、与客户端进行相互通信char rbuf[128] = "";            //读取消息内容的容器while(1){//清空容器bzero(rbuf, sizeof(rbuf));//从套接字中读取数据//int res = read(newfd, rbuf, sizeof(rbuf));int res = recv(newfd, rbuf, sizeof(rbuf), 0);if(res == 0){printf("客户端已经下线\n");break;}//将读取的消息展示出来printf("[%s:%d]:%s\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), rbuf);//将收到的消息处理一下,回复给客户端strcat(rbuf, "*_*");//讲消息发送给客户端//write(newfd, rbuf, strlen(rbuf));send(newfd, rbuf, strlen(rbuf), 0);printf("发送成功\n");}//6、关闭套接字close(newfd);//退出线程pthread_exit(NULL);}int main(int argc, const char *argv[])
{//1、为通信创建一个端点int sfd = socket(AF_INET, SOCK_STREAM, 0);//参数1:说明使用的是ipv4通信域//参数2:说明使用的是TCP面向连接的通信方式//参数3:由于参数2中已经指定通信方式,填0即可if(sfd == -1){perror("socket error");return -1;}printf("socket success sfd = %d\n", sfd);   //3//2、绑定ip和端口号//2.1 准备地址信息结构体struct sockaddr_in sin;sin.sin_family = AF_INET;    //通信域sin.sin_port = htons(SER_PORT);    //端口号sin.sin_addr.s_addr = inet_addr(SER_IP);  //ip地址//2.2 绑定工作if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) ==-1){perror("bind error");return -1;}printf("bind success\n");//3、将套接字设置成被动监听状态if(listen(sfd, 128)==-1){perror("listen error");return -1;}printf("listen success\n");//4、阻塞等待客户端的连接//4.1 定义用于接受客户端信息的容器struct sockaddr_in cin;socklen_t addrlen = sizeof(cin);while(1){//accept函数会预选一个当前未分配的最小的文件描述符//即使在阻塞过程中,有更小的文件描述符产生,本次操作的文件描述符也不会更改了int newfd = accept(sfd, (struct sockaddr*)&cin, &addrlen);if(newfd == -1){perror("accept error");return -1;}printf("[%s:%d]:发来连接请求, newfd = %d\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd);//定义要传递数据的结构体变量struct Info buf = {newfd, cin};//创建分支线程,用于通信pthread_t tid = -1;if(pthread_create(&tid, NULL, deal_cli_msg, &buf) == -1){printf("pthread_create error\n");return -1;}//将线程设置成分离态pthread_detach(tid);}close(sfd);return 0;
}

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

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

相关文章

22个C语言小白常见问题总结

一.语言使用错误 在打代码的过程中&#xff0c;经常需要在中文与英文中进行转换&#xff0c;因此常出现一些符号一不小心就用错&#xff0c;用成中文。例如&#xff1a;“&#xff1b;”中文中的分号占用了两个字节&#xff0c;而英文中“;”分号只占用一个字节。编译器只能识…

Scala的简单学习一

一 相关知识 1.1 scala的安装 1.在idea中导入依赖&#xff0c;并在Idea下载scala插件 1.2 scala基础知识点 1.scala代码中一行语句的结束是以换行符为标准&#xff0c;可以不用写分号 2.class是一个普通的类&#xff0c;object相当于一个单例对象&#xff0c;object类中的…

OpenStack配置 之 不同cpu迁移虚拟机

介绍 OpenStack是一个开源的云计算管理平台项目&#xff0c;是一系列软件开源项目的组合。 OpenStack由NASA&#xff08;美国国家航空航天局&#xff09;和Rackspace合作研发并发起&#xff0c;以Apache许可证&#xff08;Apache软件基金会发布的一个自由软件许可证&#xff…

《Google 软件工程》读书笔记

1. 写在前面 在图书馆瞎逛&#xff0c;偶然瞄见一本《Google 软件工程》Titus Winters, Tom Manshreck, Hyrum Wright 著。主要是在这一排的书架上就这本书看着挺新的&#xff08;不知道为什么有一种喜欢看新书的情节&#xff09;&#xff0c;而且最近被领导老批评&#xff0c;…

Python 一个简单的用Canny算法进行边缘检测程序

以下是一个简单的边缘检测程序&#xff0c;使用Python和OpenCV库来实现。这个程序将加载一张图像&#xff0c;对其进行灰度化处理&#xff0c;并使用Canny边缘检测算法来检测图像中的边缘。请确保安装了OpenCV库&#xff08;可以使用pip install opencv-python进行安装&#xf…

Vue的router.addRoutes不起作用

Vue的router.addRoutes()不起作用解决方案 最近在学习制作后台管理系统的时候&#xff0c;涉及到了权限&#xff0c;在通过后台获取到数据后使用router.addRoutes()时不起作用。 最终发现左侧菜单组件中的路由是根据this.$router.options.routes来渲染的&#xff0c;最终使用…

sw套合样条曲线

套合样条曲线,可以变成一条曲线,然后可以进行分段

UOS1060e分离ssh与sftp服务

文章目录 原理一、sftp 用户与目录二、ssh 和 sftp 服务分离三、启动与停止四、验证 原理 SFTP是SSH的一部分&#xff0c;SFTP没有单独的守护进程&#xff0c;它必须使用SSHD守护进程&#xff08;端口号默认是22&#xff09;来完成相应的连接操作。 通过新建另一个‘sshd’进程…

rclone迁移对象存储之间的数据

1 概述 rclone是一款文件复制工具&#xff0c;既可以用于在linux主机之间复制文件&#xff0c;也可以在对象存储之间复制文件。 rclone的官网为&#xff1a; https://rclone.orgrlcone关于对象存储的官方文档为&#xff1a; https://rclone.org/s32 安装 2.1 yum安装 yum …

使用Spring Boot和Screw轻松生成数据库设计文档

目录 引言 准备工作 项目初始化 引入依赖 配置数据库连接 集成Screw生成文档 基本配置 生成数据库文档 实战操作 示例项目 生成效果 结论 常见问题 延伸阅读 引言 数据库设计文档是项目技术文档的重要组成部分。它不仅有助于开发人员理解数据库结构&#xff0c…

PY32F系列32位单片机 特殊引脚复用说明

一 、 PY32F030 系 列 &#xff0c; 包 括 PY32F030 、PY32F003、 PY32F002A、 XL32F003 的特殊引脚使用说明。 1、晶振引脚的复用&#xff0c;使用 HSE/LSE 引脚作为 GPIO 功能引脚时&#xff0c;只要没有配置相关功能的情况下&#xff0c;正常配置 GPIO 功能即可。 其中&am…

SpringBoot3.x 整合 Spring AI

Spring AI 已经发布了一段时间&#xff0c;虽然推出的时候就被人说只是一个套了 API 的壳&#xff0c;但是作为 Spring 生态的一个开源项目&#xff0c;用它来结合到现有业务系统中还是一个比较好的方案&#xff0c;毕竟像笔者当初为了接入 OpenAI 的 API&#xff0c;还专门学了…

Pydantic与Sqlalchemy数据模型的枚举类型实践

说明&#xff1a; 乍看时以为很容易的东西&#xff0c;摸索实践后才知道窍门实战中我是用Tinyint / Interger 作为保存 enum对象数值的字段类型&#xff0c; 而不是用数据库中提供的ENUM类型字段&#xff0c;因为有更好的扩展性&#xff0c;性能&#xff0c;节省空间作为使用理…

Git 在Windows上添加文件换行相关设置

Windows使用Git时&#xff0c;如果在提交时希望换行符不变&#xff08;CRLF换行不转换为LF&#xff09;&#xff0c;core.autocrlf选项可以按照如下设置。 时保留CRLF换行符&#xff0c;检出时转换为LF&#xff0c;core.autocrlf为true&#xff1a; git config --global core.…

pcd点云江湖之处处碰壁:点云文件pcd加载02

江湖好汉&#xff0c;休走&#xff0c;废了半天力气把threejs自带的代码搬迁到自己项目中了&#xff0c;高高兴兴给领导看。领导一句话&#xff0c;顿时无奈&#xff1a;领导曰&#xff1a;点云单色太丑&#xff0c;能不能按照分类展示&#xff1f; 一句话难道英雄好汉&#xf…

#P0564. 数组元素查找升级版

问题描述 给你 n 个数&#xff0c;再给你一个数 k&#xff0c;查找 k 在这 n 个数中第一次出现的位置&#xff08;从 0 开始计数&#xff09;&#xff0c;不存在输出 No。 输入 多组测试数据&#xff0c;对于每组测试数据&#xff1a; 第一行输入一个整数 n (1 ≤ n ≤ 100…

深入理解Vue 3中的v-if和v-for指令

Vue.js是一款流行的JavaScript框架&#xff0c;其简洁的语法和强大的功能使得前端开发变得更加高效和愉快。在Vue 3中&#xff0c;我们看到了许多新的特性和改进&#xff0c;其中包括了两个最常用的指令之一&#xff1a;v-if和v-for。 1. v-if指令 v-if指令是Vue中最常用的条件…

eclipse配置JDK和Tomcat

eclipse配置JDK jdk配置 配置JDK&#xff1a; 首先&#xff0c;确保JDK已经安装并配置了环境变量。这包括设置JAVA_HOME环境变量&#xff0c;指向JDK的安装目录&#xff0c;以及更新CLASSPATH和PATH环境变量以包含JDK的bin目录。 在Eclipse中&#xff0c;通过Window > Pre…

【Verilog编程题】

20240514 20240515 20240516 题目时序有问题&#xff0c;valid_b在第六位数据的同时拉高&#xff0c;而不是在下一个时钟 20240517 module valid_ready( input clk , input rst_n , input [7:0] data_in , input valid_a , input ready_b , output ready_a , output re…

使用 PyTorch 和 Pandas 进行 Kaggle 房价预测

文章目录 1、环境设置2、数据下载3、数据预处理4、模型构建5、训练和验证6、训练模型并生成预测结果7、完整代码 在本篇博文中&#xff0c;我们将探索如何使用 PyTorch 和 Pandas 库&#xff0c;构建一个用于 Kaggle 房价预测的模型。我们将详细讨论数据加载、预处理、模型构建…