2024.12.29(进程线程实现并发服务器)
作业
多进程多线程并发服务器实现一遍提交。
服务器
#include <myhead.h>
#define PORT 12345
#define IP "192.168.124.123"void *fun(void *fd)
{int newfd = *(int *)fd;char buff[1024];while(1){int res = recv(newfd,buff,sizeof(buff),0);if(res == 0){printf("当前客户端已经退出\n");break;}printf("%s\n",buff);strcat(buff,"作业写完了");send(newfd,buff,sizeof(buff),0);}pthread_exit(NULL);
}
int main(int argc, const char *argv[])
{//创建套接字int oldfd = socket(AF_INET,SOCK_STREAM,0);if(oldfd == -1){perror("socket");return -1;}//绑定struct sockaddr_in server = {.sin_family = AF_INET,.sin_port = htons(PORT),.sin_addr.s_addr = inet_addr(IP)};if(bind(oldfd,(struct sockaddr *)&server,sizeof(server))==-1){perror("bind");return -1;}//监听if(listen(oldfd,22)==-1){perror("listen");return -1;}struct sockaddr_in client;int client_len = sizeof(client);int newfd;pthread_t tid;while(1){//接收新客户端连入请求newfd = accept(oldfd,(struct sockaddr *)&client,&client_len);if(newfd == -1){perror("accept");return -1;}//创建子线程与客户端通话if(pthread_create(&tid,NULL,fun,&newfd)==-1){perror("pthread_create");return -1;}}pthread_join(tid,NULL);close(newfd);close(oldfd);return 0;
}
客户端
#include <myhead.h>
#define PORT 12345
#define IP "192.168.124.123"
int main(int argc, const char *argv[])
{//创建套接字int oldfd = socket(AF_INET,SOCK_STREAM,0);if(oldfd == -1){perror("socket");return -1;}//连接服务器struct sockaddr_in server = {.sin_family = AF_INET,.sin_port = htons(PORT),.sin_addr.s_addr = inet_addr(IP)};if(connect(oldfd,(struct sockaddr *)&server,sizeof(server))==-1){perror("connect");return -1;}//收发消息char buff[1024];while(1){fgets(buff,sizeof(buff),stdin);buff[strlen(buff)-1] = '\0';send(oldfd,buff,strlen(buff),0);if(strcmp(buff,"quit")==0){break;}bzero(buff,sizeof(buff));recv(oldfd,buff,sizeof(buff),0);printf("服务器发来消息:%s\n",buff);}return 0;
}
学习笔记
端口号快速复用函数
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
功能: 获取套接字或者其他层级的属性
参数1:套接字描述符
参数2:要获取的层级
参数3:操作名称每一层级名称都不一样,具体见下表。
参数4:变量的地址(表中的数据类型定义的变量)
参数5:参数4 的大小。
返回值:成功返回0,失败返回-1,并置位错误码
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
功能:设置套接字或者其他层级的属性
参数1:套接字描述符
参数2:要获取的层级
参数3:操作名称每一层级名称都不一样,具体见下表。
参数4:变量的地址(表中的数据类型定义的变量)
参数5:参数4 的大小。
返回值:成功返回0,失败返回-1,并置位错误码
函数使用:
#include <myhead.h>int main(int argc, const char *argv[])
{int oldfd= socket(AF_INET,SOCK_STREAM,0);if(oldfd==-1){return -1;}//获取端口号快速复用的属性(默认关闭)int k;int k_len = sizeof(int);if(getsockopt(oldfd,SOL_SOCKET,SO_REUSEADDR,&k,&k_len)==-1){perror("getsockopt");return -1;}printf("k = %d\n",k);//k=0//设置开启端口号快速复用属性(k!=0即可)k = 2;if(setsockopt(oldfd,SOL_SOCKET,SO_REUSEADDR,&k,sizeof(k))==-1){perror("setsockopt");return -1;}printf("开启端口号快速复用功能\n");return 0;
}
1、循环服务器模型
创建套接字
绑定
监听
循环:
创建新的用于通讯的套接字
收消息
发消息
关闭新的套接字
关闭旧的套接字
代码:
缺点:新客户端要通信,必须使旧的客户端先退出。
#include <myhead.h>
#define IP "192.168.124.34"
#define PORT 6666
int main(int argc, const char *argv[])
{//1、创建套接字//2、绑定//3、监听//4、循环连接不同客户端//5、循环收发信息//1创建套接字 int oldfd = socket(AF_INET,SOCK_STREAM,0);if(-1==oldfd){perror("socket");return -1;}//2、绑定struct sockaddr_in server = {.sin_family = AF_INET,.sin_port = htons(PORT),.sin_addr.s_addr = inet_addr(IP)};if(bind(oldfd,(struct sockaddr *)&server,sizeof(server))==-1){perror("bind");return -1;}//3、监听if(listen(oldfd,20)==-1){perror("listen");return -1;}//4、连接int newfd;struct sockaddr_in client;int client_len = sizeof(client);while(1){if((newfd = accept(oldfd,(struct sockaddr *)&client,&client_len))==-1){perror("accept");return -1;}//5、信息收发char buff[1024];while(1){int res = recv(newfd,buff,sizeof(buff),0);if(res==0){printf("您的客户端已经下线\n");break;}printf("%s\n",buff);strcat(buff,"中午吃啥");send(newfd,buff,sizeof(buff),0);}}close(oldfd);close(newfd);return 0;
}
2、基于TCP并发服务器,目前有多进程和多线程并发。
2.1、多进程并发服务器,父进程只负责处理不同客户端的链接请求,每一个客户端请求连接就创建出一个子进程进行处理通话。
多进程服务器模型
1、子进程在哪创建
2、子进程怎么回收
3、终端打开的文件描述符有限。
1、多进程并发执行
模型:
定义信号处理函数,非阻塞回收僵尸进程。
绑定子进程退出时的信号。
1、创建套接字
2、绑定
3、监听
4、循环接收客户端信息
5、让父进程接收客户端请求并关闭新文件描述符,子进程关闭旧的描述符只负责数据收发。
服务器代码:
#include <myhead.h>
#define IP "192.168.124.34"
#define PORT 8888
void handle(int sss)
{if(sss==SIGCHLD){while(waitpid(-1,NULL,WNOHANG)>0);//非阻塞循环回收子进程资源}printf("回收成功\n");
}
int main(int argc, const char *argv[])
{//1、创建套接字//2、绑定//3、监听//4、父进程连接新的客户端//5、创建子父进程,父进程关闭新的描述符//5、子进程负责数据收发并关闭旧的描述符//1、创建套接字int oldfd = socket(AF_INET,SOCK_STREAM,0);if(-1==oldfd){perror("socket");return -1;}//端口号快速复用int k = 999;if(setsockopt(oldfd,SOL_SOCKET,SO_REUSEADDR,&k,sizeof(k))==-1){perror("setsockopt");return -1;}printf("端口号快速复用成功\n");//2、绑定struct sockaddr_in server = {.sin_family = AF_INET,.sin_port = htons(PORT),.sin_addr.s_addr = inet_addr(IP)};if(bind(oldfd,(struct sockaddr *)&server,sizeof(server))==-1){perror("bind");return -1;}//3、监听if(listen(oldfd,88)==-1){perror("listen");return -1;}//4、父进程连接新的客户端 struct sockaddr_in client;int client_len = sizeof(client);while(1){int newfd = accept(oldfd,(struct sockaddr *)&client,&client_len);if(newfd==-1){perror("accept");return -1;}printf("%s:%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));//输出新客户端信息//5、创建子父进程pid_t pid = fork();char buff[1024];if(pid>0){if(signal(SIGCHLD,handle)==SIG_ERR)//绑定子进程退出时的信号{perror("signal");return -1;}//父进程关闭新的描述符close(newfd);}else if(pid==0){//子进程关闭旧的描述符信息收发close(oldfd);while(1){int res = recv(newfd,buff,sizeof(buff),0);if(res==0){printf("客户端已经退出\n");break;}printf("%s\n",buff);strcat(buff,"今天周日啊");send(newfd,buff,sizeof(buff),0);}exit(EXIT_SUCCESS);//子进程退出}else{perror("fork");return -1;}}close(oldfd);return 0;
}
TCP客户端:
#include <myhead.h>
#define IP "192.168.124.34"
#define SERPORT 8888
int main(int argc, const char *argv[])
{//1、创建套接字//2、绑定(不是必须绑定)//3、连接//4、收发消息int oldfd = socket(AF_INET,SOCK_STREAM,0);if(oldfd==-1){perror("socket");return -1;}
#if 0//绑定固定的IP和端口号(不是必须的)struct sockaddr_in client = {.sin_family =AF_INET,.sin_port = htons(7899),//自定义端口号.sin_addr.s_addr = inet_addr("192.168.124.34")};if(bind(oldfd,(struct sockaddr *)&client,sizeof(client))==-1){perror("bind");return -1;}
#endif//连接服务器struct sockaddr_in server = {.sin_family =AF_INET,.sin_port = htons(SERPORT),//注意端口号需要服务器端口.sin_addr.s_addr = inet_addr(IP)};if(connect(oldfd,(struct sockaddr *)&server,sizeof(server))==-1){perror("connect");return -1;}//收发消息char buff[1024];while(1){fgets(buff,sizeof(buff),stdin);buff[strlen(buff)-1] = '\0';send(oldfd,buff,strlen(buff),0);if(strcmp(buff,"quit")==0)//退出客户端{break;}bzero(buff,sizeof(buff));recv(oldfd,buff,sizeof(buff),0);//阻塞接收服务器消息printf("服务器发来消息:%s\n",buff);}return 0;
}
2、多线程并发服务器
1、大部分的多任务并发执行,我们都选择多线程,而不是多进程,因为多线程资源开销小,而且创建销毁比进程容易。
2、如果客户端过多,需要建立一个线程池,有客户端请求,就从线程池拿出一个线程分配给该客户端。
3、由于线程是提前创建好的,所以响应速度很快。
4、客户端退出后,线程会被销毁,由于线程占用资源较少,销毁也不会占用太多开销。
模型:
线程函数:
收发消息
关闭新描述符
子线程退出
建立原始套接字
绑定主机IP 监听客户端
循环:
accept:获取客户端请求
建立子线程
线程挂起
关闭旧的文件描述符
多线程并发服务器
#include <myhead.h>
#define IP "192.168.124.34"
#define PORT 8888
void *fun(void *fd)
{int newfd = *(int *)fd; char buff[1024];while(1){int res = recv(newfd,buff,sizeof(buff),0);if(res==0){printf("客户端下线\n");break;}printf("%s\n",buff);strcat(buff,"5点放学");send(newfd,buff,sizeof(buff),0);}pthread_exit(NULL);//子线程退出
}int main(int argc, const char *argv[])
{//创建套接字int oldfd = socket(AF_INET,SOCK_STREAM,0);if(oldfd==-1){perror("socket");return -1;}//绑定struct sockaddr_in server = {.sin_family =AF_INET,.sin_port = htons(PORT),.sin_addr.s_addr =inet_addr(IP)};if(bind(oldfd,(struct sockaddr *)&server,sizeof(server))==-1){perror("bind");return -1;}//监听if(listen(oldfd,20)==-1){perror("listen");return -1;}struct sockaddr_in client;int client_len = sizeof(client);int newfd;pthread_t tid;while(1){//接收新客户端连入请求newfd = accept(oldfd,(struct sockaddr *)&client,&client_len);if(newfd==-1){perror("accept");return -1;}//创建子线程与客户端通话if(pthread_create(&tid,NULL,fun,&newfd)==-1){perror("pthread_create");return -1;}}pthread_join(tid,NULL);//回收子线程资源close(newfd);close(oldfd);return 0;
}
多线程客户端:
#include <myhead.h>
#define IP "192.168.124.34"
#define SERPORT 8888
int main(int argc, const char *argv[])
{//1、创建套接字//2、绑定(不是必须绑定)//3、连接//4、收发消息int oldfd = socket(AF_INET,SOCK_STREAM,0);if(oldfd==-1){perror("socket");return -1;}
#if 0//绑定固定的IP和端口号(不是必须的)struct sockaddr_in client = {.sin_family =AF_INET,.sin_port = htons(7899),//自定义端口号.sin_addr.s_addr = inet_addr("192.168.124.34")};if(bind(oldfd,(struct sockaddr *)&client,sizeof(client))==-1){perror("bind");return -1;}
#endif//连接服务器struct sockaddr_in server = {.sin_family =AF_INET,.sin_port = htons(SERPORT),//注意端口号需要服务器端口.sin_addr.s_addr = inet_addr(IP)};if(connect(oldfd,(struct sockaddr *)&server,sizeof(server))==-1){perror("connect");return -1;}//收发消息char buff[1024];while(1){fgets(buff,sizeof(buff),stdin);buff[strlen(buff)-1] = '\0';send(oldfd,buff,strlen(buff),0);if(strcmp(buff,"quit")==0)//退出客户端{break;}bzero(buff,sizeof(buff));recv(oldfd,buff,sizeof(buff),0);//阻塞接收服务器消息printf("服务器发来消息:%s\n",buff);}return 0;
}作业:多进程多线程并发服务器实
思维导图
相关文章:

2024.12.29(进程线程实现并发服务器)
作业 多进程多线程并发服务器实现一遍提交。 服务器 #include <myhead.h> #define PORT 12345 #define IP "192.168.124.123"void *fun(void *fd) {int newfd *(int *)fd;char buff[1024];while(1){int res recv(newfd,buff,sizeof(buff),0);if(res 0){p…...

如何在 Ubuntu 上安装 PyTorch
简介 PyTorch 因其易用性、动态计算图和高效性而日益流行,成为实现深度学习模型的首选。如果你想探索这个工具并学习如何在 Ubuntu 上安装 PyTorch,本指南将对你有所帮助! 在本教程中,我们将引导你完成在 Ubuntu 系统上使用 Pip…...

8-Gin 中间件 --[Gin 框架入门精讲与实战案例] 【文末有测试代码】
路由中间件 Gin 是一个用 Go (Golang) 编写的 HTTP web 框架。它以性能好、中间件支持灵活著称,非常适合用来构建微服务或 RESTful API 服务。下面我将提供三个使用 Gin 的路由中间件的完整示例。 示例 1: 简单的日志记录中间件 这个中间件会在每个请求处理前后打…...

【潜意识Java】深入详细理解分析Java中的toString()方法重写完整笔记总结,超级详细。
目录 一、toString() 方法是啥? (一)默认的 toString() 方法 (二)toString() 方法的作用 二、为啥要重写 toString() 方法? (一)提高代码的可读性 (二)…...

【论文笔记】Contrastive Learning for Sign Language Recognition and Translation
🍎个人主页:小嗷犬的个人主页 🍊个人网站:小嗷犬的技术小站 🥭个人信条:为天地立心,为生民立命,为往圣继绝学,为万世开太平。 基本信息 标题: Contrastive Learning for…...

Gitlab17.7+Jenkins2.4.91实现Fastapi/Django项目持续发布版本详细操作(亲测可用)
一、gitlab设置: 1、进入gitlab选择主页在左侧菜单的下面点击管理员按钮。 2、选择左侧菜单的设置,选择网络,在右侧选择出站请求后选择允许来自webhooks和集成对本地网络的请求 3、webhook设置 进入你自己的项目选择左侧菜单的设置ÿ…...
一起来看--红黑树
【欢迎关注编码小哥,学习更多实用的编程方法和技巧】 红黑树是一种自平衡的二叉搜索树,广泛应用于计算机科学中,尤其是在实现关联数组和集合时。它的设计旨在确保在最坏情况下,基本动态集合操作(如插入、删除和查找&am…...

SpringBoot整合篇 05、Springboot整合Redission
文章目录 前言Redission详细配置步骤pom依赖application.yaml配置类CacheConfigEnvironmentContext RedissionController单测 前言 本篇博客是SpringBoot整合Redission,若文章中出现相关问题,请指出! 所有博客文件目录索引:博客…...

供应链系统设计-供应链中台系统设计(六)- 商品中心概念篇
概述 我们在供应链系统设计-中台系统设计系列(五)- 供应链中台实践概述 中描述了什么是供应链中台,供应链中台主要包含了那些组成部门。包括业务中台、通用中台等概念。为了后续方便大家对于中台有更深入的理解,我会逐一针对中台…...

胡闹厨房练习(三)
ScriptableObject 一、初步了解 1、实质:是一种特殊类型的Unity对象, 2、作用:用于存储大量数据,而不必依附于游戏场景中的某个GameObject。 3、特点: 可以在不增加场景中对象数量的情况下,管理和存储复杂的数据结构、配置信息、游戏状态等。 4、适用:非常适合用来…...
关于ESD(静电放电)等级的划分
关于ESD(静电放电)等级的划分,主要依据不同的测试模型和测试标准。以下是对HBM(人体模型)和CDM(充电器件模型)两种测试模型下ESD等级划分的详细解释: HBM ESD等级划分 HBM ESD等级…...

探究步进电机与输入脉冲的关系
深入了解步进电机 前言一、 步进电机原理二、 细分三、脉冲数总结 前言 主要是探究以下内容: 1、步进电机的步进角。 2、什么是细分。 3、脉冲的计算。 最后再扩展以下STM32定时器的计算方法。 一、 步进电机原理 其实语言描述怎么样都不直观,我更建议…...

基于YOLOV5+Flask安全帽RTSP视频流实时目标检测
1、背景 在现代工业和建筑行业中,安全始终是首要考虑的因素之一。特别是在施工现场,工人佩戴安全帽是确保人身安全的基本要求。然而,人工监督难免会有疏漏,尤其是在大型工地或复杂环境中,确保每个人都佩戴安全帽变得非…...

Windows内置的服务器IIS(Internet Information Services)托管网站
一. 安装IIS 打开控制面板:在开始菜单搜索“控制面板”并打开它。程序和功能:点击“程序”然后选择“程序和功能”。启用或关闭Windows功能:在左侧菜单中选择“启用或关闭Windows功能”。查找并勾选IIS:在弹出的窗口中,…...
虚幻引擎结构之UObject
一. UObject 的介绍 UObject 是虚幻引擎中的核心基础类,所有其他游戏对象和资源类都直接或间接地继承自它。作为虚幻引擎的基石,UObject 提供了多项关键功能,包括内存管理、序列化、反射(introspection)、垃圾回收以及元数据支持。在虚幻引擎中,UObject 类的实例通常被称…...
js的Reflect对象
Reflect 对象是 JavaScript ES6 中引入的一个内建对象,它提供了一系列与对象操作相关的方法。这些方法与 Object 对象上的方法类似,但在行为上有一些差异,并且更加规范和统一。Reflect 对象并不是一个构造函数,不能被 new 操作符调…...
this指向了谁?
看函数在执行的时候是如何调用的, 1 如果这个函数是用普通函数调用模式来进行调用,它内部的this指向了window; 2 如果一个函数在调用的时候是通过对象方法模式来进行调用,则它内部的this就是我们的对象; 3 如果一个函数在调用的时候通过构…...

基于Resnet、LSTM、Shufflenet及CNN网络的Daily_and_Sports_Activities数据集仿真
在深度学习领域,不同的网络结构设计用于解决特定的问题。本文将详细分析四种主流网络结构:卷积神经网络(CNN)、残差网络(ResNet)、长短期记忆网络(LSTM)和洗牌网络(Shuff…...

mac系统vsCode中使用Better Comments在.vue文件里失效
问题:关于Better Comments默认在html、TS、JS中有效,在vue中无效,需要单独进行配置 windows系统可以参考友链Better Comments(注释高亮)在vue文件里失效的问题 关于Better Comments电脑的配置路径: Windows系统&…...

UE5.3 C++ Ceiusm中的POI 制作3DUI 结合坐标转化
一.核心思路WidgetComponent CesiumGloberAnchor 二.先制作POI 创建C Actor来制作,APOI。直接上代码 #pragma once#include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "CesiumGlobeAnchorComponent.h" #includ…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...
如何为服务器生成TLS证书
TLS(Transport Layer Security)证书是确保网络通信安全的重要手段,它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书,可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...
AspectJ 在 Android 中的完整使用指南
一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...
C#学习第29天:表达式树(Expression Trees)
目录 什么是表达式树? 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持: 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...
Kafka主题运维全指南:从基础配置到故障处理
#作者:张桐瑞 文章目录 主题日常管理1. 修改主题分区。2. 修改主题级别参数。3. 变更副本数。4. 修改主题限速。5.主题分区迁移。6. 常见主题错误处理常见错误1:主题删除失败。常见错误2:__consumer_offsets占用太多的磁盘。 主题日常管理 …...

使用SSE解决获取状态不一致问题
使用SSE解决获取状态不一致问题 1. 问题描述2. SSE介绍2.1 SSE 的工作原理2.2 SSE 的事件格式规范2.3 SSE与其他技术对比2.4 SSE 的优缺点 3. 实战代码 1. 问题描述 目前做的一个功能是上传多个文件,这个上传文件是整体功能的一部分,文件在上传的过程中…...
数据库正常,但后端收不到数据原因及解决
从代码和日志来看,后端SQL查询确实返回了数据,但最终user对象却为null。这表明查询结果没有正确映射到User对象上。 在前后端分离,并且ai辅助开发的时候,很容易出现前后端变量名不一致情况,还不报错,只是单…...

【汇编逆向系列】六、函数调用包含多个参数之多个整型-参数压栈顺序,rcx,rdx,r8,r9寄存器
从本章节开始,进入到函数有多个参数的情况,前面几个章节中介绍了整型和浮点型使用了不同的寄存器在进行函数传参,ECX是整型的第一个参数的寄存器,那么多个参数的情况下函数如何传参,下面展开介绍参数为整型时候的几种情…...
IP选择注意事项
IP选择注意事项 MTP、FTP、EFUSE、EMEMORY选择时,需要考虑以下参数,然后确定后选择IP。 容量工作电压范围温度范围擦除、烧写速度/耗时读取所有bit的时间待机功耗擦写、烧写功耗面积所需要的mask layer...