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

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多线程网络通信

思路&#xff1a;主线程&#xff08;只有一个&#xff09;建立连接&#xff0c;就创建子线程。子线程开始通信。 共享资源&#xff1a;全局数据区&#xff0c;堆区&#xff0c;内核区描述符。 线程同步不同步需要取决于线程对共享资源区的数据的操作&#xff0c;如果是只读就不…...

矩阵的c++实现(2)

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

RPC 框架之Thrift入门(一)

&#x1f4cb; 个人简介 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是阿牛&#xff0c;全栈领域优质创作者。&#x1f61c;&#x1f4dd; 个人主页&#xff1a;馆主阿牛&#x1f525;&#x1f389; 支持我&#xff1a;点赞&#x1f44d;收藏⭐️留言&#x1f4d…...

【C++】运算符重载 ⑥ ( 一元运算符重载 | 后置运算符重载 | 前置运算符重载 与 后置运算符重载 的区别 | 后置运算符重载添加 int 占位参数 )

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

538. 把二叉搜索树转换为累加树

题目描述 给出二叉 搜索 树的根节点&#xff0c;该树的节点值各不相同&#xff0c;请你将其转换为累加树&#xff08;Greater Sum Tree&#xff09;&#xff0c;使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。 提醒一下&#xff0c;二叉搜索树满足下列约束…...

java8日期时间工具类

【README】 1&#xff09;本文总结了java8中日期时间常用工具方法&#xff1b;包括&#xff1a; 日期时间对象格式化为字符串&#xff1b;日期时间字符串解析为日期时间对象&#xff1b;日期时间对象转换&#xff1b; 转换过程中&#xff0c;需要注意的是&#xff1a; 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 的核心&#xff0c;里面封装了<Router>&#xff0c;<Route>&#xff0c;<Switch>等核心组件,实现了从路由的改变到组件的更新…...

python-pytorch 利用pytorch对堆叠自编码器进行训练和验证

利用pytorch对堆叠自编码器进行训练和验证 一、数据生成二、定义自编码器模型三、训练函数四、训练堆叠自编码器五、将已训练的自编码器级联六、微调整个堆叠自编码器 一、数据生成 随机生成一些数据来模拟训练和验证数据集&#xff1a; import torch# 随机生成数据 n_sample…...

制作 3 档可调灯程序编写

PWM 0~255 可以将数据映射到0 75 150 225 尽可能均匀电压间隔...

源码分享-M3U8数据流ts的AES-128解密并合并---GoLang实现

之前使用C语言实现了一次&#xff0c;见M3U8数据流ts的AES-128解密并合并。 学习了Go语言后&#xff0c;又用Go重新实现了一遍。源码如下&#xff0c;无第三方库依赖。 package mainimport ("crypto/aes""crypto/cipher""encoding/binary"&quo…...

CSDN Q: “这段代码算是在STC89C52RC51单片机上完成PWM呼吸灯了吗?“

这是 CSDN上的一个问题 这段代码算是在STC89C52RC51单片机上完成PWM呼吸灯了吗&#xff0c;还是说得用上定时器和中断函数#include <regx52.h> 我个人认为: 效果上来说, 是的! 码以 以Time / 100-Time 调 Duty, 而 for i loop成 Period, 加上延时, 实现了 PWM周期, 虽然…...

Linux系统编程系列之线程池

Linux系统编程系列&#xff08;16篇管饱&#xff0c;吃货都投降了&#xff01;&#xff09; 1、Linux系统编程系列之进程基础 2、Linux系统编程系列之进程间通信(IPC)-信号 3、Linux系统编程系列之进程间通信(IPC)-管道 4、Linux系统编程系列之进程间通信-IPC对象 5、Linux系统…...

Linux CentOS7 vim多文件与多窗口操作

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

SPI 通信协议

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

【图像处理】使用各向异性滤波器和分割图像处理从MRI图像检测脑肿瘤(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

5个适合初学者的初级网络安全工作,网络安全就业必看

前言 网络安全涉及保护计算机系统、网络和数据免受未经授权的访问、破坏和盗窃 - 防止数字活动和数据访问的中断 - 同时也保护用户的资产和隐私。鉴于公共事业、医疗保健、金融以及联邦政府等行业的网络犯罪攻击不断升级&#xff0c;对网络专业人员的需求很高&#xff0c;这并…...

Kafka核心原理

1、Topic的分片和副本机制 分片作用&#xff1a; 解决单台节点容量有限的问题&#xff0c;节点多&#xff0c;效率提升&#xff0c;吞吐量提升。通过分片&#xff0c;将一个大的容器分解为多个小的容器&#xff0c;分布在不同的节点上&#xff0c;从而实现分布式存储。 分片…...

探秘前后端开发世界:猫头虎带你穿梭编程的繁忙街区,解锁全栈之路

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…...

洛谷_分支循环

p2433 问题 5 甲列火车长 260 米&#xff0c;每秒行 12 米&#xff1b;乙列火车长220 米&#xff0c;每秒行 20 米&#xff0c;两车相向而行&#xff0c;从两车车头相遇时开始计时&#xff0c;多长时间后两车车尾相离&#xff1f;已知答案是整数。 计算方式&#xff1a;两车车…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?

论文网址&#xff1a;pdf 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&#xff0c;若有发现欢迎评论指正&#xff01;文章偏向于笔记&#xff0c;谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)

可以使用Sqliteviz这个网站免费编写sql语句&#xff0c;它能够让用户直接在浏览器内练习SQL的语法&#xff0c;不需要安装任何软件。 链接如下&#xff1a; sqliteviz 注意&#xff1a; 在转写SQL语法时&#xff0c;关键字之间有一个特定的顺序&#xff0c;这个顺序会影响到…...

镜像里切换为普通用户

如果你登录远程虚拟机默认就是 root 用户&#xff0c;但你不希望用 root 权限运行 ns-3&#xff08;这是对的&#xff0c;ns3 工具会拒绝 root&#xff09;&#xff0c;你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案&#xff1a;创建非 roo…...

Axios请求超时重发机制

Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式&#xff1a; 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配

AI3D视觉的工业赋能者 迁移科技成立于2017年&#xff0c;作为行业领先的3D工业相机及视觉系统供应商&#xff0c;累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成&#xff0c;通过稳定、易用、高回报的AI3D视觉系统&#xff0c;为汽车、新能源、金属制造等行…...

多种风格导航菜单 HTML 实现(附源码)

下面我将为您展示 6 种不同风格的导航菜单实现&#xff0c;每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...

有限自动机到正规文法转换器v1.0

1 项目简介 这是一个功能强大的有限自动机&#xff08;Finite Automaton, FA&#xff09;到正规文法&#xff08;Regular Grammar&#xff09;转换器&#xff0c;它配备了一个直观且完整的图形用户界面&#xff0c;使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...

Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信

文章目录 Linux C语言网络编程详细入门教程&#xff1a;如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket&#xff08;服务端和客户端都要&#xff09;2. 绑定本地地址和端口&#x…...

iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈

在日常iOS开发过程中&#xff0c;性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期&#xff0c;开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发&#xff0c;但背后往往隐藏着系统资源调度不当…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析

Linux 内存管理实战精讲&#xff1a;核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用&#xff0c;还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...