linux网络编程-多进程实现TCP并发服务器
服务端流程步骤
socket函数创建监听套接字lfd
bind函数将监听套接字绑定ip和端口
listen函数设置服务器为被动监听状态,同时创建一条未完成连接队列(没走完tcp三次握手流程的连接),和一条已完成连接队列(已完成tcp三次握手的连接)
accept函数循环的从已完成连接队列中提取连接,并返回一个新的套接字cfd跟客户端进行通信
fork函数创建一个子进程,让子进程与客户端进行通信
子进程:read函数循环的从r缓冲区读取客户端发送的数据,write函数将要发送的数据写入w缓冲区
close函数关闭套接字
客户端流程步骤
socket函数创建套接字
connect函数连接服务器
write函数将要发送的数据写入w缓冲区,read函数从r缓冲区读取服务器发送给客户端的数据
close函数关闭套接字
客户端跟服务器通信流程图

相关函数
int socket(int domain, int type, int protocol);
功能:创建一个用于网络通信的套接字文件描述符
参数:domain:协议族(AF_INET:ipv4,AF_INET6:ipv6,等等)
type:套接字类型(SOCK_DGRAM:udp,SOCK_STREAM:tcp,等等)
protocol:用于制定某个协议的特定类型,即type类型中的某个类型,通常不用管它,设置 为 0
返回值:成功则返回socket套接字描述符, 失败返回-1,并设置errno
头文件:#include <sys/socket.h>
int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
功能:将sockfd绑定ip和端口
参数:sockfd:套接字
my_addr:存放有协议,ip,端口的结构体信息
addrlen:my_addr结构体大小
返回值:成功返回0,失败返回-1,并设置errno
头文件:#include <sys/types.h>
#include <sys/socket.h>
int listen(int s, int backlog);
功能:让服务器处于被动监听状态,同时创建了一条未完成三次握手的连接队列和一条已经完成三次握 手的连接队列
参数:s:套接字
backlog:支持未完成连接和已完成连接之和的最大值,一般设置128
返回值:成功返回0,失败返回-1,并设置errno
头文件:#include <sys/socket.h>
int accept(int s, struct sockaddr *addr, socklen_t *addrlen);
功能:从已完成连接队列中提取客户端连接
参数:s:套接字
addr:存放成功连接的客户端的ip,端口等信息结构体
addrlen:存放addr结构体大小的变量地址
返回值:成功则返回一个非负整数标识这个连接套接字,是否返回-1
头文件:#include <sys/types.h>
#include <sys/socket.h>
ssize_t read(int fd, void *buf, size_t count);
功能:从文件描述符 fd 中读取 count 字节的数据并放入从 buf 开始的缓冲区中
参数:fd:文件描述符
buf:缓冲区
count:读count字节
返回值:成功时返回读取到的字节数,失败返回-1,并设置errno
头文件:#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
功能:向文件描述符fd所引用的文件中写入从buf开始的缓冲区中count字节的数据
参数:fd:文件描述符
buf:缓冲区
count:写count字节
返回值:功时返回所写入的字节数,失败返回-1,并设置errno
头文件:#include <unistd.h>
int close(int fd);
功能:关闭 一个文件描述符
参数:fd:要关闭的文件描述符
返回值:成功返回0,失败返回-1,并设置errno
头文件:#include <unistd.h>
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
功能:连接服务器
参数:sockfd:套接字
addr:服务器的ip,端口等结构体信息
addrlen:addr结构体大小
返回值:成功返回0,失败返回-1,并设置errno
头文件:#include <sys/socket.h>
pid_t fork(void);
功能:创建一个子进程
参数:无
返回值:父进程返回子进程的进程id,子进程返回0
头文件:#include <unistd.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
功能:设置信号集屏蔽字
参数:how:SIG_BLOCK (mask |= set),SIG_UNBLOCK (mask &= ~set),SIG_SETMASK (mask = set)
set:信号集
oldset:旧的信号集
返回值:成功返回0,失败返回-1,并设置errno
头文件:#include <signal.h>
pid_t waitpid(pid_t pid, int *status, int options);
功能:回收已结束的子进程资源
参数:pid:pid<-1 等待进程组识别码为pid绝对值的任何子进程
pid=-1 等待任何子进程, 相当于wait()
pid=0 等待进程组识别码与目前进程相同的任何子进程
pid>0 等待任何子进程识别码为pid的子进程
status:子进程的结束状态值
options:WNOHANG 如果没有任何已经结束的子进程则马上返回, 不予以等待
WUNTRACED 如果子进程进入暂停执行情况则马上返回, 但结束状态不予以理会
返回值:成功返回子进程pid,失败返回-1
头文件: #include <sys/types.h>
#include <sys/wait.h>
服务端代码
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>#define BUF_SIZE 256void do_sigchld(int signo, siginfo_t *siginfo, void *p)
{int status;pid_t pid;while((pid = waitpid(0, &status, WNOHANG)) > 0) //0:回收跟调用进程同组的子进程的资源,WNOHANG:不阻塞{if (WIFEXITED(status))printf("pid = %d, parent pid = %d, child pid = %d, exit status %d\n", pid, getpid(), siginfo->si_pid, WEXITSTATUS(status));else if (WEXITSTATUS(status))printf("pid = %d, parent pid = %d, child pid = %d, exit by signal %d\n ", pid, getpid(), siginfo->si_pid, WIFSIGNALED(status));}
}void sys_err(const char *str, int err)
{perror(str);exit(err);
}int main(int argc, char *argv[])
{if (argc < 2){printf("%s port\n", argv[0]);exit(1);}//创建流式套接字int lfd = socket(AF_INET, SOCK_STREAM, 0);if (lfd < 0)sys_err("socket", 1);//绑定ip端口 struct sockaddr_in server_addr;server_addr.sin_family = AF_INET;server_addr.sin_port = htons((unsigned short)atoi(argv[1]));server_addr.sin_addr.s_addr = 0; //0表示将本机所有ip都绑定上int ret = bind(lfd, (struct sockaddr *)&server_addr, sizeof(server_addr));if (ret < 0)sys_err("bind", 1);//监听ret = listen(lfd, 128);if (ret < 0)sys_err("listen", 1);//阻塞SIGCHLD信号sigset_t set;sigemptyset(&set);sigaddset(&set, SIGCHLD);sigprocmask(SIG_BLOCK, &set, NULL);int block = 1;//循环提取int cfd;pid_t pid;struct sockaddr_in client_addr; socklen_t len = sizeof(client_addr);char buf[BUF_SIZE];ssize_t size;char ip[INET_ADDRSTRLEN] = "";while(1){cfd = accept(lfd, (struct sockaddr *)&client_addr, &len);if (cfd < 0){if (errno == EINTR)continue;sys_err("accept", 1);}printf("client ip = %s, port = %d connect success\n", inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, ip, sizeof(ip)), ntohs(client_addr.sin_port));pid = fork();if (pid == 0){//in childclose(lfd); //关闭不用了的监听套接字//解除阻塞SIGCHLD信号sigprocmask(SIG_UNBLOCK, &set, NULL);block = 0;while(1){memset(buf, 0, sizeof(buf));size = read(cfd, buf, sizeof(buf));if (size == 0) //客户端断开连接{printf("client close\n");break;}printf("%s\n", buf);write(cfd, buf, size);}break;}else if (pid > 0){//in parentclose(cfd); //关闭不用了的跟客户端通讯的套接字if (1 == block){//先捕捉struct sigaction sa;sa.sa_sigaction = do_sigchld;sigemptyset(&sa.sa_mask);sa.sa_flags = SA_SIGINFO;sigaction(SIGCHLD, &sa, NULL);//后解除阻塞sigprocmask(SIG_UNBLOCK, &set, NULL);block = 0;}}elsesys_err("fork", 1);}//关闭套接字if (pid == 0)close(cfd);else if (pid > 0)close(lfd);return 0;
}
客户端代码
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>int main(int argc, char *argv[])
{if (argc < 3){printf("%s sever_ip server_port\n", argv[0]);exit(1);}//创建流式套接字int fd = socket(AF_INET, SOCK_STREAM, 0);if (fd < 0){perror("socket");exit(1);}//连接服务器struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons((unsigned short)atoi(argv[2]));addr.sin_addr.s_addr = inet_addr(argv[1]);int ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));printf("connect ret = %d\n", ret);if (ret < 0){perror("connect");exit(1);}//读写数据char buf_w[128] = "";char buf_r[128] = "";ssize_t size_r = 0;while(1){fgets(buf_w, sizeof(buf_w), stdin);buf_w[strlen(buf_w) - 1] = 0;write(fd, buf_w, strlen(buf_w));size_r = read(fd, buf_r, sizeof(buf_r));if (size_r == 0) //服务器断开break;elseprintf("%s\n", buf_r);memset(buf_w, 0, sizeof(buf_w));memset(buf_r, 0, sizeof(buf_r));}//关闭套接字close(fd);return 0;
}
结果
3个客户端连接服务器时
服务端

客户端

ps aux 可以看到有4个服务器进程(其中一个是主进程,用于监听处理新的连接,其他3个子进程用于跟客户端进行通信)

回射服务信息截图
客户端1:

客户端2:

客户端3:

服务器收到进行打印:

3个客户端都退出时
服务端收到客户端退出,并回收跟客户端进行通信的3个子进程的资源

ps aux 查看可以看到只剩主进程,用于监听处理新的连接,其他3个子进程都已结束

相关文章:

linux网络编程-多进程实现TCP并发服务器
服务端流程步骤socket函数创建监听套接字lfdbind函数将监听套接字绑定ip和端口listen函数设置服务器为被动监听状态,同时创建一条未完成连接队列(没走完tcp三次握手流程的连接),和一条已完成连接队列(已完成tcp三次握手…...

C语言的学习小结——数组
一、一维数组的创建与初始化 1、格式: type_t arr_name[const_n];//type_t 是指数组的元素类型 //const_n 是一个常量表达式,用来指定数组的大小 注: 数组是使用下标来访问的,下标从0开始。 数组的大小可以通过计算得到&…...

HTB-Photobomb
HTB-Photobomb信息收集开机提权对于问题的思考信息收集 端口扫描 目标首页 有一个http Authorization 目录扫描 在查看源码的时候发现了一个js文件。 并且发现了访问不存在的目录会出现错误提示。 通过搜索得知 Sinatra 是一个基于 Ruby 语言的 DSL(领域…...

【LSTM】2 多因素单步骤预测
基于时间序列的预测,一定要明白它的原理,不是工作原理,而是工程落地原因。 基于时间序列,以已知回归未知----这两句话是分量很重的。 多因素单步单输出组合 时间序列:t1 是 特征 1,2,3 预测t2 的回归值41 多因素单步多…...

ChatGPT从下游应用“火”到了上游芯片厂,国内谁将受益?
因库存陷入低迷周期的半导体市场近日因ChatGPT的火热而重新受到外界关注。 原文链接:ChatGPT从下游应用“火”到了上游芯片厂,国内谁将受益? 由于ChatGPT属于生成式AI,被誉为“AI芯片”第一股的英伟达应声而涨。2月13日收盘&#…...
算法单调栈—Java版
单调栈 概念:维护栈中元素的单调性,单调增或者单调减。 什么时候用? 要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置。单调栈的本质是空间换时间,在遍历的过程中需要用一个栈来记录右边第一个比当前元素高的元…...

在Linux中进行rocketmq及rocketmq控制台安装与配置
rocketmq下载安装的版本:rocketmq-rocketmq-all-5.0.0.tar.gz rocketmq控制台下载安装的版本:rocketmq-externals-rocketmq-console-1.0.0.tar.gz rocketmq安装 第一步,下载server-jre-8u202-linux-x64.tar.gz安装包。 登录网址ÿ…...
2023年全国最新食品安全管理员精选真题及答案4
百分百题库提供食品安全管理员考试试题、食品安全员考试预测题、食品安全管理员考试真题、食品安全员证考试题库等,提供在线做题刷题,在线模拟考试,助你考试轻松过关。 31.国家对食品添加剂生产实行____制度。 A.产品注册 B.产品备案 C.登…...
es-07脚本查询
脚本查询 概念 Scripting是Elasticsearch支持的一种专门用于复杂场景下支持自定义编程的强大的脚本功能,ES支持多种脚本语言,如painless,其语法类似于Java,也有注释、关键字、类型、变量、函数等,其就要相对于其他脚本高出几倍的性…...

JM员工福利与健康平台,企业关怀Always Online
庄信万丰(Johnson Matthey, JM),全球性专用化学品公司,是可持续发展技术的全球领导者。在30多个国家和地区拥有13000多名员工。 JM的价值观之一是保护人类和地球。在生产过程中,JM保持对环境保护和能源清洁的高度关注;在员工福利…...

如何使用U-Mail搭建企业邮件服务器?
在当今的信息时代,企业也应该跟上时代的步伐。做好企业信息化建设,对企业事业单位尤为重要。电子邮件作为企业信息化过程中的重要组成部分,在企业内部沟通和外部沟通中发挥着重要作用。目前,有实力的企业已经开始倾向于自己搭建邮…...

用规则来搭建团队:写周报不一定是坏事
你好,我是Smile,一位有二十年工作经验的技术专家。今天我会结合我的经历,和你聊聊搭建技术团队这个话题。 众所周知,技术团队很大程度上决定了一个公司业务的生命力和生命周期,因此技术团队的投入成本往往很高&#x…...
Apollo使用方法
Apollo使用方法1.Apollo相关原理1.Apollo启动方法1.1 软件包方式1.2 脚本方式2.播放数据包2.1 软件包方式2.2 脚本方式3.试验planning模块4.从官网下载场景集其他工具1.Apollo相关原理 cyber / mainboard / mainboard.cc 是Apollo入口 cyber / mainboard / module_argument.cc…...

科研快讯 | 14篇论文被信号处理领域顶级国际会议ICASSP录用
ICASSP 2023 近日,2023年IEEE声学、语音与信号处理国际会议(2023 IEEE International Conference on Acoustics, Speech, and Signal Processing,ICASSP 2023)发布录用通知,清华大学人机语音交互实验室(TH…...

设计模式—策略(Strategy)模式
一、概述策略模式的用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化使用策略模式可以把行为和环境分割开来。环境类负责维持和查询行为类,…...

STM32 触摸屏移植GUI控制控件
目录 1、emWin 支持指针输入设备。 2、 模拟触摸屏驱动 3、实现触摸屏的流程 3.1 实现硬件函数 3.2 实现对GUI_TOUCH_Exec()的定期调用 3.3 使用上一步确定的值,在初始化函数LCD_X_Config()当中添加对GUI_TOUCH_Calibrate()的调用 4、…...

数仓模型之维度建模
目录 1、数仓架构原则 2、如何搭建一个好的数仓 2.1 建模方法 2.2 建模解决的痛点 2.3 数仓系统满足的特性 2.4 数仓架构设计 3、维度建模 4、案例 5、问题讨论 今天我们来聊聊在数仓模型中举足轻重的维度建模。 简单而言,数据仓库的核心目标是为展现层提…...

Servlet笔记(9):Cookie处理
一、Cookies处理 1、Cookies概念 Cookies是存储在客户端计算机上的文本文件,并保留各种跟踪信息。 识别返回用户的三个步骤 服务器脚本向浏览器发送一组Cookies。例如姓名、年龄或识别号码等。浏览器将这些信息存储在本地计算机上。当下一次浏览器向Web服务器发送…...

骨传导耳机是怎么传声的,选择骨传导耳机的时候需要注意什么?
骨传导耳机之所以能够成为当下最火的耳机,骨传导技术将声音转化为震动感,通过骨头进行传播,不会堵塞耳朵,就不会影响到周围环境音。这种技术也让骨传导耳机比传统入耳式耳机更安全,无需入耳式设计,避免了…...

达梦数据库DSC集群部署
一、概述 1.1 DSC 集群架构 1.2 架构说明 1、DMDSC 集群是一个多实例、单数据库的系统。 多个数据库实例可以同时访问、修改同一个数据库的数据。 2、数据文件、控制文件在集群系统中只有一份,不论有几个节点,这些节点都平等地使用这些文件, 这些文件保存在共享存储上。 3…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...
React Native在HarmonyOS 5.0阅读类应用开发中的实践
一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强,React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 (1)使用React Native…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...

3-11单元格区域边界定位(End属性)学习笔记
返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...
代码随想录刷题day30
1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...

uniapp手机号一键登录保姆级教程(包含前端和后端)
目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号(第三种)后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...
CRMEB 中 PHP 短信扩展开发:涵盖一号通、阿里云、腾讯云、创蓝
目前已有一号通短信、阿里云短信、腾讯云短信扩展 扩展入口文件 文件目录 crmeb\services\sms\Sms.php 默认驱动类型为:一号通 namespace crmeb\services\sms;use crmeb\basic\BaseManager; use crmeb\services\AccessTokenServeService; use crmeb\services\sms\…...
为什么要创建 Vue 实例
核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...
C语言中提供的第三方库之哈希表实现
一. 简介 前面一篇文章简单学习了C语言中第三方库(uthash库)提供对哈希表的操作,文章如下: C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...
上位机开发过程中的设计模式体会(1):工厂方法模式、单例模式和生成器模式
简介 在我的 QT/C 开发工作中,合理运用设计模式极大地提高了代码的可维护性和可扩展性。本文将分享我在实际项目中应用的三种创造型模式:工厂方法模式、单例模式和生成器模式。 1. 工厂模式 (Factory Pattern) 应用场景 在我的 QT 项目中曾经有一个需…...