Linux多线程网络通信
思路:主线程(只有一个)建立连接,就创建子线程。子线程开始通信。
共享资源:全局数据区,堆区,内核区描述符。
线程同步不同步需要取决于线程对共享资源区的数据的操作,如果是只读就不需要,如果是写就需要了。
多线程布置过程
(1)包含对应的头文件,特别是多线程库文件pthread.h
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>//多线程的头文件
(2)创建监听套接字
// 1. 创建监听的套接字 https://subingwen.cn/linux/socket/。返回的是文件描述符int lfd = socket(AF_INET, SOCK_STREAM, 0);if(lfd == -1){perror("socket");exit(0);}
(3)端口绑定
使用 struct sockaddr_in 类型的结构体来存储数据,需要注意的是大小端转换,因为网络通信使用大端序,需要htons()函数进行端口转换。端口设置完毕之后,可以设置ip地址,我们使用INADDR_ANY,表示任意绑定。
struct sockaddr_in addr;addr.sin_family = AF_INET;//地址族协议addr.sin_port = htons(10000); // 大端端口,要使用的是大端端口,如果是字符串的大小端转换使用其他函数// INADDR_ANY代表本机的所有IP, 假设有三个网卡就有三个IP地址// 这个宏可以代表任意一个IP地址// 这个宏一般用于本地的绑定操作addr.sin_addr.s_addr = INADDR_ANY; // 这个宏的值为0 == 0.0.0.0int ret = bind(lfd, (struct sockaddr*)&addr, sizeof(addr));//使用强制类型转换if(ret == -1){perror("bind");exit(0);}
(4)设置监听
// 3. 设置监听ret = listen(lfd, 128);//最多128个if(ret == -1){perror("listen");exit(0);}
(5)开始通信
因为我们要实现多线程并发,需要存储多个addr 和fd通信套接字。不是监听套接字。
struct SockInfo
{struct sockaddr_in addr;//地址信息int fd;//文件描述符
};struct SockInfo infos[512];//最多接收256个客户端----------------------------------------
对结构体进行初始化,通信描述符设置为-1是为了判断其有没有被占有
*/// 等待并接受客户端的连接请求, 建立新的连接, 会得到一个新的文件描述符(通信的)
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数:
sockfd: 监听的文件描述符
addr: 传出参数, 里边存储了建立连接的客户端的地址信息
addrlen: 传入传出参数,用于存储addr指向的内存大小
返回值:函数调用成功,得到一个文件描述符, 用于和建立连接的这个客户端通信,调用失败返回 -1
*///初始化结构体大小int max=sizeof(infos)/sizeof(infos[0]);for(int i=0;i<max;++i){memset(&infos[i],0,max);//将结构体全部设置为0infos[i].fd=-1;//通信描述符设置为-1}
(6)初始化完毕之后需要开始进行进行通信操作
int clilen = sizeof(struct sockaddr_in);
定义一个结构体指针,如果通信套接字fd=-1的话,说明没有被占有,因为如果占用了就会返回fd套接字,失败才返回-1.
所以如果-1的话,就令这个指针指向一个info数组元素。
struct SockInfo* pinfo;//结构体指针for(int i=0;i<max;++i){if(infos[i].fd==-1){pinfo=&infos[i];break;}}
开始进行accept(),如果成功了就开辟子线程,把参数pinfo指针指向的内存数据传递给子线程,其实就是对应IP地址,端口,通信协议等数据。
int cfd = accept(lfd, (struct sockaddr*)&pinfo->addr, &clilen);//并且是个阻塞函数pinfo->fd=cfd;if(cfd == -1){perror("accept");break;}//如果连接已经成功了我们需要创建一个子线程,来处理这个客户端连接的数据pthread_t tid;pthread_create(&tid,NULL,working,pinfo);//通过pinfo指针变量传递数据给子线程pthread_detach(tid);//避免阻塞在这里,使用线程分离
主线程结束了说明通信服务器已经关闭,close监听套接字。
close(lfd);//监听的文件描述符,通信的不需要关闭,因为子线程需要使用
(7)子线程就是对数据的读取操作了。
//子线程函数
void* working(void* arg)
{ struct SockInfo*pinfo=(struct SockInfo*)arg;// 打印客户端的地址信息char ip[24] = {0};printf("客户端的IP地址: %s, 端口: %d\n",inet_ntop(AF_INET, &pinfo->addr.sin_addr.s_addr, ip, sizeof(ip)),ntohs(pinfo->addr.sin_port));// 5. 和客户端通信while(1){// 接收数据char buf[1024];memset(buf, 0, sizeof(buf));int len = read(pinfo->fd, buf, sizeof(buf));//阻塞函数,cfd是通信的文件描述符accept的函数返回值if(len > 0){printf("客户端say: %s\n", buf);write(pinfo->fd, buf, len);//发送数据的函数}else if(len == 0){printf("客户端断开了连接...\n");break;}else{perror("read");break;}}close(pinfo->fd);pinfo->fd=-1;return 0;
}
全部代码,server_muti
// server_muti.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>//多线程的头文件void* working(void*arg);
struct SockInfo
{struct sockaddr_in addr;//地址信息int fd;//文件描述符
};struct SockInfo infos[512];int main()
{// 1. 创建监听的套接字 https://subingwen.cn/linux/socket/。返回的是文件描述符int lfd = socket(AF_INET, SOCK_STREAM, 0);if(lfd == -1){perror("socket");exit(0);}// 2. 将socket()返回值和本地的IP端口绑定到一起struct sockaddr_in addr;addr.sin_family = AF_INET;//地址族协议addr.sin_port = htons(10000); // 大端端口,要使用的是大端端口,如果是字符串的大小端转换使用其他函数// INADDR_ANY代表本机的所有IP, 假设有三个网卡就有三个IP地址// 这个宏可以代表任意一个IP地址// 这个宏一般用于本地的绑定操作addr.sin_addr.s_addr = INADDR_ANY; // 这个宏的值为0 == 0.0.0.0int ret = bind(lfd, (struct sockaddr*)&addr, sizeof(addr));//使用强制类型转换if(ret == -1){perror("bind");exit(0);}// 3. 设置监听ret = listen(lfd, 128);if(ret == -1){perror("listen");exit(0);}//初始化结构体大小int max=sizeof(infos)/sizeof(infos[0]);for(int i=0;i<max;++i){memset(&infos[i],0,max);//将结构体全部设置为0infos[i].fd=-1;//通信描述符设置为-1}// 4. 阻塞等待并接受客户端连接//struct sockaddr_in cliaddr;int clilen = sizeof(struct sockaddr_in);while(1){ struct SockInfo* pinfo;//结构体指针for(int i=0;i<max;++i){if(infos[i].fd==-1){pinfo=&infos[i];break;}}int cfd = accept(lfd, (struct sockaddr*)&pinfo->addr, &clilen);//并且是个阻塞函数pinfo->fd=cfd;if(cfd == -1){perror("accept");break;}//如果连接已经成功了我们需要创建一个子线程,来处理这个客户端连接的数据pthread_t tid;pthread_create(&tid,NULL,working,pinfo);//通过pinfo指针变量传递数据给子线程pthread_detach(tid);//避免阻塞在这里,使用线程分离}close(lfd);//监听的文件描述符,通信的不需要关闭,因为子线程需要使用return 0;
}//子线程函数
void* working(void* arg)
{ struct SockInfo*pinfo=(struct SockInfo*)arg;// 打印客户端的地址信息char ip[24] = {0};printf("客户端的IP地址: %s, 端口: %d\n",inet_ntop(AF_INET, &pinfo->addr.sin_addr.s_addr, ip, sizeof(ip)),ntohs(pinfo->addr.sin_port));// 5. 和客户端通信while(1){// 接收数据char buf[1024];memset(buf, 0, sizeof(buf));int len = read(pinfo->fd, buf, sizeof(buf));//阻塞函数,cfd是通信的文件描述符accept的函数返回值if(len > 0){printf("客户端say: %s\n", buf);write(pinfo->fd, buf, len);//发送数据的函数}else if(len == 0){printf("客户端断开了连接...\n");break;}else{perror("read");break;}}close(pinfo->fd);pinfo->fd=-1;return 0;
}
client.c
// client.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>int main()
{// 1. 创建通信的套接字int fd = socket(AF_INET, SOCK_STREAM, 0);if(fd == -1){perror("socket");exit(0);}// 2. 连接服务器struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(10000); // 大端端口inet_pton(AF_INET, "你的地址", &addr.sin_addr.s_addr);int ret = connect(fd, (struct sockaddr*)&addr, sizeof(addr));if(ret == -1){perror("connect");exit(0);}// 3. 和服务器端通信int number = 0;while(1){// 发送数据char buf[1024];sprintf(buf, "你好, 服务器...%d\n", number++);write(fd, buf, strlen(buf)+1);// 接收数据memset(buf, 0, sizeof(buf));int len = read(fd, buf, sizeof(buf));if(len > 0){printf("服务器say: %s\n", buf);}else if(len == 0){printf("服务器断开了连接...\n");break;}else{perror("read");break;}sleep(1); // 每隔1s发送一条数据}close(fd);return 0;
}
这里借鉴了爱编程的大丙博主的程序。
套接字通信
Linux多线程多进程编程
相关文章:

Linux多线程网络通信
思路:主线程(只有一个)建立连接,就创建子线程。子线程开始通信。 共享资源:全局数据区,堆区,内核区描述符。 线程同步不同步需要取决于线程对共享资源区的数据的操作,如果是只读就不…...

矩阵的c++实现(2)
上一次我们了解了矩阵的运算和如何使用矩阵解决斐波那契数列,这一次我们多看看例题,了解什么情况下用矩阵比较合适。 先看例题 1.洛谷P1939 【模板】矩阵加速(数列) 模板题应该很简单。 补:1<n<10^9 10^9肯定…...

RPC 框架之Thrift入门(一)
📋 个人简介 💖 作者简介:大家好,我是阿牛,全栈领域优质创作者。😜📝 个人主页:馆主阿牛🔥🎉 支持我:点赞👍收藏⭐️留言Ὅ…...

【C++】运算符重载 ⑥ ( 一元运算符重载 | 后置运算符重载 | 前置运算符重载 与 后置运算符重载 的区别 | 后置运算符重载添加 int 占位参数 )
文章目录 一、后置运算符重载1、前置运算符重载 与 后置运算符重载 的区别2、后置运算符重载添加 int 占位参数 上 2 2 2 篇博客 【C】运算符重载 ④ ( 一元运算符重载 | 使用 全局函数 实现 前置 自增运算符重载 | 使用 全局函数 实现 前置 - - 自减运算符重载 )【C】运算符…...

538. 把二叉搜索树转换为累加树
题目描述 给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。 提醒一下,二叉搜索树满足下列约束…...
java8日期时间工具类
【README】 1)本文总结了java8中日期时间常用工具方法;包括: 日期时间对象格式化为字符串;日期时间字符串解析为日期时间对象;日期时间对象转换; 转换过程中,需要注意的是: Instan…...

算法-动态规划/trie树-单词拆分
算法-动态规划/trie树-单词拆分 1 题目概述 1.1 题目出处 https://leetcode.cn/problems/word-break/description/?envTypestudy-plan-v2&envIdtop-interview-150 1.2 题目描述 2 动态规划 2.1 解题思路 dp[i]表示[0, i)字符串可否构建那么dp[i]可构建的条件是&…...

React框架核心原理
一、整体架构 三大核心库与对应的组件 history -> react-router -> react-router-dom react-router 可视为react-router-dom 的核心,里面封装了<Router>,<Route>,<Switch>等核心组件,实现了从路由的改变到组件的更新…...
python-pytorch 利用pytorch对堆叠自编码器进行训练和验证
利用pytorch对堆叠自编码器进行训练和验证 一、数据生成二、定义自编码器模型三、训练函数四、训练堆叠自编码器五、将已训练的自编码器级联六、微调整个堆叠自编码器 一、数据生成 随机生成一些数据来模拟训练和验证数据集: import torch# 随机生成数据 n_sample…...

制作 3 档可调灯程序编写
PWM 0~255 可以将数据映射到0 75 150 225 尽可能均匀电压间隔...
源码分享-M3U8数据流ts的AES-128解密并合并---GoLang实现
之前使用C语言实现了一次,见M3U8数据流ts的AES-128解密并合并。 学习了Go语言后,又用Go重新实现了一遍。源码如下,无第三方库依赖。 package mainimport ("crypto/aes""crypto/cipher""encoding/binary"&quo…...
CSDN Q: “这段代码算是在STC89C52RC51单片机上完成PWM呼吸灯了吗?“
这是 CSDN上的一个问题 这段代码算是在STC89C52RC51单片机上完成PWM呼吸灯了吗,还是说得用上定时器和中断函数#include <regx52.h> 我个人认为: 效果上来说, 是的! 码以 以Time / 100-Time 调 Duty, 而 for i loop成 Period, 加上延时, 实现了 PWM周期, 虽然…...

Linux系统编程系列之线程池
Linux系统编程系列(16篇管饱,吃货都投降了!) 1、Linux系统编程系列之进程基础 2、Linux系统编程系列之进程间通信(IPC)-信号 3、Linux系统编程系列之进程间通信(IPC)-管道 4、Linux系统编程系列之进程间通信-IPC对象 5、Linux系统…...

Linux CentOS7 vim多文件与多窗口操作
窗口是可视化的分割区域。Windows中窗口的概念与linux中基本相同。连接xshell就是在Windows中新建一个窗口。而vim打开一个文件默认创建一个窗口。同时,Vim打开一个文件也就会建立一个缓冲区,打开多个文件就会创建多个缓冲区。 本文讨论vim中打开多个文…...

SPI 通信协议
1. SPI通信 1. 什么是SPI通信协议 2. SPI的通信过程 在一开始会先把发送缓冲器的数据(8位)。一次性放到移位寄存器里。 移位寄存器会一位一位发送出去。但是要先放到锁存器里。然后从机来读取。从机的过程也一样。当移位寄存器的数据全部发送完。其实…...

【图像处理】使用各向异性滤波器和分割图像处理从MRI图像检测脑肿瘤(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

5个适合初学者的初级网络安全工作,网络安全就业必看
前言 网络安全涉及保护计算机系统、网络和数据免受未经授权的访问、破坏和盗窃 - 防止数字活动和数据访问的中断 - 同时也保护用户的资产和隐私。鉴于公共事业、医疗保健、金融以及联邦政府等行业的网络犯罪攻击不断升级,对网络专业人员的需求很高,这并…...
Kafka核心原理
1、Topic的分片和副本机制 分片作用: 解决单台节点容量有限的问题,节点多,效率提升,吞吐量提升。通过分片,将一个大的容器分解为多个小的容器,分布在不同的节点上,从而实现分布式存储。 分片…...

探秘前后端开发世界:猫头虎带你穿梭编程的繁忙街区,解锁全栈之路
🌷🍁 博主猫头虎 带您 Go to New World.✨🍁 🦄 博客首页——猫头虎的博客🎐 🐳《面试题大全专栏》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 &a…...
洛谷_分支循环
p2433 问题 5 甲列火车长 260 米,每秒行 12 米;乙列火车长220 米,每秒行 20 米,两车相向而行,从两车车头相遇时开始计时,多长时间后两车车尾相离?已知答案是整数。 计算方式:两车车…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘
美国西海岸的夏天,再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至,这不仅是开发者的盛宴,更是全球数亿苹果用户翘首以盼的科技春晚。今年,苹果依旧为我们带来了全家桶式的系统更新,包括 iOS 26、iPadOS 26…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...

高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题
在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件,这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下,实现高效测试与快速迭代?这一命题正考验着…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...

CTF show 数学不及格
拿到题目先查一下壳,看一下信息 发现是一个ELF文件,64位的 用IDA Pro 64 打开这个文件 然后点击F5进行伪代码转换 可以看到有五个if判断,第一个argc ! 5这个判断并没有起太大作用,主要是下面四个if判断 根据题目…...

2025-05-08-deepseek本地化部署
title: 2025-05-08-deepseek 本地化部署 tags: 深度学习 程序开发 2025-05-08-deepseek 本地化部署 参考博客 本地部署 DeepSeek:小白也能轻松搞定! 如何给本地部署的 DeepSeek 投喂数据,让他更懂你 [实验目的]:理解系统架构与原…...
Vue 实例的数据对象详解
Vue 实例的数据对象详解 在 Vue 中,数据对象是响应式系统的核心,也是组件状态的载体。理解数据对象的原理和使用方式是成为 Vue 专家的关键一步。我将从多个维度深入剖析 Vue 实例的数据对象。 一、数据对象的定义方式 1. Options API 中的定义 在 Options API 中,使用 …...

java 局域网 rtsp 取流 WebSocket 推送到前端显示 低延迟
众所周知 摄像头取流推流显示前端延迟大 传统方法是服务器取摄像头的rtsp流 然后客户端连服务器 中转多了,延迟一定不小。 假设相机没有专网 公网 1相机自带推流 直接推送到云服务器 然后客户端拉去 2相机只有rtsp ,边缘服务器拉流推送到云服务器 …...