当前位置: 首页 > news >正文

常见的socket函数封装和多进程和多线程实现服务器并发

常见的socket函数封装和多进程和多线程实现服务器并发

  • 1.常见的socket函数封装
  • 2.多进程和多线程实现服务器的并发
    • 2.1多进程服务器
    • 2.2多线程服务器
    • 2.3运行效果

1.常见的socket函数封装

在这里插入图片描述

accept函数或者read函数是阻塞函数,会被信号打断,我们不能让它停止,所以我们应该进行一些封装操作。

//wrap.h#ifndef __WRAP_H_
#define __WRAP_H_
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <strings.h>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

下面是相关函数的实现

//wrap.c#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <strings.h>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;
}

2.多进程和多线程实现服务器的并发

当有多个客户端向服务器发送数据的时候,我们如何去操作,这就涉及到了我们的多线程和多进程开发了,下面看看如何来实现。

2.1多进程服务器

(1)首先我们想如何通过多进程来实现呢?那么我们得想清楚父子进程分别来干啥,我们可以这样,父进程来获取连接

(2)然后子进程来进行通信发送数据给服务端。

(3)最后我们利用信号的方式来回收子进程,防止出现僵尸进程。

/*多进程实现并发,主进程中使用sigaction函数回收子进程*/
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
#include "wrap.h"void sighandler(int sig)
{pid_t wpid;//回收子进程while(1){wpid = waitpid(-1, NULL, WNOHANG);if(wpid <= 0){break;}}
}int main()
{int lfd = Socket(AF_INET, SOCK_STREAM, 0);//设置端口复用int opt = 1;setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));struct sockaddr_in serverAddr;bzero(&serverAddr, sizeof(serverAddr));serverAddr.sin_family = AF_INET;serverAddr.sin_port = htons(8888);serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);Bind(lfd, (struct sockaddr*)&serverAddr, sizeof(serverAddr));Listen(lfd, 128);//将SIGCHLD信号阻塞sigset_t mask;sigemptyset(&mask);sigaddset(&mask, SIGCHLD);sigprocmask(SIG_BLOCK, &mask, NULL);int cfd;pid_t mpid;struct sockaddr_in clientAddr;socklen_t len = sizeof(clientAddr);while(1){cfd = Accept(lfd, (struct sockaddr*)&clientAddr, &len);mpid = fork();if (mpid < 0){perror("fork error:");exit(0);}else if (mpid > 0){close(cfd);//signal(SIGCHLD, sighandler);//注册信号处理函数struct sigaction act;act.sa_handler = sighandler;sigemptyset(&act.sa_mask);act.sa_flags = 0;sigaction(SIGCHLD, &act, NULL);//解除对SIGCHLD信号的阻塞sigprocmask(SIG_UNBLOCK, &mask, NULL);}else if(mpid == 0){//子进程中执行消息收发close(lfd);char buf[1024];int nLen; char cIP[16];while(1){memset(buf, 0, sizeof(buf));nLen = Read(cfd, buf, sizeof(buf));if (nLen <= 0){perror("read error:");break;}printf("%s--%d: %s\n", inet_ntop(AF_INET, &clientAddr.sin_addr.s_addr, cIP, sizeof(cIP)), ntohs(clientAddr.sin_port), buf);strcat(buf, "---recvied");Write(cfd, buf, strlen(buf));}close(cfd);exit(0);  //子进程退出,防止子进程继续创建子进程}}close(lfd);return 0;
}

2.2多线程服务器

接下来就是多线程服务器如何去实现呢?

我们可以参考上面的多进程开发:
(1)首先我们利用主进程来获取连接。

(2)然后利用子线程来和服务器进行通信给服务器发送数据。

(3)最后设置线程分离属性,任务完成后自动回收子线程。

注意:

(1)线程和进程之间是有不同的,线程的文件描述符时共享的,一旦有一个新的连接过来的时候,所有的通信文件描述符cfd都会改变,但是进程时写时拷贝的,所以进程不会出现这种情况。因此在使用线程开发时,我们要分别给他们开辟空间,这里可以用一个结构体,不同线程使用不同的空间

(2)由于线程的文件描述符是共享的,所以我们不可以关闭父线程的通信文件描述符,这样会导致子线程的通信文件描述符全关闭,导致子线程无法正常通信;而进程程会有计数引用,只会是通信文件描述符的引用次数减1,不会直接全部关闭。

下面是代码:

/*多线程实现并发, 解决多个子线程共享cfd存在的问题*/
#include "wrap.h"
#include <pthread.h>#define MAX_NUM 100struct PthreadInfo
{int cfd;  //若为-1表示可用, 大于0表示已被占用pthread_t threadID;struct sockaddr_in clientAddr;
};
//定义结构体数组,不同的线程访问不同的内存
struct PthreadInfo info[MAX_NUM];//线程执行函数
void* mythread(void* arg)
{struct PthreadInfo* p = (struct PthreadInfo*)arg;char buf[1024];int cfd = p->cfd;ssize_t len;while (1){memset(buf, 0, sizeof(buf));len = Read(cfd, buf, sizeof(buf));if (len <= 0){perror("read error:");close(cfd);p->cfd = -1;  //设置为-1表示该位置可用pthread_exit(NULL);}printf("%s\n", buf);strcat(buf, "---recvied");Write(cfd, buf, strlen(buf));}}void init_info()
{//初始化数组,当cfd = -1时表明这块内存空间可以使用for (size_t i = 0; i < MAX_NUM; i++){info[i].cfd = -1;}
}int find_index()
{int i;for(i = 0; i < MAX_NUM; i++){if (info[i].cfd == -1){break;}}if (i == MAX_NUM){return -1;}return i;
}int main()
{int lfd = Socket(AF_INET, SOCK_STREAM, 0);//设置端口复用int opt = 1;setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));struct sockaddr_in serverAddr;bzero(&serverAddr, sizeof(serverAddr));serverAddr.sin_family = AF_INET;serverAddr.sin_port = htons(8888);serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);Bind(lfd, (struct sockaddr*)&serverAddr, sizeof(serverAddr));Listen(lfd, 128);//初始化init_info();int cfd;int ret;int idx;socklen_t len;struct sockaddr_in client;while (1){len = sizeof(client);bzero(&client, len);cfd = Accept(lfd, (struct sockaddr*)&client, &len);//找数组中空闲的位置idx = find_index();if (idx == -1){close(cfd);continue;}//对空闲位置的元素的成员赋值info[idx].cfd = cfd;memset(&info[idx].clientAddr, &client, len);//创建子线程---该子线程完成对数据的收发ret = pthread_create(&info[idx].threadID, NULL, mythread, &info[idx]);if(ret!=0){printf("create thread error:[%s]\n", strerror(ret));exit(-1);}//设置子线程为分离属性pthread_detach(info[idx].threadID);}close(lfd);return 0;
}

我们在写的时候发现当一些进程完成通信以后,关闭文件描述符,我们的空间是无法进行回收的,这样就会大大浪费空间,因此我们可以写一个函数来返回结束通信的空间位置可利用的空间,来使用这块空间。

int find_index()
{int i;for(i = 0; i < MAX_NUM; i++){if (info[i].cfd == -1){break;}}if (i == MAX_NUM){return -1;}return i;
}

2.3运行效果

下面我们看看效果

1.这是连接的第一个客户端,可以看到通信正常
在这里插入图片描述
2.这是连接的第二个客户端,通信也正常

在这里插入图片描述3.我们用命令来看看连接的状态

在这里插入图片描述
可以看到tcp连接是一个双向的可靠连接,我们连接了两个客户端,所以有四个连接,可以看到都处于ESTABLISHESD的状态,可以看出是达到了效果。两个客户端和服务端的通信都是正常的。

相关文章:

常见的socket函数封装和多进程和多线程实现服务器并发

常见的socket函数封装和多进程和多线程实现服务器并发 1.常见的socket函数封装2.多进程和多线程实现服务器的并发2.1多进程服务器2.2多线程服务器2.3运行效果 1.常见的socket函数封装 accept函数或者read函数是阻塞函数&#xff0c;会被信号打断&#xff0c;我们不能让它停止&a…...

Tomcat架构分析

Tomcat的核心组件 Tomcat将请求器和处理器分离&#xff0c;使用多种请求器支持不同的网络协议&#xff0c;而处理器只有一个。从而网络协议和容器解耦。 Tomcat的容器 Host&#xff1a;Tomcat提供多个域名的服务&#xff0c;其将每个域名都视为一个虚拟的主机&#xff0c;在…...

旧项目集成阿里云滑动验证码(web和H5方式)

简述 旧项目集成阿里云滑动验证码(web和H5方式) 适用于servlet和HTML项目,VUE + springboot请看另一篇文档 前情提示 系统: 一说 部分截图、链接等因过期、更换域名、MD语法等可能不显示,可联系反馈(备注好博文地址),谢谢❤带有​​#号、删除线、不操作、不执行​…...

机器人内部传感器阅读梳理及心得-速度传感器-数字式速度传感器

在机器人控制系统中&#xff0c;增量式编码器既可以作为位置传感器测量关节相对位置&#xff0c;又可作为速度传感器测量关节速度。当作为速度传感器时&#xff0c;既可以在模拟量方式下使用&#xff0c;又可以在数字量方式下使用。 模拟式方法 在这种方式下&#xff0c;需要…...

【vue+element ui】大屏自适应中el-select下拉内容在低分辨率下显示不全问题解决

目录 背景 现象 解决方案 背景 最近要把一个1920px*1080px的大屏改成自适应的&#xff1b;最低适配到1028px*720px&#xff1b; 现象 自适应适配改完之后 将电脑屏幕改成1028px*720px分辨率后&#xff0c;下拉显示正常 通过谷歌浏览器设置Toggle device toolbar为1028px*…...

前端架构: 脚手架之多package项目管理和架构

多package项目管理 1 &#xff09;多package项目管理概述 通常来说&#xff0c;当一个项目变大了以后&#xff0c;我们就要对这个项目进行拆分在前端当中&#xff0c;对于项目进行拆分的方式&#xff0c;通常把它称之为javascript包管理需要使用一个工具叫做 npm (Node Packag…...

【C# 多线程】如何停止正在运行中的子线程

如何停止正在运行中的子线程 通过协作式取消模式强制终止线程&#xff08;可能存在资源不能及时释放的泄漏风险&#xff09; 通过协作式取消模式 在线程函数中&#xff0c;你可以周期性地检查一个标志位&#xff0c;以确定是否应该停止线程。该标记位可以是共享变量&#xff0…...

服务器机房安全守护:五大物理安全实践

服务器机房是数字企业的心脏。无论是企业家还是经验丰富的IT专业人员&#xff0c;都知道服务器机房的安全性至关重要。如果没有采用适当的物理安全措施&#xff0c;其服务器很容易受到盗窃、人为破坏和自然灾害的破坏。 在保护服务器机房的领域内&#xff0c;需要采用多方面的…...

spring boot 修复 Spring Framework URL解析不当漏洞(CVE-2024-22243)

漏洞描述 当应用程序使用UriComponentsBuilder来解析外部提供的URL&#xff08;如通过查询参数&#xff09;并对解析的URL的主机执行验证检查时可能容易受到Open重定向攻击和SSRF攻击&#xff0c;导致网络钓鱼和内部网络探测等。 受影响产品或系统 6.1.0 < Spring Framew…...

VR全景HDR拍摄教程

什么是HDR&#xff1f; HDR可以用在哪里&#xff1f; 书面解释&#xff1a;HDR&#xff08;高动态范围 High Dynamic Range&#xff09;摄影&#xff0c;是摄影领域广泛使用的一种技术。 是不是有点懵&#xff1f; 我们来看一个实际的拍摄现场环境&#xff0c;你就懂了 我们…...

hive执行select count(1)返回0

背景&#xff1a; 做数据质量检核任务的时候&#xff0c;有些数据表有数据&#xff0c;直接查hive执行select count(1) from table返回的值一直是0 问题原因&#xff1a; hive通过select count(1)或者select count(*) 查询的是元数据库里面的rownum,如果数据表数据是通过load、…...

3D Gaussian splatting 协方差矩阵 球谐函数 简单理解

3D Gaussian splatting 是一种图形和视觉处理技术&#xff0c;常用于体积渲染、点云渲染和其他应用中&#xff0c;以便对数据进行平滑或重建。在这个上下文中&#xff0c;高斯分布&#xff08;或高斯“splat”&#xff09;用于表示单个数据点&#xff08;如一个点云中的点&…...

代码随想录算法刷题训练营day27:LeetCode(39)组合总和、LeetCode(40)组合总和 II、LeetCode(131)分割回文串

代码随想录算法刷题训练营day27&#xff1a;LeetCode(39)组合总和、LeetCode(40)组合总和 II、LeetCode(131)分割回文串 LeetCode(39)组合总和 题目 代码 import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List;clas…...

docker 容器修改端口和目录映射

一、容器修改端口映射 一般在运行容器时&#xff0c;我们都会通过参数 -p&#xff08;使用大写的-P参数则会随机选择宿主机的一个端口进行映射&#xff09;来指定宿主机和容器端口的映射&#xff0c;例如 docker run -it -d --name [container-name] -p 8088:80 [image-name]…...

echarts鼠标事件

鼠标事件支持方法 ECharts 支持常规的鼠标事件类型&#xff0c;包括 ‘click’、 ‘dblclick’、 ‘mousedown’、 ‘mousemove’、 ‘mouseup’、 ‘mouseover’、 ‘mouseout’、 ‘globalout’、 ‘contextmenu’ 事件 简单实例 // 基于准备好的dom&#xff0c;初始化EChar…...

【北京迅为】《iTOP-3588开发板网络环境配置手册》第2章 电脑、开发板直连交换机或路由器

RK3588是一款低功耗、高性能的处理器&#xff0c;适用于基于arm的PC和Edge计算设备、个人移动互联网设备等数字多媒体应用&#xff0c;RK3588支持8K视频编解码&#xff0c;内置GPU可以完全兼容OpenGLES 1.1、2.0和3.2。RK3588引入了新一代完全基于硬件的最大4800万像素ISP&…...

6.5 共享数据

本节介绍Android的四大组件之一ContentProvider的基本概念和常见用法&#xff1a;首先说明如何使用内容提供器封装内部数据的外部访问接口&#xff0c;然后阐述如何使用内容解析器通过外部接口操作内部数据&#xff0c;最后叙述如何利用内容解析器读写联系人信息&#xff0c;以…...

SpringBoot之Session新增、删除、获取配置与使用

SpringBoot之Session新增、删除、获取配置与使用 文章目录 SpringBoot之Session新增、删除、获取配置与使用1. SpringBoot版本2. 定义增删查Session的类3. 定义Session的监听器4. 使用 自定义根据sessionId进行session的新增、删除、获取操作 1. SpringBoot版本 <parent>…...

Hive UDF 札记

低版本的udf就不说了&#xff0c;太老了&#xff0c;说现在主流的。 1&#xff1a;initialize 方法的进一步理解&#xff1a; 在Apache Hive中&#xff0c;用户自定义函数&#xff08;UDF&#xff09;的initialize方法是一个可选的方法&#xff0c;它属于Hive UDF的生命周期…...

npm已经配置淘宝源仍然无法使用

使用npm命令安装Taro框架的时候&#xff0c;尽管已经设置淘宝源但是仍然无法下载&#xff0c;提示错误 >npm ERR! code CERT_HAS_EXPIRED npm ERR! errno CERT_HAS_EXPIRED npm ERR! request to https://registry.npm.taobao.org/cnpm failed, reason: certificate h…...

设计模式和设计原则回顾

设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...

三体问题详解

从物理学角度&#xff0c;三体问题之所以不稳定&#xff0c;是因为三个天体在万有引力作用下相互作用&#xff0c;形成一个非线性耦合系统。我们可以从牛顿经典力学出发&#xff0c;列出具体的运动方程&#xff0c;并说明为何这个系统本质上是混沌的&#xff0c;无法得到一般解…...

3-11单元格区域边界定位(End属性)学习笔记

返回一个Range 对象&#xff0c;只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意&#xff1a;它移动的位置必须是相连的有内容的单元格…...

[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.

ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #&#xff1a…...

MinIO Docker 部署:仅开放一个端口

MinIO Docker 部署:仅开放一个端口 在实际的服务器部署中,出于安全和管理的考虑,我们可能只能开放一个端口。MinIO 是一个高性能的对象存储服务,支持 Docker 部署,但默认情况下它需要两个端口:一个是 API 端口(用于存储和访问数据),另一个是控制台端口(用于管理界面…...

Visual Studio Code 扩展

Visual Studio Code 扩展 change-case 大小写转换EmmyLua for VSCode 调试插件Bookmarks 书签 change-case 大小写转换 https://marketplace.visualstudio.com/items?itemNamewmaurer.change-case 选中单词后&#xff0c;命令 changeCase.commands 可预览转换效果 EmmyLua…...

用鸿蒙HarmonyOS5实现国际象棋小游戏的过程

下面是一个基于鸿蒙OS (HarmonyOS) 的国际象棋小游戏的完整实现代码&#xff0c;使用Java语言和鸿蒙的Ability框架。 1. 项目结构 /src/main/java/com/example/chess/├── MainAbilitySlice.java // 主界面逻辑├── ChessView.java // 游戏视图和逻辑├── …...

智能体革命:企业如何构建自主决策的AI代理?

OpenAI智能代理构建实用指南详解 随着大型语言模型&#xff08;LLM&#xff09;在推理、多模态理解和工具调用能力上的进步&#xff0c;智能代理&#xff08;Agents&#xff09;成为自动化领域的新突破。与传统软件仅帮助用户自动化流程不同&#xff0c;智能代理能够自主执行工…...

SE(Secure Element)加密芯片与MCU协同工作的典型流程

以下是SE&#xff08;Secure Element&#xff09;加密芯片与MCU协同工作的典型流程&#xff0c;综合安全认证、数据保护及防篡改机制&#xff1a; 一、基础认证流程&#xff08;参数保护方案&#xff09; 密钥预置‌ SE芯片与MCU分别预置相同的3DES密钥&#xff08;Key1、Key2…...

MySQL 数据库深度剖析:事务、SQL 优化、索引与 Buffer Pool

在当今数据驱动的时代&#xff0c;数据库作为数据存储与管理的核心&#xff0c;其性能与可靠性至关重要。MySQL 作为一款广泛使用的开源数据库&#xff0c;在众多应用场景中发挥着关键作用。在这篇博客中&#xff0c;我将围绕 MySQL 数据库的核心知识展开&#xff0c;涵盖事务及…...