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

Linux 多线程解决客户端与服务器端通信

一、一个服务器端只能和一个客户端进行通信(单线程模式)

客户端代码ser.c如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>int main()
{//1.创建套接字int sockfd=socket(AF_INET,SOCK_STREAM,0);//监听套接字if(sockfd==-1){printf("创建失败\n");exit(1);}//定义套接字地址struct sockaddr_in saddr,caddr;memset(&saddr,0,sizeof(saddr));saddr.sin_family=AF_INET;saddr.sin_port=htons(6000);saddr.sin_addr.s_addr=inet_addr("127.0.0.1");//2.指定套接字ip地址int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));if(res==-1){printf("绑定失败\n");exit(1);}//3.创建监听队列res=listen(sockfd,5);if(res==-1){printf("监听队列创建失败\n");exit(1);}while(1){int len = sizeof(caddr);//4.接收客户端的连接int c =accept(sockfd,(struct sockaddr*)&caddr,&len);//连接套接字if(c<0){continue;}printf("c=%d\n",c);while(1){char buff[128]={0};//5.接收客户端的消息int num=recv(c,buff,127,0);//recv的返回值是实际收到的字节数   //可能阻塞if(num<=0){break;}printf("buff=%s\n",buff);//6.向客户端回复消息send(c,"ok",2,0);}printf("关闭客户端\n");//7.关闭客户端close(c);}
}

客户端代码cli.c如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>int main()
{//1.创建套接字int sockfd=socket(AF_INET,SOCK_STREAM,0);if(sockfd==-1){printf("创建失败\n");}//定义套接字地址struct sockaddr_in saddr;memset(&saddr,0,sizeof(saddr));saddr.sin_family=AF_INET;saddr.sin_port=htons(6000);saddr.sin_addr.s_addr=inet_addr("127.0.0.1");//2.连接服务器端int res=connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));if(res==-1){printf("连接失败\n");exit(1);}while(1){printf("输入:");char buff[128]={0};fgets(buff,128,stdin);if(strncmp(buff,"end",3)==0){break;}//3.向服务器端发送消息send(sockfd,buff,strlen(buff),0);memset(buff,0,128);//4.接收服务器端回复的消息recv(sockfd,buff,127,0);printf("buff=%s\n",buff);}//5.关闭服务器端close(sockfd);exit(0);}

运行结果:

先在一个终端启动服务器端,然后在另一个终端启动客户端,此时,客户端就可以与服务器端进行通信:

在这里插入图片描述

二、接收缓冲区和发送缓冲区

每一个套接字都有两个缓冲区,即发送缓冲区和接收缓冲区。也就是说服务器端和客户端两端都有自己的发送缓冲区和接收缓冲区。

比如说客户端通过send向服务器端发送数据,那么这个数据就会被写入发送缓冲区中,只要send返回成功就说明所要发送的数据已经成功写入发送缓冲区中。发送缓冲区要通过底层网络协议把数据通过网络发送到服务器端的接收缓冲区中,此时服务器端的接收缓冲区中就放着从客户端发送来的数据,当服务器端执行recv的时候,服务器端会将客户端所发送来的数据接收出来,如果接收缓冲区是空的,那么recv就接受不到数据。反之,也一样。

套接字是一个全双工的通信方式。

将上述服务器端代码ser.c中的int num=recv(c,buff,127,0);这一行代码修改为int num=recv(c,buff,1,0);,此时启动服务器端和客户端:

在这里插入图片描述

通过命令netstat -natp来查看接收缓冲区和发送缓冲区还有没有数据。

三、两个客户端要与一个服务器端进行通信(多线程模式)

1.代码还是上述代码,但是运行结果失败

代码如上,运行结果如下:

在这里插入图片描述

从结果可以看出,第二个客户端的printf("输入:");这一行代码已经执行过了,说明此时connect已经执行成功,说明三次握手已经完成,建立了TCP连接,将这个建立好的连接放到了已完成三次握手的监听队列中,等待服务器端执行accept进行接收。第二个客户端send(sockfd,buff,strlen(buff),0);这一行代码也已经执行过了,但是send执行成功并不意味之已经把消息发送给了服务器端,而是说明所发送的数据现在已经存到了该客户端的发送缓冲区中,而此时服务器端此时也发送阻塞,阻塞在int num=recv(c,buff,127,0);这一行代码的位置,因为此时第一个客户端没有发送消息,所以客户端执行recv的时候发送阻塞,所以服务器端就没有机会执行accept去接收已完成三次握手的监听队列中与第二个客户端建立的连接,因此服务器端的接收缓冲区此时也接收不到第二个客户端从发送缓冲区发送来的数据,所以第二个客户端在执行recv的时候接收不到服务器端给它回复的消息而被阻塞。

当我们关闭第一个客户端之后,服务器端代码就退出小while循环,执行printf("关闭客户端\n");关闭与第一个客户端的连接,此时服务器端就可以执行accept接收已完成三次握手的监听队列中与第二个客户端建立的连接,然后接收到第二个客户端发送到服务器端的信息并输出,客户端也会得到服务器端的回复:

在这里插入图片描述

2.同时让两个客户端与服务器端进行通信成功

方法:再创建一个线程与第二个客户端连接。服务端接受一个客户端的连接后,创建
一个线程,然后在新创建的线程中循环处理数据。主线程只负责监听客户端的连接,并使用accept()接受连接,不进行数据的处理。如下图所示:

在这里插入图片描述

服务器端代码ser.c如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<pthread.h>//创建一个结构体,把想要传给线程函数的参数全部放到这个结构体中
struct Node_Arg
{int c;//连接套接字的描述符
};void* fun(void* arg)
{struct Node_Arg* p=(struct Node_Arg*)arg;int c=p->c;while(1){char buff[128]={0};//5.接收客户端发送来的信息int num=recv(c,buff,127,0);if(num<=0){break;}printf("buff(c=%d)=%s\n",c,buff);//6.向客户端回复信息send(c,"ok",2,0);}printf("关闭客户端\n");//7.关闭客户端close(c);free(p);//释放堆区空间}int main()
{//1.创建套接字int sockfd=socket(AF_INET,SOCK_STREAM,0);//监听套接字if(sockfd==-1){printf("创建失败\n");exit(1);}//定义套接字地址struct sockaddr_in saddr,caddr;memset(&saddr,0,sizeof(saddr));saddr.sin_family=AF_INET;saddr.sin_port=htons(6000);saddr.sin_addr.s_addr=inet_addr("127.0.0.1");//2.指定套接字ip地址int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));if(res==-1){printf("绑定失败\n");exit(1);}//3.创建监听队列res=listen(sockfd,5);if(res==-1){printf("监听队列创建失败\n");exit(1);}while(1){int len = sizeof(caddr);//4.接收客户端的连接int c =accept(sockfd,(struct sockaddr*)&caddr,&len);//连接套接字if(c<0){continue;}printf("c=%d\n",c);pthread_t id;//在堆区为结构体struct Node_Arg申请一块空间struct Node_Arg*ptr=(struct Node_Arg*)malloc(sizeof(struct Node_Arg));ptr->c=c;pthread_create(&id,NULL,fun,ptr);}}

客户端代码没有改变cli.c如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>int main()
{//1.创建套接字int sockfd=socket(AF_INET,SOCK_STREAM,0);if(sockfd==-1){printf("创建失败\n");}//定义套接字地址struct sockaddr_in saddr;memset(&saddr,0,sizeof(saddr));saddr.sin_family=AF_INET;saddr.sin_port=htons(6000);saddr.sin_addr.s_addr=inet_addr("127.0.0.1");//2.连接服务器端int res=connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));if(res==-1){printf("连接失败\n");exit(1);}while(1){printf("输入:");char buff[128]={0};fgets(buff,128,stdin);if(strncmp(buff,"end",3)==0){break;}//3.向服务器端发送消息send(sockfd,buff,strlen(buff),0);memset(buff,0,128);//4.接收服务器端回复的消息recv(sockfd,buff,127,0);printf("buff=%s\n",buff);}//5.关闭服务器端close(sockfd);exit(0);}

运行结果:

在这里插入图片描述

根据结果可以看出,在启动服务器端之后,可以启动两个客户端与服务器端进行通信。

查看客户端与服务器端通信时的线程的数量:

通过命令ps -eLf | grep "ser"查看线程数量,可以看到一共有三个线程,其中有一个是主线程还有两个线程分别接收两个客户端发来的信息。

在这里插入图片描述

相关文章:

Linux 多线程解决客户端与服务器端通信

一、一个服务器端只能和一个客户端进行通信&#xff08;单线程模式&#xff09; 客户端代码ser.c如下&#xff1a; #include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<sys/socket.h> #include<netinet…...

FMX的TListBox单选列表框

TListBox功能比较全&#xff0c;对于选择项&#xff0c;有“两种”模式&#xff0c;一种就是ListItem选中&#xff08;界面上就是焦点和颜色变化&#xff09;&#xff0c;可以无&#xff0c;单选和多选。另一种是通过CheckBox来选择ListItem的选中。默认下&#xff0c;ShowChec…...

prompt工程(持续更新ing...)

诸神缄默不语-个人CSDN博文目录 我准备想办法把这些东西整合到我的ScholarEase项目里。到时候按照分类、按照prompt生成方法列一堆选项&#xff0c;用户自己生成prompt后可以选择在ScholarEase里面聊天&#xff0c;也可以复制到别的地方&#xff08;比如ChatGPT网页版之类的&a…...

win11 docker-desktop安装记录

win11安装Docker踩坑实录 马上开始正式工作了&#xff0c;需要用到docker&#xff0c;以前在win10上安装过&#xff0c;新电脑是win11&#xff0c;心想肯定会遇到坑&#xff0c;就浅浅记录一下 首先看一下安装要求 需要wsl2 那么就先进行 wsl的更新 wsl --update注意这里网络…...

opencv特征提取、梯度计算

...

AI绘画工具MJ新功能有点东西,小白也能轻松一键换装

先看最终做出来的效果 直接来干货吧。Midjourney&#xff0c;下面简称MJ 1.局部重绘功能来袭 就在前两天&#xff0c;MJ悄咪咪上线了这个被众人期待的新功能&#xff1a;局部重绘。 对于那些追求创新和个性化的设计师来说&#xff0c;局部绘制不仅是一个实用的功能&#xff…...

java springboot sql防注入的6种方式

​在Spring Boot中&#xff0c;可以通过使用参数绑定、预处理语句和使用ORM框架等方式来防止SQL注入。以下是几种常见的方式&#xff1a; 1. 参数绑定&#xff1a;通过使用参数绑定&#xff0c;将用户输入的数据作为参数传递给SQL语句&#xff0c;而不是将其直接拼接到SQL语句中…...

深度学习实战49-基于卷积神经网络和注意力机制的汽车品牌与型号分类识别的应用

大家好,我是微学AI,今天给大家介绍一下深度学习实战49-基于卷积神经网络和注意力机制的汽车品牌与型号分类识别的应用,该项目就像是一只智慧而敏锐的眼睛,专注地凝视着汽车世界。这个项目使用PyTorch作为强有力的工具,提供了一个深度学习的舞台,让我们能够设计和训练一个…...

Open3D(C++) 可视化(3)——批量动态可视化点云

目录 一、概述二、代码实现三、结果展示本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫。 一、概述 拿到一个新的点云数据集,想要快速查看数据集内点云的形状特征。然而,对于动辄几千个点云的数据集而言,逐个将点云拖入…...

opencv 文档识别+UI界面识别系统

目录 一、实现和完整UI视频效果展示 主界面&#xff1a; 识别结果界面&#xff1a; 查看处理图片过程&#xff1a; 查看历史记录界面&#xff1a; 二、原理介绍&#xff1a; 将图像变换大小->灰度化->高斯滤波->边缘检测 轮廓提取 筛选第三步中的轮廓&#xf…...

下|税收大数据应用研究

上文呢&#xff0c;对于税收大数据我们已经对它有了一定程度的认知。下篇呢&#xff0c;就研究一下应用方面有哪些优势和存在的不足之处。 一、税收大数据应用的优势 1.提升征管效率和预测准确率 税收部门通过收集、分析海量数据。并建立数据分析模型来提升效率和准确率。税…...

数据库连接池druid 的jar包官网下载-最新版下载

进入官网Central Repository: com/alibaba/druid 往下滑 找到最新版点击进入 找到该jar包 点击即可下载...

2023河南萌新联赛第(六)场:河南理工大学 C - 旅游

2023河南萌新联赛第&#xff08;六&#xff09;场&#xff1a;河南理工大学 C - 旅游 时间限制&#xff1a;C/C 1秒&#xff0c;其他语言2秒 空间限制&#xff1a;C/C 262144K&#xff0c;其他语言524288K Special Judge, 64bit IO Format: %lld 题目描述 小C喜欢旅游&#xf…...

Java | IDEA中Netty运行多个client的方法

想要运行多个client但出现这种提示&#xff1a; 解决方法 1、打开IDEA&#xff0c;右上角找到下图&#xff0c;并点击 2、勾选...

【蓝桥杯】 [蓝桥杯 2015 省 A] 饮料换购

原题链接&#xff1a;https://www.luogu.com.cn/problem/P8627 1. 题目描述 2. 思路分析 小伙伴们如果没有思路可以看看这篇文章~&#xff08;这里很详细讲解了三种方法&#xff01;&#xff09; https://blog.csdn.net/m0_62531913/article/details/132385341?spm1001.2014…...

操作系统-笔记-第三章-内存管理

&#x1f338;章节汇总 一、第一章——操作系统的概念 二、第二章——【进程】 二、第二章——【线程】​编辑 二、第二章——【进程调度】 二、第二章——【进程同步与互斥】 二、第二章——【锁】 三、第三章——内存管理 四、第四章——文件管理 五、第五章——输入输出管理…...

详解单体架构和微服务(概念,优缺点和区别)

单体架构和微服务 单体架构和微服务架构区别&#xff1f;为什么要用微服务架构&#xff1f; 单体架构的整个系统是一个War包&#xff0c;即war包走天下。微服务架构的项目是很多个war包&#xff08;一个子系统一个&#xff09;。 单体架构的优点: 架构简单开发测试部署简单…...

储能运行约束的Matlab建模方法

最近一段时间有很多人问我最优潮流计算中储能系统的建模方法。部分朋友的问题我回复了&#xff0c;有些没有回消息的&#xff0c;我就不再一一回复了&#xff0c;在这里我写一篇博客统一介绍一下。 1.储能系统介绍 首先&#xff0c;让【GPT】简单介绍一下储能系统&#xff1a;…...

微信小程序 车牌号输入组件

概述 一个小组件&#xff0c;用于方便用户输入车牌号码 详细 概述 有时候我们开发过程中会遇到需要用户输入车牌号的情况&#xff0c;让客户通过自带键盘输入&#xff0c;体验不好且容易出错&#xff0c;例如车牌号是不能输入O和I的&#xff0c;因此需要有一个自定义的键盘…...

Bootstrap Blazor 实战动态表单组件

1.新建工程 源码 新建工程b18ValidateForm,使用 nuget.org 进行 BootstrapBlazor 组件安装, Chart 库,字体. 将项目添加到解决方案中 dotnet new blazorserver -o b18ValidateForm dotnet add b06chart package BootstrapBlazor dotnet add b06chart package BootstrapBlazo…...

地震勘探——干扰波识别、井中地震时距曲线特点

目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波&#xff1a;可以用来解决所提出的地质任务的波&#xff1b;干扰波&#xff1a;所有妨碍辨认、追踪有效波的其他波。 地震勘探中&#xff0c;有效波和干扰波是相对的。例如&#xff0c;在反射波…...

Debian系统简介

目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版&#xff…...

Day131 | 灵神 | 回溯算法 | 子集型 子集

Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 笔者写过很多次这道题了&#xff0c;不想写题解了&#xff0c;大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...

大学生职业发展与就业创业指导教学评价

这里是引用 作为软工2203/2204班的学生&#xff0c;我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要&#xff0c;而您认真负责的教学态度&#xff0c;让课程的每一部分都充满了实用价值。 尤其让我…...

C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。

1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj&#xff0c;再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...

推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)

推荐 github 项目:GeminiImageApp(图片生成方向&#xff0c;可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...

永磁同步电机无速度算法--基于卡尔曼滤波器的滑模观测器

一、原理介绍 传统滑模观测器采用如下结构&#xff1a; 传统SMO中LPF会带来相位延迟和幅值衰减&#xff0c;并且需要额外的相位补偿。 采用扩展卡尔曼滤波器代替常用低通滤波器(LPF)&#xff0c;可以去除高次谐波&#xff0c;并且不用相位补偿就可以获得一个误差较小的转子位…...

WPF八大法则:告别模态窗口卡顿

⚙️ 核心问题&#xff1a;阻塞式模态窗口的缺陷 原始代码中ShowDialog()会阻塞UI线程&#xff0c;导致后续逻辑无法执行&#xff1a; var result modalWindow.ShowDialog(); // 线程阻塞 ProcessResult(result); // 必须等待窗口关闭根本问题&#xff1a…...

实战设计模式之模板方法模式

概述 模板方法模式定义了一个操作中的算法骨架&#xff0c;并将某些步骤延迟到子类中实现。模板方法使得子类可以在不改变算法结构的前提下&#xff0c;重新定义算法中的某些步骤。简单来说&#xff0c;就是在一个方法中定义了要执行的步骤顺序或算法框架&#xff0c;但允许子类…...

sshd代码修改banner

sshd服务连接之后会收到字符串&#xff1a; SSH-2.0-OpenSSH_9.5 容易被hacker识别此服务为sshd服务。 是否可以通过修改此banner达到让人无法识别此服务的目的呢&#xff1f; 不能。因为这是写的SSH的协议中的。 也就是协议规定了banner必须这么写。 SSH- 开头&#xff0c…...