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 米,两车相向而行,从两车车头相遇时开始计时,多长时间后两车车尾相离?已知答案是整数。 计算方式:两车车…...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...
《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)
CSI-2 协议详细解析 (一) 1. CSI-2层定义(CSI-2 Layer Definitions) 分层结构 :CSI-2协议分为6层: 物理层(PHY Layer) : 定义电气特性、时钟机制和传输介质(导线&#…...
学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...
生成 Git SSH 证书
🔑 1. 生成 SSH 密钥对 在终端(Windows 使用 Git Bash,Mac/Linux 使用 Terminal)执行命令: ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" 参数说明: -t rsa&#x…...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...
Springboot社区养老保险系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,社区养老保险系统小程序被用户普遍使用,为方…...
NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合
在汽车智能化的汹涌浪潮中,车辆不再仅仅是传统的交通工具,而是逐步演变为高度智能的移动终端。这一转变的核心支撑,来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒(T-Box)方案:NXP S32K146 与…...
JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...
AGain DB和倍数增益的关系
我在设置一款索尼CMOS芯片时,Again增益0db变化为6DB,画面的变化只有2倍DN的增益,比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析: 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...
算法:模拟
1.替换所有的问号 1576. 替换所有的问号 - 力扣(LeetCode) 遍历字符串:通过外层循环逐一检查每个字符。遇到 ? 时处理: 内层循环遍历小写字母(a 到 z)。对每个字母检查是否满足: 与…...
