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

Linux中 socket编程中多进程/多线程TCP并发服务器模型

一、循环服务器(while)【不常用】

  1. 一次只能处理一个客户端的请求,等这个客户端退出后,才能处理下一个客户端。
  2. 缺点:循环服务器所处理的客户端不能有耗时操作。

模型

sfd = socket();
bind();
listen();
while(1)
{newfd = accept();while(1){recv();send();    }close(newfd);
}
close(sfd);

源码

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <string.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <head.h>#define PORT 6666               //1024-49151
#define IP "192.168.122.80"    //ifconfig查看本机ipint main(int argc, const char *argv[])
{//创建流式套接字int sfd = socket(AF_INET,SOCK_STREAM,0);if(sfd < 0){ERR_MSG("socket");return -1;}printf("sfd= %d\n",sfd);                                                                                                    //填充地址信息结构体,真实的地址信息结构体根据地址族制定//AF_INET: man 7 ipstruct sockaddr_in sin;sin.sin_family         = AF_INET;       //必须填AF_INETsin.sin_port           = htons(PORT);   //端口号:1024~49151(网络端口号的字节序)(端口号存储在2个字节的无符号整数中)sin.sin_addr.s_addr    = inet_addr(IP); //本机IP inconfig查看(本机IP地址的字节序)//设置端口允许端口被快速复用int reuse = 1;if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0){ERR_MSG("setsockopt");return -1;}printf("允许端口快速重用成功\n");//绑定服务器的IP和端口号--->必须绑定( bind )if(bind(sfd , (struct sockaddr*)&sin, sizeof(sin)) < 0){ERR_MSG("bind");return -1;}printf("bind success\n");//将套接字设置为被动监听状态( listen)if( listen(sfd,128) < 0){ERR_MSG("listen");return -1;}printf("listen success\n");struct sockaddr_in cin;   //存储客户端的地址信息socklen_t addrlen = sizeof(cin);int newfd = -1;//从已完成连接的队列中获取一个客户端信息,生成一个新的文件描述符//该文件描述符才是与客户端的通信的文件描述符//int newfd = accept(sfd, NULL ,NULL); while(1){newfd = accept(sfd, (struct sockaddr*)&cin, &addrlen);if(newfd < 0){ERR_MSG("accept");return -1;}printf("[%s : %d]newfd=%d 客户端连接成功\n",\inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);char buf[128]="";ssize_t res = 0;while(1){//接收数据bzero(buf,sizeof(buf));res=recv(newfd,buf,sizeof(buf),0);if(res < 0){ERR_MSG("res");return -1;}else if(0 == res){printf("[%s : %d]newfd=%d 客户端已下线\n",\inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);break;}printf("[%s : %d]newfd=%d : %s\n",\inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd, buf);//发送数据strcat(buf,"*_*");if(send(newfd,buf,sizeof(buf),0) < 0){ERR_MSG("send");return -1;}printf("send succuss\n");}}//关闭所有文件描述符close(newfd);close(sfd);return 0;
}

二、并发服务器【常用】

  1. 可以同时处理多个客户端请求
  2. 父进程 / 主线程专门用于负责连接,创建子进程 / 分支线程用来与客户端交互。

1) 多进程

模型

void zombie_callBack(int sig)
{while(waitpid(-1, NULL, WNOHANG) > 0);
}signal(17, zombie_callback);sfd = socket();
bind();
listen();
while(1)
{newfd = accept();if(0 == fork()){close(sfd) ;recv();send();close(newfd);exit(0);   }close(newfd);
}
close(sfd);

源码

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <head.h>#define PORT 6666               //1024-49151
#define IP "192.168.122.80"    //ifconfig查看本机ipint deal_cli_msg(int newfd,struct sockaddr_in cin);void handlr(int sig)
{while(waitpid(-1,NULL,WNOHANG) > 0);
}int main(int argc, const char *argv[])
{if(signal(17,handlr) == SIG_ERR){ERR_MSG("signal");return -1;}printf("捕获成功\n");//创建流式套接字int sfd = socket(AF_INET,SOCK_STREAM,0);if(sfd < 0){ERR_MSG("socket");return -1;}printf("sfd= %d\n",sfd);                                                                                                    //设置端口允许端口被快速复用int reuse = 1;if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0){ERR_MSG("setsockopt");return -1;}printf("允许端口快速重用成功\n");//填充地址信息结构体,真实的地址信息结构体根据地址族制定//AF_INET: man 7 ipstruct sockaddr_in sin;sin.sin_family         = AF_INET;       //必须填AF_INETsin.sin_port           = htons(PORT);   //端口号:1024~49151(网络端口号的字节序)(端口号存储在2个字节的无符号整数中)sin.sin_addr.s_addr    = inet_addr(IP); //本机IP inconfig查看(本机IP地址的字节序)//绑定服务器的IP和端口号--->必须绑定( bind )if(bind(sfd , (struct sockaddr*)&sin, sizeof(sin)) < 0){ERR_MSG("bind");return -1;}printf("bind success\n");//将套接字设置为被动监听状态( listen)if( listen(sfd,128) < 0){ERR_MSG("listen");return -1;}printf("listen success\n");struct sockaddr_in cin;   //存储客户端的地址信息socklen_t addrlen = sizeof(cin);int newfd = -1;pid_t cpid = 0;while(1){newfd = accept(sfd, (struct sockaddr*)&cin, &addrlen);if(newfd < 0){ERR_MSG("accept");return -1;}printf("[%s : %d]newfd=%d 客户端连接成功\n",\inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);cpid = fork();if(0 == cpid){close(sfd);deal_cli_msg(newfd,cin);exit(0);}close(newfd);}//关闭所有文件描述符close(sfd);return 0;
}int deal_cli_msg(int newfd,struct sockaddr_in cin)
{char buf[128]="";ssize_t res = 0;while(1){//接收数据bzero(buf,sizeof(buf));res=recv(newfd,buf,sizeof(buf),0);if(res < 0){ERR_MSG("recv");return -1;}else if(0 == res){printf("[%s : %d]newfd=%d 客户端已下线\n",\inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);break;}printf("[%s : %d]newfd=%d : %s\n",\inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd, buf);//发送数据strcat(buf,"*_*");if(send(newfd,buf,sizeof(buf),0) < 0){ERR_MSG("send");return -1;}printf("send succuss\n");}close(newfd);
}

2) 多线程

模型

sfd = socket();
bind();
listen();
while(1)
{newfd = accept();pthread_create();     --> callBack();pthread_detach(tid);
}
close(sfd);void* callBack(void* arg)
{参数另存recv();send();close(newfd);pthread_exit(NULL);
}

源码

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>#define ERR_MSG(msg) do{\fprintf(stderr, "line:%d ", __LINE__);\perror(msg);\
}while(0)#define PORT 6666               //1024~49151
#define IP  "127.0.0.1"     //IP地址,本机IP ifconfigstruct cli_msg
{int newfd;struct sockaddr_in cin;
};void* deal_cli_msg(void* arg);int main(int argc, const char *argv[])
{//创建流式套接字int sfd = socket(AF_INET, SOCK_STREAM, 0); if(sfd < 0){   ERR_MSG("socket");return -1; }   printf("socket create success  sfd = %d\n", sfd);//设置允许端口快速被重用int resue = 1;if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &resue, sizeof(resue)) < 0){   ERR_MSG("setsockopt");return -1; }   //填充服务器的地址信息结构体//真实的地址信息结构体根据地址族执行,AF_INET: man 7 ipstruct sockaddr_in sin;sin.sin_family      = AF_INET;      //必须填AF_INET;sin.sin_port        = htons(PORT);  //端口号的网络字节序,1024~49151sin.sin_addr.s_addr = inet_addr(IP);    //IP地址的网络字节序,ifconfig查看//绑定---必须绑定if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) < 0){   ERR_MSG("bind");return -1; }   printf("bind success __%d__\n", __LINE__);//将套接字设置为被动监听状态if(listen(sfd, 128) < 0){   ERR_MSG("listen");return -1; }   printf("listen success __%d__\n", __LINE__);//功能:阻塞函数,阻塞等待客户端连接成功。//当客户端连接成功后,会从已完成连接的队列头中获取一个客户端信息,//并生成一个新的文件描述符;新的文件描述符才是与客户端通信的文件描述符struct sockaddr_in cin;     //存储连接成功的客户端的地址信息socklen_t addrlen = sizeof(cin);int newfd = -1; pthread_t tid;struct cli_msg info;while(1){   //主线程负责连接//accept函数阻塞之前,会先预选一个没有被使用过的文件描述符//当解除阻塞后,会判断预选的文件描述符是否被使用//如果被使用了,则重新遍历一个没有被用过的//如果没有被使用,则直接返回预先的文件描述符;newfd = accept(sfd, (struct sockaddr*)&cin, &addrlen);if(newfd < 0){ERR_MSG("accept");return -1; }printf("[%s : %d] newfd=%d 客户端连接成功\n", \inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd);info.newfd = newfd;info.cin = cin;//能运行到当前位置,则代表有客户端连接成功//则需要创建一个分支线程用来,与客户端交互if(pthread_create(&tid, NULL, deal_cli_msg, &info) != 0){fprintf(stderr, "pthread_create failed __%d__\n", __LINE__);return -1; }pthread_detach(tid);        //分离线程}   //关闭所有套接字文件描述符close(sfd);return 0;
}//线程执行体
void* deal_cli_msg(void* arg)   //void* arg = (void*)&info
{//必须要另存,因为同一个进程下的线程共享其附属进程的所有资源//如果使用全局,则会导致每次连接客户端后, newfd和cin会被覆盖//如果使用指针间接访问外部成员变量,也会导致,成员变量被覆盖。int newfd = ((struct cli_msg*)arg)->newfd;struct sockaddr_in cin = ((struct cli_msg*)arg)->cin;char buf[128] = ""; ssize_t res = -1; while(1){   bzero(buf, sizeof(buf));//接收res = recv(newfd, buf, sizeof(buf), 0); if(res < 0){ERR_MSG("recv");break;}else if(0 == res){fprintf(stderr, "[%s : %d] newfd=%d 客户端下线\n", \inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd);break;}printf("[%s : %d] newfd=%d : %s\n", \inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd, buf);//发送 -- 将数据拼接一个 *_* 发送回去strcat(buf, "*_*");if(send(newfd, buf, sizeof(buf), 0) < 0){ERR_MSG("send");break;}printf("send success\n");}   close(newfd);pthread_exit(NULL);
}

相关文章:

Linux中 socket编程中多进程/多线程TCP并发服务器模型

一、循环服务器(while)【不常用】 一次只能处理一个客户端的请求&#xff0c;等这个客户端退出后&#xff0c;才能处理下一个客户端。缺点&#xff1a;循环服务器所处理的客户端不能有耗时操作。 模型 sfd socket(); bind(); listen(); while(1) {newfd accept();while(1){r…...

【内网穿透】如何实现在外web浏览器远程访问jupyter notebook服务器

文章目录 前言1. Python环境安装2. Jupyter 安装3. 启动Jupyter Notebook4. 远程访问4.1 安装配置cpolar内网穿透4.2 创建隧道映射本地端口 5. 固定公网地址 前言 Jupyter Notebook&#xff0c;它是一个交互式的数据科学和计算环境&#xff0c;支持多种编程语言&#xff0c;如…...

win10下如何安装ffmpeg

安装ffmpeg之前先安装win10 绿色软件管理软件&#xff1a;scoop. Scoop的基本介绍 Scoop是一款适用于Windows平台的命令行软件&#xff08;包&#xff09;管理工具&#xff0c;这里是Github介绍页。简单来说&#xff0c;就是可以通过命令行工具&#xff08;PowerShell、CMD等…...

分代收集 + 垃圾回收算法

分代假说 1. 弱分代假说&#xff08;Weak Generational Hypothesis&#xff09;&#xff1a;绝大多数对象都是朝生夕灭的 2. 强分代假说&#xff08;Strong Generational Hypothesis&#xff09;&#xff1a;熬过越多次垃圾收集过程的对象就越难以消亡 3. 跨代引用假说&…...

第三届“赣政杯”网络安全大赛 | 赛宁筑牢安全应急防线

​​为持续强化江西省党政机关网络安全风险防范意识&#xff0c;提高信息化岗位从业人员基础技能&#xff0c;提升应对网络安全风险处置能力。由江西省委网信办、江西省发展改革委主办&#xff0c;江西省大数据中心、国家计算机网络与信息安全管理中心江西分中心承办&#xff0…...

CHATGPT源码简介与使用指南

CHATGPT源码的基本介绍 CHATGPT源码备受关注&#xff0c;它是一款基于人工智能的聊天机器人&#xff0c;旨在帮助开发者快速搭建自己的聊天机器人&#xff0c;无需编写代码。下面是对CHATGPT搭建源码的详细介绍。 CHATGPT源码的构建和功能 CHATGPT源码是基于Google的自然语言…...

【C++精华铺】8.C++模板初阶

目录 1. 泛型编程 2. 函数模板 2.1 函数模板的概念及格式 2.2 函数模板的原理 2.3 模板的实例化 2.4 模板参数的匹配原则 3. 类模板 3.1 类模板格式 3.2 类模板的实例化 1. 泛型编程 什么是泛型编程&#xff1f;泛型编程是避免使用某种具体类型而去使用某种通用类型来进行…...

离谱的Bug

离谱的 Bug Bug 情况发现 Bug修改 Bug其他感受历史 Bug火星Spirit号Mars Global Surveyor任务 Bug 情况 有一次&#xff0c;我在开发一个网页应用程序时&#xff0c;遇到了一个令人目瞪口呆的Bug。这个Bug出现在一个特定的页面上&#xff0c;当用户点击某个按钮时&#xff0c;…...

leetcode 322. 零钱兑换

本题属于完全背包问题&#xff0c;但要求最少的硬币个数。于是设定dp数组的含义dp[i]:总金额为i时&#xff0c;能凑成i的最少硬币个数。 需要注意初始化dp数组时&#xff0c;除0以外的其他地方需要初始化为INT_MAX以保证在递推过程中能被正确的覆盖。 代码如下&#xff1a; …...

(二)结构型模式:6、外观模式(Facade Pattern)(C++实例)

目录 1、外观模式&#xff08;Facade Pattern&#xff09;含义 2、外观模式的UML图学习 3、外观模式的应用场景 4、外观模式的优缺点 5、C实现外观模式的简单实例 1、外观模式&#xff08;Facade Pattern&#xff09;含义 外观模式&#xff08;Facade Pattern&#xff09;…...

docker的资源控制管理——Cgroups

目录 一、对CPU使用率的控制 1.1 CPU 资源控制 1.2 cgroups有四大功能 1.3 设置cpu使用率上限 查看周期限制和cpu配额限制 进行cpu压力测试然后修改每个周期的使用cpu的时间&#xff0c;查看cpu使用率 1.4 设置cpu资源占用比&#xff08;设置多个容器时才有效&#xf…...

less学习语法

1.CSS函数的补充 1.rgb/rgba/translate/rotate/scale 2.非常好用的css函数&#xff1a; var:使用css定义的变量calc:计算css值&#xff0c;通常用于计算元素的大小或位置blur:毛玻璃&#xff08;高斯模糊&#xff09;效果gradient:颜色渐变函数 var:定义变量 css中可以自定…...

在 SHELL 脚本中调用另一个 SHELL 脚本(报错: go: not found)

文章目录 在 SHELL 脚本中调用另一个 SHELL 脚本&#xff08;报错&#xff1a; go: not found&#xff09;在 SHELL 脚本中调用另一个 SHELL 脚本一个脚本sudo调另外一个脚本&#xff0c;报错&#xff08;报错&#xff1a; go: not found&#xff09; 在 SHELL 脚本中调用另一个…...

07微服务的事务管理机制

一句话导读 在单体应用程序中&#xff0c;事务通常是在单个数据库或单个操作系统中管理的&#xff0c;而在微服务架构中&#xff0c;事务需要跨越多个服务和数据库&#xff0c;这就使得事务管理变得更加复杂和困难。 目录 一句话导读 一、微服务事务管理的定义和意义 二、微…...

CS5523规格书|MIPI转EDP方案设计|替代LT8911芯片电路原理|ASL集睿致远CS替代龙讯

ASL芯片&#xff08;集睿致远&#xff09; CS5523是一款MIPI DSI输入&#xff0c;DP/e DP输出转换芯片&#xff0c;可pin to pin替代LT8911龙讯芯片。 MIPI DSI 最多支持 4 个通道&#xff0c;每个通道的最大运行速度为 1.5Gps。对于DP 1.2输出&#xff0c;它支持1.62Gbps和2.…...

【制作npm包5】npm包制作完整教程,我的第一个npm包

制作npm包目录 本文是系列文章&#xff0c; 作者一个橙子pro&#xff0c;本系列文章大纲如下。转载或者商业修改必须注明文章出处 一、申请npm账号、个人包和组织包区别 二、了解 package.json 相关配置 三、 了解 tsconfig.json 相关配置 四、 api-extractor 学习 五、npm包…...

QT:定时器事件

定时器第一种办法&#xff1a; 1.利用事件timerEvent&#xff0c;在帮助文档中找到该字段&#xff1a;[override virtual protected] void QTimer::timerEvent(QTimerEvent *e) 重写该虚函数 //重写定时器事件void timerEvent(QTimerEvent *e);2.启动定时器startTimer(1000); …...

GitHub Actions自动化部署+定时百度链接推送

前言 最近用VuePress搭建了一个静态网站&#xff0c;由于是纯静态的东西&#xff0c;每次修改完文章都要重新打包上传很是麻烦。虽然vuepress-theme-vdoing主题作者提供了GitHub Actions自动化部署的教程文章&#xff0c;但是过于简陋且是19年发布的。。 1. 创建一个GitHub仓…...

PHP学习心得:如何编写可维护的代码

PHP学习心得&#xff1a;如何编写可维护的代码 引言&#xff1a; 在现代的软件开发中&#xff0c;编写可维护的代码是非常重要的。无论是个人项目还是团队项目&#xff0c;可维护的代码可以提高开发效率&#xff0c;减少维护成本&#xff0c;确保代码的质量和可扩展性。本文将…...

使用vscode进行远程调试

官方调试手册&#xff1a;vscode官方调试手册 1.安装python扩展 如果是远程连接的话&#xff0c;一定要在ssh上启用扩展。不然创建基于python的配置文件时就会提示&#xff0c;无python扩展。 2.新建配置文件&#xff0c;并修改参数 点击左侧第四个按钮&#xff0c;运行与调试…...

国防科技大学计算机基础课程笔记02信息编码

1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制&#xff0c;因此这个了16进制的数据既可以翻译成为这个机器码&#xff0c;也可以翻译成为这个国标码&#xff0c;所以这个时候很容易会出现这个歧义的情况&#xff1b; 因此&#xff0c;我们的这个国…...

应用升级/灾备测试时使用guarantee 闪回点迅速回退

1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间&#xff0c; 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点&#xff0c;不需要开启数据库闪回。…...

MFC内存泄露

1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...

【机器视觉】单目测距——运动结构恢复

ps&#xff1a;图是随便找的&#xff0c;为了凑个封面 前言 在前面对光流法进行进一步改进&#xff0c;希望将2D光流推广至3D场景流时&#xff0c;发现2D转3D过程中存在尺度歧义问题&#xff0c;需要补全摄像头拍摄图像中缺失的深度信息&#xff0c;否则解空间不收敛&#xf…...

Cinnamon修改面板小工具图标

Cinnamon开始菜单-CSDN博客 设置模块都是做好的&#xff0c;比GNOME简单得多&#xff01; 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...

土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等

&#x1f50d; 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术&#xff0c;可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势&#xff0c;还能有效评价重大生态工程…...

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...

QT: `long long` 类型转换为 `QString` 2025.6.5

在 Qt 中&#xff0c;将 long long 类型转换为 QString 可以通过以下两种常用方法实现&#xff1a; 方法 1&#xff1a;使用 QString::number() 直接调用 QString 的静态方法 number()&#xff0c;将数值转换为字符串&#xff1a; long long value 1234567890123456789LL; …...

Java 二维码

Java 二维码 **技术&#xff1a;**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...

rnn判断string中第一次出现a的下标

# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...