网络编程代码实例:多进程版
文章目录
- 前言
- 代码仓库
- 内容
- 代码(有详细注释)
- server.c
- client.c
- Makefile
- 结果
- 总结
- 参考资料
- 作者的话
前言
网络编程代码实例:多进程版。
代码仓库
- yezhening/Environment-and-network-programming-examples: 环境和网络编程实例 (github.com)
- Environment-and-network-programming-examples: 环境和网络编程实例 (gitee.com)
内容
- 使用传输控制协议(TCP)
- 一个服务端可连接多个客户端
- 多次手动通信
代码(有详细注释)
server.c
// 头文件————————————————————
// #include <sys/socket.h> // socket()、sockaddr、bind()、listen()、accept()、recv()、send()
#include <stdio.h> //(perror())、printf()
#include <stdlib.h> //exit()
// #include <netinet/in.h> //sockaddr_in、(htons())
#include <string.h> //bzero()、strncpy()
#include <arpa/inet.h> //inet_pton()
// #include <unistd.h> //close()、fork()
#include <errno.h> //errno// 全局常量————————————————————
const g_serv_port = 6666; // 服务端的端口号
const g_listen_max_count = 5; // 监听的最大连接数
const g_buff_size = 64; // 消息缓冲区的大小// 函数声明————————————————————
void handle_request(int connect_fd); // 处理请求// 主函数————————————————————
int main(int arg, char *argv[])
{// 网络连接————————————————————int listen_fd; // 监听套接字文件描述符// 创建套接字并获取套接字文件描述符if ((listen_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1){perror("socket() error");exit(EXIT_FAILURE);}struct sockaddr_in serv_addr; // 服务端网络信息结构体// 初始化服务端网络信息结构体bzero(&serv_addr, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(g_serv_port);if ((inet_pton(AF_INET, "0.0.0.0", &serv_addr.sin_addr)) != 1){perror("inet_pton() error");exit(EXIT_FAILURE);}// 绑定套接字与网络信息if ((bind(listen_fd, (struct sockaddr *)(&serv_addr), sizeof(serv_addr))) == -1){if ((close(listen_fd)) == -1){perror("bind() close() error");exit(EXIT_FAILURE);}perror("bind() error");exit(EXIT_FAILURE);}// 套接字设置被动监听状态if ((listen(listen_fd, g_listen_max_count)) == -1){if ((close(listen_fd)) == -1){perror("listen() close() error");exit(EXIT_FAILURE);}perror("listen() error");exit(EXIT_FAILURE);}struct sockaddr_in clie_addr; // 客户端网络信息结构体int clie_addr_size; // 客户端网络信息结构体大小int connect_fd; // 连接套接字文件描述符bzero(&clie_addr, sizeof(clie_addr));clie_addr_size = sizeof(struct sockaddr);pid_t pid; // 进程号// 循环监听客户端请求// 原则:父进程不能退出,子进程可以退出while (1){// 与客户端建立连接if ((connect_fd = accept(listen_fd, (struct sockaddr *)(&clie_addr), &clie_addr_size)) == -1){perror("accept() error");continue; // 继续监听}// 创建子进程处理请求pid = fork();if (pid == -1) // 错误{perror("fork() error");continue; // 继续监听}else if (pid == 0) // 子进程{if ((close(listen_fd)) == -1) // 1.关闭监听套接字文件描述符{if ((close(connect_fd)) == -1){perror("fork() close() connect_fd child_process error");exit(EXIT_FAILURE); // 子进程退出}perror("fork() close() listen_fd child_process error");exit(EXIT_FAILURE); // 子进程退出}handle_request(connect_fd); // 2.处理请求if ((close(connect_fd)) == -1) // 3.关闭连接套接字文件描述符{perror("fork() close() connect_fd2 child_process error");exit(EXIT_FAILURE); // 子进程退出}exit(EXIT_SUCCESS);}else if (pid > 0) // 父进程{if ((close(connect_fd)) == -1) // 关闭连接套接字文件描述符{perror("fork() close() connect_fd parent_process error");continue; // 继续监听}}}if ((close(listen_fd)) == -1) // 父进程关闭监听套接字文件描述符。实际不会执行{perror("close() listen_fd error");exit(EXIT_FAILURE);}return 0;
}// 函数定义————————————————————
// 处理请求
void handle_request(int connect_fd) // 参数:连接套接字文件描述符
{// 传输消息————————————————————char msg_recv[g_buff_size]; // 从客户端接收的消息缓冲区char msg_send[g_buff_size]; // 发送到客户端的消息缓冲区int recv_bytes; // 接收的消息字节数while (1) // 循环接收和发送消息{bzero(&msg_recv, sizeof(*msg_recv));bzero(&msg_send, sizeof(*msg_send));recv_bytes = recv(connect_fd, msg_recv, g_buff_size, 0); // 接收消息if (recv_bytes > 0) // 有消息{printf("Received message: %s", msg_recv); // 接收的消息strncpy(msg_send, msg_recv, g_buff_size); // 发送的消息if ((send(connect_fd, msg_send, g_buff_size, 0)) == -1) // 发送消息{perror("send() error");return; // 函数返回后,关闭连接套接字文件描述符,结束子进程}}else if (recv_bytes == 0) // 文件末尾EOF:在客户端标准输入Ctrl+D{printf("The process %d received the end of file\n", getpid());return; // 函数返回后,关闭连接套接字文件描述符,结束子进程}else if ((recv_bytes == -1) && (errno == EINTR)) // 信号或网络中断recv(){continue; // 继续接收消息}else if (recv_bytes == -1) // 错误{perror("recv() error");return; // 函数返回后,关闭连接套接字文件描述符,结束子进程}}return;
}
client.c
// 头文件————————————————————
// #include <sys/socket.h> //socket()、sockaddr、connect()、send()、recv()
#include <stdio.h> //(perror())、printf()、(fgets())
#include <stdlib.h> //exit()
// #include <netinet/in.h> //sockaddr_in、(htons())
#include <string.h> //bzero()、strncpy()
#include <arpa/inet.h> //inet_pton()
// #include <unistd.h> //close()
#include <errno.h> //errno// 全局常量————————————————————
const g_serv_port = 6666; // 服务端端口号
const g_buff_size = 64; // 消息缓冲区大小// 函数声明————————————————————
void handle(int sock_fd); // 处理// 主函数————————————————————
int main(int argc, char *argv[])
{// 网络连接————————————————————int sock_fd; // 套接字文件描述符// 创建套接字并获取套接字文件描述符if ((sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1){perror("socket() error");exit(EXIT_FAILURE);}struct sockaddr_in serv_addr; // 服务端网络信息结构体// 初始化服务端网络信息结构体bzero(&serv_addr, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(g_serv_port);if ((inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr)) != 1){perror("inet_pton() error");exit(EXIT_FAILURE);}// 与服务端建立连接if ((connect(sock_fd, (struct sockaddr *)(&serv_addr), sizeof(serv_addr))) == -1){if ((close(sock_fd)) == -1){perror("connect() close() error");exit(EXIT_FAILURE);}perror("connect() error");exit(EXIT_FAILURE);}handle(sock_fd); // 处理// 关闭套接字文件描述符if ((close(sock_fd)) == -1){perror("close() error");exit(EXIT_FAILURE);}return 0;
}// 函数定义————————————————————
// 处理
void handle(int sock_fd) // 参数:套接字文件描述符
{// 传输消息————————————————————char msg_send[g_buff_size]; // 发送到服务端的消息缓冲区char msg_recv[g_buff_size]; // 从服务端接收的消息缓冲区int recv_bytes; // 接收的消息字节数while (1) // 循环发送和接收消息{bzero(&msg_send, sizeof(*msg_send));bzero(&msg_recv, sizeof(*msg_recv));printf("Send message: ");if ((fgets(msg_send, g_buff_size, stdin)) == NULL)// 从标准输入获取消息。错误或遇到文件结尾(EOF):在客户端标准输入Ctrl+D,相当于关闭连接{printf("End of connection\n");return; // 函数返回后,关闭套接字文件描述符,结束进程}if ((send(sock_fd, msg_send, g_buff_size, 0)) == -1) // 发送消息{perror("send() error");return; // 函数返回后,关闭连接套接字文件描述符,结束进程}recv_bytes = recv(sock_fd, msg_recv, g_buff_size, 0); // 接收消息if (recv_bytes > 0) // 有数据{printf("Received message: %s", msg_recv); // 接收的消息}else if (recv_bytes == 0) // 服务端进程提前终止,在服务端标准输入Ctrl+C中断进程{printf("Server terminated prematurely\n");return; // 函数返回后,关闭套接字文件描述符,结束进程}else if ((recv_bytes == -1) && (errno == EINTR)) // 信号或网络中断recv(){continue; // 继续发送和接收数据}else if (recv_bytes == -1) // 错误{perror("recv() error");return; // 函数返回后,关闭套接字文件描述符,结束进程}}return;
}
Makefile
#变量
targets = server client#伪目标
.PHONY : all
all : $(targets) #规则
server : server.cgcc -o server server.c client : client.cgcc -o client client.c#伪目标
.PHONY : clean
clean :rm $(targets)
结果
正常情况,服务端表现:

正常情况,客户端1表现:

正常情况,客户端2表现:

正常情况,客户端3表现:

服务端终止,服务端表现:

服务端终止,客户端表现:

客户端终止连接,服务端表现:

客户端终止连接,客户端表现:

总结
网络编程代码实例:多进程版。
参考资料
- 《UNIX环境高级编程(第3版)》作者:W.Richard Stevens,Stephen A.Rago
- 《UNIX网络编程(第3版)》作者:W.Richard Stevens,Bill Fenner,Andrew M.Rudoff
作者的话
- 感谢参考资料的作者/博主
- 作者:夜悊
- 版权所有,转载请注明出处,谢谢~
- 如果文章对你有帮助,请点个赞或加个粉丝吧,你的支持就是作者的动力~
- 文章在描述时有疑惑的地方,请留言,定会一一耐心讨论、解答
- 文章在认识上有错误的地方, 敬请批评指正
- 望读者们都能有所收获
相关文章:
网络编程代码实例:多进程版
文章目录 前言代码仓库内容代码(有详细注释)server.cclient.cMakefile 结果总结参考资料作者的话 前言 网络编程代码实例:多进程版。 代码仓库 yezhening/Environment-and-network-programming-examples: 环境和网络编程实例 (github.com)E…...
一家传统制造企业的上云之旅,怎样成为了数字化转型典范?
众所周知,中国是一个制造业大国。在想要上云以及正在上云的企业当中,传统制造企业也占据了相当大的比例。 那么这类企业在实施数字化转型的时候,应该如何着手?我们不妨来看看一家传统制造企业的现身说法。 国茂股份的数字化转型诉…...
C++入门(C++)
目录 命名空间 1、命名空间的定义 2、命名空间的使用 1、加名空间名称和作用域限定符 2、使用using namespace 命名空间引入 3、使用using将命名空间中某个成员引入 C的输入与输出 缺省参数 1、缺省参数的概念 2、缺省参数分类 1、全缺省参数 2、半缺省参数 函数重载 1、函数重…...
Linux 利用网络同步时间
yum -y install ntp ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime ntpdate ntp1.aliyun.com 创建加入crontab echo "*/20 * * * * /usr/sbin/ntpdate -u ntp.api.bz >/dev/null &" >> /var/spool/cron/rootntp常用服务器 中国国家授…...
炫技亮点 SpringBoot下消灭If Else,让你的代码更亮眼
文章目录 背景案例第一阶段 萌芽第二阶段 屎上雕花第三阶段 策略工厂模式重构第四阶段 优化 总结 背景 大家好,我是大表哥laker。今天,我要和大家分享一篇关于如何使用策略模式和工厂模式消除If Else耦合问题的文章。这个方法能够让你的代码更加优美、简…...
免费ChatGPT接入网站-网站加入CHATGPT自动生成关键词文章排名
网站怎么接入chatGPT 要将ChatGPT集成到您的网站中,需要进行以下步骤: 注册一个OpenAI账户:访问OpenAI网站并创建一个账户。这将提供访问API密钥所需的身份验证凭据。 获取API密钥:在您的OpenAI控制台中,您可以找到您…...
PostgreSQL的数据类型有哪些?
数据类型分类 分类名称说明与其他数据库的对比布尔类型PG支持SQL标准的boolean数据类型与MySQL中的bool、boolean类型相同,占用1字节存储空间数值类型整数类型有2字节的smallint、4字节的int、8字节的bigint;精确类型的小数有numeric;非精确…...
Android 9.0 系统开机自启动第三方app
1.前言 在9.0的系统rom定制化开发中,在framework定制话的功能开发中,在内置的app中,有时候在系统开机以后会要求启动第三方app的功能,所以这就需要在监听开机完成的广播,然后在启动第三方app就可以了,接下来就需要在系统类中监听开机完成的广播流程来实现功能 2.系统开…...
一些想法:关于学习一门新的编程语言
很多人可能长期使用一种编程语言,并感到很有成就感和舒适感,发现学习一种新的编程语言的想法令人生畏而痛苦。或者可能知道并使用多种编程语言,但有一段时间没有学习新的语言。更或者可能只是好奇别人是如何潜心学习新的编程语言并迅速取得成…...
线性代数——矩阵
文章目录 版权声明基础概念矩阵的运算矩阵的加法数与矩阵相乘矩阵的乘法矩阵的转置 矩阵和方程组方阵和行列式伴随矩阵可逆矩阵分块矩阵矩阵的初等变换初等矩阵等价矩阵行阶梯矩阵行最简矩阵初等变换在矩阵求解中的应用 矩阵的秩 版权声明 本文大部分内容皆来自李永乐老师考研…...
taro之小程序持续集成
小程序持续集成 Taro 小程序端构建后支持 CI(持续集成)的插件 tarojs/plugin-mini-ci。 目前已支持(企业)微信、京东、字节、支付宝、钉钉、百度小程序 功能包括: 构建完毕后自动唤起小程序开发者工具并打开项目上传…...
Ceph入门到精通-Ceph 编排器简介
第 1 章 Ceph 编排器简介 作为存储管理员,您可以将 Ceph 编排器与 Cephadm 实用程序搭配使用,能够发现设备并在 Red Hat Ceph Storage 集群中创建服务。 1.1. 使用 Ceph Orchestrator Red Hat Ceph Storage Orchestrators 是经理模块,主要…...
【Feign扩展】OpenFeign日志打印Http请求参数和响应数据
SpringBoot使用log4j2 在Spring Boot中所有的starter 都是基于spring-boot-starter-logging的,默认使用Logback。使用Log4j2的话,你需要排除 spring-boot-starter-logging 的依赖,并添加 spring-boot-starter-log4j2的依赖。 配置依赖 <…...
MongoDB (零) 安装和简单使用
1.安装(Ubuntu) 1.1.安装gnupg sudo apt-get install gnupg1.2.获取GPG Key curl -fsSL https://pgp.mongodb.com/server-6.0.asc | \sudo gpg -o /usr/share/keyrings/mongodb-server-6.0.gpg \--dearmor1.3.创建本地文件 echo "deb [ archamd64,arm64 signed-by/usr…...
Java中的异常是什么?
Java中的异常是指在程序运行时发生的错误或异常情况。这些异常可能会导致程序崩溃或无法正确执行,因此需要在代码中进行处理。Java中的异常机制可以帮助程序员捕获并处理异常,从而保证程序的稳定性和可靠性。 Java中的异常分为两种类型:受检…...
微短剧“小阳春”,“爱优腾芒”抢滩登陆?
降本增效一整年,长视频平台们似乎扭转了市场对于它们“烧钱”的印象。 爱奇艺宣布2022全年盈利,腾讯视频宣布从去年10月起开始盈利,视频平台们结束了一场“无限战争”。 与此同时,随着短视频平台的崛起,视频内容的形…...
C++菱形继承(再剖析)
当子类对象给父类对象的时候,怎么找公共的虚基类(A) 就得通过偏移量来算虚基类的位置 ---------------------------------------------------------------------------------------------------------------------------- 我们来分析一下B…...
java获取星期几
如果你要问 java什么时候学习比较好,那么答案肯定是 java的星期几。 在 Java中,你可以使用 public static void main ()方法来获取一个类的所有成员变量,然后在所有类中调用这个方法来获取对象的所有成员变量。它能以对…...
【TypeScript】03-TypeScript基本类型
TypeScript基本类型 在TypeScript中,基本类型是非常重要的一部分,下面我们将详细介绍TypeScript中的基本类型。 基本类型约束 在TypeScript中,可以使用基本类型来约束变量的类型。常见的基本类型有: number:表示数…...
什么是跨域?
什么是跨域 什么是跨域? 什么是同源策略及其限制内容? 同源策略是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSRF等攻击。所谓同源是指"协议域名端口"三者相…...
华为云AI开发平台ModelArts
华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...
地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...
大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...
DAY 47
三、通道注意力 3.1 通道注意力的定义 # 新增:通道注意力模块(SE模块) class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...
PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...
【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...
C++ 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...
RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill
视觉语言模型(Vision-Language Models, VLMs),为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展,机器人仍难以胜任复杂的长时程任务(如家具装配),主要受限于人…...
