基于LinuxC语言实现的TCP多线程/进程服务器
多进程并发服务器
设计流程
框架一(使用信号回收僵尸进程)
void handler(int sig)
{while(waitpid(-1, NULL, WNOHANG) > 0);
}int main()
{//回收僵尸进程siganl(17, handler);//创建服务器监听套接字 serverserver = socket();//给服务器地址信息结构体赋值,并绑定bind();//监听指定端口,设置监听队列listen();while(1){//创建与客户端通信的套接字client = accept();//创建子进程if(fork() == 0){//关闭拷贝的服务器套接字close(server);while(1){//接收消息recv();//发送消息send();}//通信结束关闭套接字close(client);//退出进程exit(0);}//关闭父进程的通信套接字close(client);}//服务器关闭close(server);
}
实例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/wait.h>
#include <time.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <errno.h>#define LOG(s) printf("[%s] {%s:%d} %s \n", __DATE__, __FILE__, __LINE__, s);void cil(int client, struct sockaddr_in caddr);void handler(int sig)
{while(waitpid(-1, NULL, WNOHANG) > 0);
}int main(int argc, char *argv[])
{//回收僵尸进程signal(17, handler);//创建服务器int server = -1;if((server = socket(AF_INET, SOCK_STREAM, 0)) == -1){LOG("socket error");return -1;}//给服务器地址信息结构体赋值,并绑定struct sockaddr_in saddr = {0};saddr.sin_family = AF_INET;saddr.sin_port = htons(8888);saddr.sin_addr.s_addr = htonl(INADDR_ANY);if(bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == -1){LOG("bind error");return -1;}//监听指定端口,设置监听队列if(listen(server, 5) == -1){LOG("listen error");return -1;}puts("Tcp server start success");int client = -1;struct sockaddr_in caddr = {0};socklen_t len = sizeof(caddr);pid_t pid = -1;while(1){//创建与客户端通信的套接字if((client = accept(server, (struct sockaddr*)&caddr, &len)) == -1){LOG("accpet error");return -1;}printf("[%s/%d] client已上线\n", inet_ntoa(caddr.sin_addr), ntohl(caddr.sin_port));//创建子进程if((pid = fork()) < 0){LOG("fork error");return -1;}else if(pid == 0){cil(client, caddr);exit(0);}close(client);}close(server);return 0;
}void cil(int client, struct sockaddr_in caddr)
{char buf[128] = "";int res = 0;while(1){bzero(buf, sizeof(buf));if((res = read(client, buf, sizeof(buf))) < 0){LOG("read error");break;}else if(res == 0){printf("[%s/%d] client已下线\n", inet_ntoa(caddr.sin_addr), ntohl(caddr.sin_port));close(client);break;}printf("[%s/%d] client: %s\n", inet_ntoa(caddr.sin_addr), ntohl(caddr.sin_port), buf);bzero(buf, sizeof(buf));strcpy(buf, "ok");if(write(client, buf, sizeof(buf)) < 0){LOG("write error");break;}}}
框架二(使用孤儿进程机制避免僵尸进程产生)
int main()
{//创建服务器监听套接字 serverserver = socket();//给服务器地址信息结构体赋值,并绑定bind();//监听指定端口,设置监听队列listen();while(1){//创建子进程if((pid = fork()) == 0){//子进程创建用于与客户端通信的clientclient = accept();//创建孙进程if(pid = fork() == 0){while(1){//孙进程负责与客户端通信recv();send();}//通信结束关闭套接字close(client);//退出进程exit(0);}//子进程else if(pid > 0){//关闭多余的文件描述符close(server);close(client);//退出子进程exit(0);}}//父进程else if(pid > 0){//回收子进程while(waitpid() == pid)}}//关闭文件描述符close(server);
}
实例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/wait.h>
#include <time.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <errno.h>#define LOG(s) printf("[%s] {%s:%d} %s\n", __DATE__, __FILE__, __LINE__, s);void deal_cil_msg(int client, struct sockaddr_in caddr);int main(int argc, char *argv[])
{//创建服务器socketint server = 0;if((server = socket(AF_INET, SOCK_STREAM, 0)) == -1){LOG("socket error");return -1;}//绑定服务器IP和端口号//给地址信息结构体赋值struct sockaddr_in saddr = {0};saddr.sin_family = AF_INET;saddr.sin_port = htons(8888);saddr.sin_addr.s_addr = htonl(INADDR_ANY);if(bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == -1){LOG("bind error");return -1;}//监听对应的端口号if(listen(server, 5) == -1){LOG("listen error");return -1;}puts("server start success");//创建用于与客户端通信的socketstruct sockaddr_in caddr = {0};int client = 0;socklen_t asize = sizeof(caddr);pid_t pid = -1;int status = 0;while(1){//父进程只负责生儿子pid = fork();if(pid < 0){LOG("fork error");return -1;}//子进程负责创建通信socketif(pid == 0){//创建用于与客户端通信的socketif((client = accept(server, (struct sockaddr*)&caddr, &asize)) == -1){LOG("accept error");return -1;}printf("[%s/%d]client已上线\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));//孙进程负责通信pid = fork();if(pid < 0){LOG("fork error");return -1;}else if(pid == 0){deal_cil_msg(client, caddr);close(client);exit(0);}else if(pid > 0){close(server);close(client);//退出子进程exit(0);}}else if(pid > 0){printf("wait child = %d\n", pid);//父进程等待子进程结束,准备收尸while(waitpid(pid, &status, 0) == pid){printf("Parent is over - child: %d, status = %x\n", pid, status);}}}//关闭文件描述符close(server);return 0;
}void deal_cil_msg(int client, struct sockaddr_in caddr)
{//接收消息char buf[128] = " ";while(1){int len = 0;bzero(buf, sizeof(buf));if((len = recv(client, buf, sizeof(buf), 0)) < 0){ LOG("recv error");}else if(len == 0){printf("[%s/%d]client已下线\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));break;}printf("[%s/%d]client: %s\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port), buf);bzero(buf, sizeof(buf));//发送消息strcpy(buf, "ok");write(client, buf, len);}}
多线程并发服务器
设计流程
//线程参数结构体
typedef struct
{int client;struct sockaddr_in caddr;
} Client_msg;int main()
{//创建服务器监听套接字 serverserver = socket();//给服务器地址信息结构体赋值,并绑定bind();//监听指定端口,设置监听队列listen();while(1){//创建用于与客户端通信的套接字client = accpet();//创建线程pthread_create();while(1){//读写//关闭文件描述符//退出线程}//解离线程pthread_detach();}//关闭文件描述符close(server);
}
实例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/wait.h>
#include <time.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <errno.h>#define LOG(s) printf("[%s] {%s:%d} %s \n", __DATE__, __FILE__, __LINE__, s);void* cil(void* arg);//线程参数结构体
typedef struct
{int client;struct sockaddr_in caddr;
} Cli_msg;int main(int argc, char *argv[])
{//创建服务器监听分套接字 serverint server = -1;if((server = socket(AF_INET, SOCK_STREAM, 0)) == -1){LOG("socket error");return -1;}//给服务器地址信息结构体赋值,并绑定struct sockaddr_in saddr = {0};saddr.sin_family = AF_INET;saddr.sin_port = htons(8899);saddr.sin_addr.s_addr = htonl(INADDR_ANY);if(bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == -1){LOG("bind error");return -1;}//监听端口if(listen(server, 5) == -1){LOG("listen error");return -1;}puts("tcp server start success");struct sockaddr_in caddr = {0};socklen_t len = sizeof(caddr);pthread_t tid = -1;int client = -1;Cli_msg cli_msg;while(1){//创建用于与客户端通信的套接字 clientif((client = accept(server, (struct sockaddr*)&caddr, &len)) == -1){LOG("accept error");return -1;}printf("[%s/%d]client已上线\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));cli_msg.client = client;cli_msg.caddr = caddr;//创建线程if(pthread_create(&tid, NULL, cil, &cli_msg) != 0){LOG("pthread_create error");return -1;}pthread_detach(tid);}close(server);return 0;
}void* cil(void* arg)
{int client = ((Cli_msg*)arg)->client;struct sockaddr_in caddr = ((Cli_msg*)arg)->caddr;char buf[128] = "";int res = 0;while(1){bzero(buf, sizeof(buf));if((res = read(client, buf, sizeof(buf))) < 0){LOG("read error");break;}else if(res == 0){printf("[%s/%d]client已下线\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));close(client);pthread_exit(NULL);}printf("[%s/%d]client: %s\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port), buf);bzero(buf, sizeof(buf));strcpy(buf, "ok");if(write(client, buf, sizeof(buf)) < 0){LOG("write error");break;}}}相关文章:
基于LinuxC语言实现的TCP多线程/进程服务器
多进程并发服务器 设计流程 框架一(使用信号回收僵尸进程) void handler(int sig) {while(waitpid(-1, NULL, WNOHANG) > 0); }int main() {//回收僵尸进程siganl(17, handler);//创建服务器监听套接字 serverserver socket();//给服务器地址信息…...
浅谈JVM垃圾回收机制
一、HotSpot VM中的GC分为两大类 1.部分收集(Partial GC): 新生代收集(Minor GC/Young GC):只对新生代进行垃圾收集老年代收集(Major GC/Old GC):只队老年代进行垃圾收集混合收集(Mixed GC):对整个新生代和老年代进行垃圾收集 2.整堆收集(Full GC) 收集整个Java堆和方法区 …...
【80天学习完《深入理解计算机系统》】第十二天3.6数组和结构体
专注 效率 记忆 预习 笔记 复习 做题 欢迎观看我的博客,如有问题交流,欢迎评论区留言,一定尽快回复!(大家可以去看我的专栏,是所有文章的目录) 文章字体风格: 红色文字表示&#…...
基于Python+OpenCV智能答题卡识别系统——深度学习和图像识别算法应用(含Python全部工程源码)+训练与测试数据集
目录 前言总体设计系统整体结构图系统流程图 运行环境Python 环境PyCharm安装OpenCV环境 模块实现1. 信息识别2. Excel导出模块3. 图形用户界面模块4. 手写识别模块 系统测试1. 系统识别准确率2. 系统识别应用 工程源代码下载其它资料下载 前言 本项目基于Python和OpenCV图像处…...
Redis集群操作-----主从互换
一、将节点cluster1的主节点7000端口的redis关掉 [rootredis-cluster1 src]# ps -ef |grep redis 二、查看集群信息:...
肖sir __linux命令拓展__05
linux命令拓展 1.追加内容到某文件 echo “i like learn linux” >>quzhi.txt 2.删除指定的空目录: rmdir 目录名 rmdir -p 目录名 (删除指定的空目录及其内子空目录) 3.显示zip包信息 zipinfo 压缩包名 (显示压缩包内的文…...
大白菜清理电脑密码教程
首先安装大白菜: 插入u盘一键制作启动盘 制作成功,重启进入u盘启动模式...
[libglog][FFmpeg] 如何把 ffmpeg 的库日志输出到 libglog里
ffmpeg 提供了自己的 log 模块 av_log,会默认把输出打印到 stderr 上,因此无法方便地跟踪日志。但是 ffmpeg 提供了一个接口 av_log_set_callback 以供外界自定义自己的日志输出。 libglog 提供的是c 形式的日志输出样式,因此需要将二者关联起…...
【Unity-Cinemachine相机】虚拟相机(Virtual Camera)的本质与基本属性
我们可以在游戏进行时修改各个属性,但在概念上,最好将Virtual Camera 当作一种相机行为的“配置文件”,而不是一个组件。 我们的相机有几种行为就为它准备几种虚拟相机,比如角色移动就为它第三人称相机,瞄准就准备一个…...
LeetCode:718. 最长重复子数组 - Python
718. 最长重复子数组 问题描述: 给两个整数数组 nums1 和 nums2 ,返回 两个数组中 公共的 、长度最长 的 子数组 的 长度 。 示例 1: 输入:nums1 [1,2,3,2,1], nums2 [3,2,1,4,7] 输出:3 解释:长度最长…...
【面试题精讲】Redis如何实现分布式锁
首发博客地址 系列文章地址 Redis 可以使用分布式锁来实现多个进程或多个线程之间的并发控制,以确保在给定时间内只有一个进程或线程可以访问临界资源。以下是一种使用 Redis 实现分布式锁的常见方法: 获取锁: 客户端尝试使用 SETNX命令在 Re…...
list【2】模拟实现(含迭代器实现超详解哦)
模拟实现list 引言(实现概述)list迭代器实现默认成员函数operator* 与 operator->operator 与 operator--operator 与 operator!迭代器实现概览 list主要接口实现默认成员函数构造函数析构函数赋值重载 迭代器容量元素访问数据修改inserterasepush_ba…...
Nginx+Tomcat的动静分离与负载均衡
目录 前言 一、案例 二、Nginx的高级用法 三、tomcat部署 四、Nginx部署 五、测试 总结 前言 通常情况下,一个 Tomcat 站点由于可能出现单点故障及无法应付过多客户复杂多样的请求等情况,不能单独应用于生产环境下,所以我们需要一套更…...
【设计模式】Head First 设计模式——策略模式 C++实现
设计模式最大的作用就是在变化和稳定中间寻找隔离点,然后分离它们,从而管理变化。将变化像小兔子一样关到笼子里,让它在笼子里随便跳,而不至于跳出来把你整个房间给污染掉。 设计思想 将行为想象为一族算法,定义算法族…...
c#object类中方法的使用
C#中的Object类是所有类的基类,它定义了一些通用的方法和属性,可以在任何对象上使用。以下是Object类中常用的方法和属性的使用: 1.ToString():将对象转换为字符串表示形式。 string str obj.ToString();2.Equals():…...
三种常用盒子布局的方法
在Vue中,可以使用各种CSS布局属性和技巧来设置盒子的布局。以下是一些常用的方法: 1.使用Flexbox布局:在包含盒子的父元素上设置display: flex,然后可以使用flex-direction、justify-content和align-items 等属性来控制盒子的布局…...
GB28181学习(二)——注册与注销
概念 使用REGISTER方法进行注册和注销;注册和注销应进行认证,认证方式应支持数字摘要认证方式,高安全级别的宜支持数字证书认证;注册成后,SIP代理在注册过期时间到来之前,应向注册服务器进行刷新注册&…...
【Linux】线程安全-信号量
文章目录 信号量原理信号量保证同步和互斥的原理探究信号量相关函数初始化信号量函数等待信号量函数释放信号量函数销毁信号量函数 信号量实现生产者消费者模型 信号量原理 信号量的原理:资源计数器 PCB等待队列 函数接口 资源计数器:对共享资源的计…...
数字IC验证——PSS可移植测试用例
PSS是Accellera组织定义的测试用例生成规范,其思想是定义一个抽象模型,EDA工具可以从中生成适用于每个设计层次结构和每个验证平台的测试,即PSS定义了统一的测试场景,而场景的使用可以横跨不同验证层次和配置。 这种特性决定了PSS…...
java设计模式---策略模式
策略模式的定义 策略设计模式是一种行为设计模式。当在处理一个业务时,有多种处理方式,并且需要再运行时决定使哪一种具体实现时,就会使用策略模式。 策略模式的类图: 策略模式的实现 在支付业务中,有三种付款方式&…...
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...
React Native 导航系统实战(React Navigation)
导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...
PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...
今日科技热点速览
🔥 今日科技热点速览 🎮 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售,主打更强图形性能与沉浸式体验,支持多模态交互,受到全球玩家热捧 。 🤖 人工智能持续突破 DeepSeek-R1&…...
Spring数据访问模块设计
前面我们已经完成了IoC和web模块的设计,聪明的码友立马就知道了,该到数据访问模块了,要不就这俩玩个6啊,查库势在必行,至此,它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据(数据库、No…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...
用机器学习破解新能源领域的“弃风”难题
音乐发烧友深有体会,玩音乐的本质就是玩电网。火电声音偏暖,水电偏冷,风电偏空旷。至于太阳能发的电,则略显朦胧和单薄。 不知你是否有感觉,近两年家里的音响声音越来越冷,听起来越来越单薄? —…...
DingDing机器人群消息推送
文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人,点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置,详见说明文档 成功后,记录Webhook 2 API文档说明 点击设置说明 查看自…...
uniapp手机号一键登录保姆级教程(包含前端和后端)
目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号(第三种)后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...
