【Linux】Socket阻塞和非阻塞、同步与异步

news2024/9/20 18:25:38

创作不易,本篇文章如果帮助到了你,还请点赞 关注支持一下♡>𖥦<)!!
主页专栏有更多知识,如有疑问欢迎大家指正讨论,共同进步!
🔥Linux系列专栏:Linux基础 🔥

给大家跳段街舞感谢支持!ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ

在这里插入图片描述


目录

  • 阻塞
  • 非阻塞
  • 同步和异步

阻塞

阻塞模式下,send函数返回时,只代表需要发送的数据已经被拷贝到内核的发送缓冲区

并且如果发送缓冲区有足够的空间,数据可能已经开始从发送缓冲区发送到网络上。但是并不代表数据已经成功地被接收方接收

当发送缓冲区空间不够大的时候,等到发送缓冲区空间足够大再发送

调用recv函数并且套接字没有数据可读时,recv函数会阻塞当前线程,直到有数据可读或者出现错误。

如果 recv 函数返回,返回值代表内核的接收缓冲区中的数据已经被拷贝到用户空间

/* Windows下UDP阻塞socket*/

#include <iostream>
#include <WinSock2.h>
using namespace std;

#pragma comment(lib,"Ws2_32.lib")

int main()
{
	//1.加载库,相当于将dll拷贝到exe路径下
	WORD wVersion = MAKEWORD(2, 2);
	WSADATA wsadata;
	int errcode = 0;
	errcode = WSAStartup(wVersion, &wsadata);
		//1.1判断WSAStartup是否成功
		if (errcode != 0)
		{
			cout << "WSAStartup error:" << errcode << endl;
			return -1;
		}
		//1.2判断版本是否正确
		if (LOBYTE(wsadata.wVersion)!= 2 || HIBYTE(wsadata.wVersion) != 2)
		{
			cout << "WSAStartup wVersion error" << endl;
			WSACleanup();			
				return -1;	
		}
		else //库和版本都加载成功
		{
			cout << "WSAStartup success!" << endl;
		}
	//2.创建套接字
	SOCKET sock;
	sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
		//2.1判断套接字是否创建成功
		if (sock == INVALID_SOCKET)		//INVALID无效的
		{
			cout << "set socket error:" << WSAGetLastError() << endl;
			WSACleanup();
			return -1;
		}
		else   //套接字创建成功
		{
			cout << "set socket success!" << endl;
		}
	//3.绑定ip和端口号
	struct sockaddr_in addrServer;
		addrServer.sin_family = AF_INET;
		addrServer.sin_addr.S_un.S_addr = INADDR_ANY;
		addrServer.sin_port = htons(8080);	//htons将端口号转换为大端模式
	errcode = bind(sock, (sockaddr*)&addrServer, sizeof(addrServer));
		//3.1判断绑定是否成功
		if (errcode == SOCKET_ERROR)
		{
			cout << "bind error:" << WSAGetLastError() << endl;
			//关闭套接字
			closesocket(sock);
			//卸载库
			WSACleanup();
			return -1;
		}
		else
		{
			cout << "bind success!" << endl;
		}
	//4.循环接收发送消息
	int recvNum = 0;
	int sendNum = 0;
	char recvbuf[1024] = "";
	char sendbuf[1024] = "";
	struct sockaddr_in addrClient;
	int addrClientSize = sizeof(addrClient);
	while (true)
	{
		//接收消息
		recvNum = recvfrom(sock, recvbuf, sizeof(recvbuf), 0, (sockaddr*)&addrClient, &addrClientSize);
			//判断接收成功
			if (recvNum > 0)
			{
				//打印收到的数据
				//192.168.1.1——十进制四等分字符串类型ip地址:inet_addr
				//ulong类型ip地址:inet_ntoa
				cout << "来自ip:" << inet_ntoa(addrClient.sin_addr) << " 的消息: " << recvbuf << endl;
			}
			else if (recvNum == 0)		//连接关闭
			{
				cout << "connection closed" << endl;
				break;
			}
			else                        //接收错误
			{
				cout << "recvfrom error:" << WSAGetLastError() << endl;
				break;
			}
		//发送消息
			gets_s(sendbuf);
			sendNum = sendto(sock, sendbuf, sizeof(sendbuf), 0, (sockaddr*)&addrClient, addrClientSize);
			//判断发送成功
			if (sendNum != 0)
			{
				cout << "send success!" << endl;
			}
			else
			{
				cout << "sendto error:" << WSAGetLastError() << endl;
			}
	}
	//5.关闭套接字
	closesocket(sock);
	//6.卸载库
	WSACleanup();
	return 0;
}

阻塞 IO 开发简单,但不适合多个客户端高并发


非阻塞

在非阻塞模式下,send函数将数据放入内核的发送缓冲区,并立即返回

但是,如果发送缓冲区没有足够的空间来容纳全部数据,send函数会返回已经成功拷贝到发送缓冲区的字节数,有多少空间就往里拷贝多少内容,而不是全部要发送的数据

当发送缓冲区空间不够大的时候,有多少空间就往里拷贝多少内容,剩下的数据应用程序自己处理

当调用recv函数并且套接字没有数据可读时,recv函数会立即返回一个错误(EWOULDBLOCK或EAGAIN),而不是阻塞当前线程。

/* Windows下UDP非阻塞socket*/
#include <iostream>
#include <WinSock2.h>
using namespace std;

#pragma comment(lib,"Ws2_32.lib")

int main()
{
	//1.加载库,相当于将dll拷贝到exe路径下
	WORD wVersion = MAKEWORD(2, 2);
	WSADATA wsadata;
	int errcode = 0;
	errcode = WSAStartup(wVersion, &wsadata);
		//1.1判断WSAStartup是否成功
		if (errcode != 0)
		{
			cout << "WSAStartup error:" << errcode << endl;
			return -1;
		}
		//1.2判断版本是否正确
		if (LOBYTE(wsadata.wVersion)!= 2 || HIBYTE(wsadata.wVersion) != 2)
		{
			cout << "WSAStartup wVersion error" << endl;
			WSACleanup();			
				return -1;	
		}
		else //库和版本都加载成功
		{
			cout << "WSAStartup success!" << endl;
		}
	//2.创建套接字
	SOCKET sock;
	sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

	//Windows下设置socket为非阻塞
	u_long val = 1;
	ioctlsocket(sock, FIONBIO, &val);

	//Linux下:
	//int flags = fcntl(sock, F_GETFL, 0);
	//fcntl(sock, F_SETFL, flags | O_NONBLOCK);

		//2.1判断套接字是否创建成功
		if (sock == INVALID_SOCKET)		//INVALID无效的
		{
			cout << "set socket error:" << WSAGetLastError() << endl;
			WSACleanup();
			return -1;
		}
		else   //套接字创建成功
		{
			cout << "set socket success!" << endl;
		}
	//3.绑定ip和端口号
	struct sockaddr_in addrServer;
		addrServer.sin_family = AF_INET;
		addrServer.sin_addr.S_un.S_addr = INADDR_ANY;
		addrServer.sin_port = htons(8080);	//htons将端口号转换为大端模式
	errcode = bind(sock, (sockaddr*)&addrServer, sizeof(addrServer));
		//3.1判断绑定是否成功
		if (errcode == SOCKET_ERROR)
		{
			cout << "bind error:" << WSAGetLastError() << endl;
			//关闭套接字
			closesocket(sock);
			//卸载库
			WSACleanup();
			return -1;
		}
		else
		{
			cout << "bind success!" << endl;
		}
	//4.循环接收发送消息
	int recvNum = 0;
	int sendNum = 0;
	char recvbuf[1024] = "";
	char sendbuf[1024] = "";
	struct sockaddr_in addrClient;
	int addrClientSize = sizeof(addrClient);
	while (true)
	{
		//接收消息
		recvNum = recvfrom(sock, recvbuf, sizeof(recvbuf), 0, (sockaddr*)&addrClient, &addrClientSize);
			//判断接收成功
			if (recvNum > 0)
			{
				//打印收到的数据
				//192.168.1.1——十进制四等分字符串类型ip地址:inet_addr
				//ulong类型ip地址:inet_ntoa
				cout << "来自ip:" << inet_ntoa(addrClient.sin_addr) << " 的消息: " << recvbuf << endl;
			}
			else if (recvNum == 0)		//连接关闭
			{
				cout << "connection closed" << endl;
				break;
			}
			else if (WSAGetLastError() != 10035) {		//非阻塞
			{
				cout << "recvfrom error:" << WSAGetLastError() << endl;
				break;
			}

	}
	//5.关闭套接字
	closesocket(sock);
	//6.卸载库
	WSACleanup();
	return 0;
}

同步和异步

  • 同步

同步模型:一个操作必须等待前一个操作完成后才能进行。比如说,当客户端向服务器发送一个请求并期望得到响应时,它会处于阻塞状态,直到服务器返回响应

read(), write(), recv(), send()


  • 异步

异步模型则打破了这种顺序执行的限制。在异步 I/O 中,客户端发送请求后不会被阻塞,而是可以继续执行其他操作。当服务器的响应准备好时,通过回调函数、事件机制或者消息队列等方式通知客户端

异步模型的优点在于能够极大地提高系统的并发性和响应性。它能够充分利用系统资源,避免了因等待而造成的资源浪费

同步和异步的区分:是否是用户自己写代码实现的将数据从内核缓冲区拷贝到用户缓冲区,如果不是自己写代码实现的,通过回调函数、事件通知或其他机制告知用户 就是异步


在这里插入图片描述

大家的点赞、收藏、关注将是我更新的最大动力! 欢迎留言或私信建议或问题。
大家的支持和反馈对我来说意义重大,我会继续不断努力提供有价值的内容!如果本文哪里有错误的地方还请大家多多指出(●'◡'●)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1858392.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

20240623日志:大模型压缩-sliceGPT

context 1. 剪枝方案图释2. 正交矩阵Q 1. 剪枝方案图释 Fig. 1.1 剪枝方案 图中的阴影是表示丢弃掉这部分数据。通过引入正交矩阵 Q Q Q使 Q ⊤ Q Q Q ⊤ I \mathrm{Q}^\top\mathrm{Q}\mathrm{Q}\mathrm{Q}^\top\mathrm{I} Q⊤QQQ⊤I&#xff0c;来大量缩减 X X X的列数和 W …

[职场] 保险理赔工作简历自我评价范文 #媒体#经验分享#笔记

保险理赔工作简历自我评价范文 保险行业是指将通过契约形式集中起来的资金&#xff0c;用以补偿被保险人的经济利益业务的行业。下面是保险理赔工作简历自我评价范文&#xff0c;供大家参考。 保险公司工作自我评价1 我已在__保险公司工作_个年头。在同事和领导的热情帮忙下&…

query2doc:用大模型做query检索拓展

原文&#xff1a; 前沿重器[38] | 微软新文query2doc&#xff1a;用大模型做query检索拓展 比较主流的检索方案&#xff1a; 字面检索&#xff08;sparse&#xff0c;稀疏&#xff09;向量检索&#xff08;dense&#xff0c;稠密&#xff09; query对文档文段的召回&#xff…

【消息队列】六万字长文详细带你RabbitMQ从入门到精通

目录 1、基础篇1.1 为什么要用消息队列MQ1.2 什么是消息队列&#xff1f;1.3 RabbitMQ体系结构介绍1.4 RabbitMQ安装1.5 Hello World1.5.1 目标1.5.2 具体操作 1.6 RabbitMQ用法1.6.1 Work Queues1.6.2 Publish/Subscribe1.6.3 Routing1.6.4 Topics1.6.5 工作模式小结 2. 进阶篇…

维度建模中的事实表

在维度建模中&#xff0c;根据粒度划分&#xff0c;数据表主要分为维度表和事实表。而事实表又可以分为三种类型&#xff1a;事务型事实表、周期快照型事实表和累计快照型事实表。本文将详细介绍这三种事实表&#xff0c;并提供相应的代码示例。 目录 1. 事务型事实表示例 2. 周…

推荐系统三十六式学习笔记:原理篇.模型融合13|经典模型融合办法:线性模型和树模型的组合拳

目录 为什么要融合&#xff1f;“辑度组合”原理逻辑回归梯度提升决策树GBDT二者结合 总结 推荐系统在技术实现上一般划分为三个阶段&#xff1a;挖掘、召回、排序 。 为什么要融合&#xff1f; 挖掘的工作是对用户和物品做非常深入的结构化分析&#xff0c;各个角度各个层面…

硬盘监控和分析工具:Smartctl

文章目录 1. 概述2. 安装3. 使用4. smartctl属性信息介绍 1. 概述 Smartctl&#xff08;S.M.A.R.T 自监控&#xff0c;分析和报告技术&#xff09;是类Unix系统下实施SMART任务命令行套件或工具&#xff0c;它用于打印SMART自检和错误日志&#xff0c;启用并禁用SMRAT自动检测…

PR模板 | RGB特效视频标题模板Titles | MOGRT

RGB特效视频标题模板mogrt免费下载 4K分辨率&#xff08;38402160&#xff09; 支持任何语言 友好的界面 输入和输出动画 快速渲染 视频教程 免费下载&#xff1a;https://prmuban.com/39055.html 更多pr模板视频素材下载地址&#xff1a;https://prmuban.com

初见:AntDB智能运维“三剑客“之ADC

引言 6月15日&#xff0c;PostgreSQL数据库技术峰会广州站圆满落幕。峰会上&#xff0c;亚信安慧数据库智能运维产品负责人李志龙介绍了AntDB的6大数据库引擎和3大工具产品能力。 这里的3大工具分别指&#xff1a; AntDB数据库迁移工具包 MTK 数据库智能运维平台 ACC AntDB数据…

Modbus协议在工业自动化中的应用

Modbus协议介绍 Modbus是一种常用的工业现场总线通信协议,被广泛应用于工业自动化领域。它是一种简单、易实现的主从式通信协议,具有高度的可靠性和通用性。本文将从Modbus协议的基本概念、通信模式、数据格式、常见应用场景等方面进行全面介绍,并通过图文并茂的方式帮助读者更…

ardupilot开发 --- 视觉伺服 篇

风驰电掣云端飘&#xff0c;相机无法对上焦 视觉伺服分类视觉伺服中的坐标系成像模型推导IBVS推导参考文献 视觉伺服分类 控制量是在图像空间中推导得到还是在欧式空间中推导得到&#xff0c;视觉伺服又可以分类为基于位置(PBVS)和基于图像的(IBVS)视觉伺服。 视觉伺服中的坐…

关于docker存储overlay2相关问题

报错如下&#xff1a; 报错原因&#xff1a;使用rm -rf 清理overlay2导致的&#xff0c;非正常清理。 正常清理命令如下&#xff1a; # 清理Docker的所有构建缓存 docker builder prune# 删除旧于24小时的所有构建缓存 docker builder prune --filter "until24h"#删…

node.js环境安装以及Vue-CLI脚手架搭建项目教程

目录 ▐ vue-cli 搭建项目的优点 ▐ 安装node.js环境 ▐ 搭建vue脚手架项目 ▐ 项目结构解读 ▐ 常用命令 ▐ 创建组件 ▐ 组件路由 ▐ vue-cli 搭建项目的优点 传统的前端项目架构由多个html文件&#xff0c;且每个html文件都是相互独立的&#xff0c;导入外部组件时需…

wireshark常用过滤命令

wireshark常用过滤命令 wireshark抓包介绍单机单点&#xff1a;单机多点&#xff1a;双机并行&#xff1a; wireshark界面认识默认布局调整布局(常用)显示FCS错误 wireshark常见列Time回包数据报对应网络模型 wireshark基本操作结束抓包再次开始抓包 **wireshark常用过滤命令**…

【实物资料包】基于STM32智能台灯设计

【实物资料包】基于STM32智能台灯设计 需要资料的请在文章结尾获取哦~~~~&#xff08;如有问题私信我即可&#xff09; 1.介绍 1 添加wifi模块模块&#xff0c;可通过wifi模块APP或者手动按钮切换自动/手动模式 2 自动模式下&#xff0c;台灯可以感应是否有人落座&#xff0…

【BSCP系列第2期】XSS攻击的深度剖析和利用(文末送书)

文章目录 前言一、官方地址二、开始&#xff08;15个&#xff09;1&#xff1a;Lab: DOM XSS in document.write sink using source location.search inside a select element2&#xff1a;Lab: DOM XSS in AngularJS expression with angle brackets and double quotes HTML-e…

猫头虎分享已解决Bug:Array Index Out of Bounds Exception

&#x1f42f; 猫头虎分享已解决Bug&#xff1a;Array Index Out of Bounds Exception &#x1f42f; 摘要 大家好&#xff0c;我是猫头虎&#xff0c;今天我们要聊聊后端开发中经常遇到的一个问题&#xff1a;Array Index Out of Bounds Exception&#xff0c;即 java.lang.…

哪里找好用的商城系统源码?

很多企业在挑选商城系统时&#xff0c;由于不懂源码&#xff0c;很难选择到高质量源码的商城系统&#xff0c;那么哪里找好用的商城系统源码?如何选择?接下来就跟着启山智软小编一起来看看吧&#xff0c;以下为选择源码时的四看&#xff1a; 1.一看源码公司行业动态 可以查…

【linux】详解——库

目录 概述 库 库函数 静态库 动态库 制作动静态库 使用动静态库 如何让系统默认找到第三方库 lib和lib64的区别 /和/usr/和/usr/local下lib和lib64的区别 环境变量 配置相关文件 个人主页&#xff1a;东洛的克莱斯韦克-CSDN博客 简介&#xff1a;C站最萌博主 相关…

[FreeRTOS 内部实现] 信号量

文章目录 基础知识创建信号量获取信号量释放信号量信号量 内部实现框图 基础知识 [FreeRTOS 基础知识] 信号量 概念 创建信号量 #define queueQUEUE_TYPE_BINARY_SEMAPHORE ( ( uint8_t ) 3U ) #define semSEMAPHORE_QUEUE_ITEM_LENGTH ( ( uint8_t ) 0U ) #define xSe…