SpringBoot教程(十五) | SpringBoot集成RabbitMq

SpringBoot教程(十五) | SpringBoot集成RabbitMq

RabbitMq是我们在开发过程中经常会使用的一种消息队列。今天我们来研究研究rabbitMq的使用。

rabbitMq的官网: rabbitmq.com/

rabbitMq的安装这里先略过,因为我尝试了几次都失败了,后面等我安装成功了会把详细的文章发出来。目前是使用公司的环境进行的调试。

1. 一些概念

RabbitMQ是一个开源的消息代理和队列服务器,用来实现各个应用服务间的数据共享(跨平台 ,跨语言)。RabbitMQ是使用erlang语言编写的,并且基于AMQP协议实现。

所有的消息队列产品模型抽象上来说,都是类似的过程。生产者创建消息,然后发布到消息队列中,由消费者进行消费。

而rabbitMQ也是类似的,有生产者,消费者角色。其内部结构如下图所示。

image.png

那么接下来我们就来介绍一下RabbitMQ中的这些概念。

1. Message:

消息,就是我们需要传递和共享的信息,消息由一些列的可选属性组成,包括路由键,优先级,是否持久化等信息

2. Publisher

消息的生产者,也是一个向交换机发布消息的客户端应用程序。

3. Exchange:

交换机,这是RabbitMQ中的一个非常重要的概念,在rabbitMq中,生产者产生的消息都不是直接发送到队列中去的,而是发送到了交换机中,交换机会通过一定的规则绑定队列,交换机会根据相应的路由规则发送给对服务器中的队列。

4. Binding:

绑定, 用于交换机和消息列队之间的关联。一个绑定就是基于路由键(routing-key)将交换机和消息队列连接起来的路由规则。所以可以将交换机理解成一个有绑定有成的路由表。

5. Queue:

消息队列,用来保存消息直到发送给消费者。它是消息的容器,也是消息的终点。一个消息可以投入一个或多个队列中。消息一直在对队列里边,等待消费者连接到这个队列将其消费。

6. Connection:

网络连接,比如一个TCP连接。

7. Channel

信道,多路复用连接中的一条独立的双向数据流通道。信道是简历在真实的TCP连接内的虚拟连接。AMQP命令都是通过信道发出去的,不管是发布消息、订阅队列还是接收消息,这些动作都是通过信道完成的。因为对于操作系统过来说建立和销毁TCP都是非常昂贵的开销,所以引入了信道的概念,以复用一条TCP连接。

8. Consumer

消息的消费者,表示一个从消息队列中取得消息的客户端应用。

9. Virtual Host

虚拟主机,标识一批交换机、消息队列和相关对象。 虚拟主机是相同的身份认证和加密环境的独立服务器域。 每个vhost本质就是一个mini版的rabbitMQ服务器,拥有自己的队列,交换机,绑定和权限机制。vhost是AMQP概念的基础,必须在连接时指定,RabbitMQ的默认vhost是/.

10. Broker

标识消息队列服务器实体。

2. Exchange类型

Exchange分发消息的时候根据类型的不同分发策略有所区别,目前常见的有四种类型: direct、fanout、topic、headers。 headers匹配AMQP消息的header而不是路由键,此外headers交换机和direct交换机完成一直但是性能差很多,几乎用不到了,所以直接看另外三种类型。

2.1 direct交换机

image.png

消息中的路由键(routing key)如果和Binding中的bing key一致,交换机就将消息发送到队列的队列中。路由键要完全匹配,单个传播。

2.2 fanout

image.png

每个发到fanout类型交换机的消息都会分到所有绑定的队列上去。fanout交换器不处理路由键,只是简单的将队列绑定到交换机上,每个发送到交换机的消息都会被转发到与该交换机绑定的所有队列上。很像子网广播,每台子网内的主机都获得了一份复制的消息。fanout类型转发消息是最快的。

2.3 topic

image.png

topic交换机通过模式匹配分配路由的路由键属性,将路由键和某个模式进行匹配,此时队列需要绑定到一个模式上。它将路由键和绑定键的字符串切分成单词,这些单词之间用.隔开。它同样会识别两个通配符: # 和* 。 #匹配0个或多个单词, * 匹配一个单词

3. springBoot集成RabbitMQ

SpringBoot集成rabbitMQ还是比较简单的,因为springBoot使用RabbitTemplate对常用操作进行了封装。

接下来我们来看一下集成过程。首先导入依赖。

xml复制代码<!-- 无需在parent的配置文件中添加 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

然后在springBoot的配置文件 application.yml中配置rabbitMQ连接信息

yml复制代码server:port: 7890spring:rabbitmq:host: 172.15.33.52port: 5672username: rootpassword: 123456

接下来我们我们分三种交换机进行演示。

3.1 direct

首先是配置类,在配置类中我们需要声明交换机,队列和绑定关系。

java复制代码@Configuration
public class DirectExchangeConfig {public static final String DIRECT_QUEUE = "directQueue";public static final String DIRECT_QUEUE2 = "directQueue2";public static final String DIRECT_EXCHANGE = "directExchange";public static final String DIRECT_ROUTING_KEY = "direct";@Beanpublic Queue directQueue() {return new Queue(DIRECT_QUEUE, true);}@Beanpublic Queue directQueue2() {return new Queue(DIRECT_QUEUE2, true);}@Beanpublic DirectExchange directExchange() {return new DirectExchange(DIRECT_EXCHANGE, true, false);}@Beanpublic Binding bindingDirectExchange(Queue directQueue, DirectExchange directExchange) {return BindingBuilder.bind(directQueue).to(directExchange).with(DIRECT_ROUTING_KEY);}@Beanpublic Binding bindingDirectExchange2(Queue directQueue2, DirectExchange directExchange) {return BindingBuilder.bind(directQueue2).to(directExchange).with(DIRECT_ROUTING_KEY);}}

这里我们创建了一个叫directExchange的交换机,绑定了directQueue和directQueue2两个队列,路由键是direct.

消息的生产者,我们通过一个Controller来进行模拟,直接引用rabbitTemplate

java复制代码@RestController
@Slf4j
@RequestMapping("/direct")
public class DirectController {private final RabbitTemplate rabbitTemplate;public DirectController(RabbitTemplate rabbitTemplate) {this.rabbitTemplate = rabbitTemplate;}/*** direct交换机为直连模式交换机*      根据消息携带的路由键将消息投递给对应队列*** @return*/@GetMapping("send")public Object sendMsg() {rabbitTemplate.convertAndSend(DirectExchangeConfig.DIRECT_EXCHANGE, DirectExchangeConfig.DIRECT_ROUTING_KEY, "发送一条测试消息:direct");return "direct消息发送成功!!";}

当我在浏览器访问对应连接的时候,就会生产一条消息发送到directExchange交换机,路由key为:direct, 消息内容为:发送一条测试消息:direct

接下来我们来看消息的消费者。

java复制代码package com.lsqingfeng.action.rabbitmq.direct;import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;/*** @className: DirectQueueListener* @description: 直连交换机的监听器* @author: sh.Liu* @date: 2021-08-23 16:03*/
@Slf4j
@Component
public class DirectQueueListener {/*** 尽管设置了两个消费者,但是只有一个能够消费成功* 多次发送则轮训消费:* DirectReceiver消费者收到消息1  : 发送一条测试消息:direct* DirectReceiver消费者收到消息2  : 发送一条测试消息:direct* DirectReceiver消费者收到消息1  : 发送一条测试消息:direct* DirectReceiver消费者收到消息2  : 发送一条测试消息:direct** 一个交换机可以绑定多个队列。如果通过路由key可以匹配到多个队列,消费的时候也只能有一个进行消费* @param testMessage*/@RabbitHandler@RabbitListener(queues = DirectExchangeConfig.DIRECT_QUEUE)public void process(String testMessage) {System.out.println("DirectReceiver消费者收到消息1  : " + testMessage);}@RabbitHandler@RabbitListener(queues = DirectExchangeConfig.DIRECT_QUEUE)public void process2(String testMessage) {System.out.println("DirectReceiver消费者收到消息2  : " + testMessage);}@RabbitHandler@RabbitListener(queues = DirectExchangeConfig.DIRECT_QUEUE2)public void process3(String testMessage) {System.out.println("DirectReceiver消费者收到消息3  : " + testMessage);}}

当我们访问浏览器生产消息会,观察控制台结果:

DirectReceiver消费者收到消息1 : 发送一条测试消息:direct DirectReceiver消费者收到消息3 : 发送一条测试消息:direct

在发送一次:

DirectReceiver消费者收到消息3 : 发送一条测试消息:direct DirectReceiver消费者收到消息2 : 发送一条测试消息:direct

由于我们又两个队列都绑定了交换机,且routeKey一样,所以会打印两条。要注意direct只有routeKey完全匹配的时候才能被消费,同时每个队列中的消息只会 被消费一次。

3.2 fanout

配置类:

java复制代码@Configuration
public class FanoutExchangeConfig {public static final String FANOUT_QUEUE = "fanoutQueue";public static final String FANOUT_QUEUE2 = "fanoutQueue2";public static final String FANOUT_QUEUE3 = "fanoutQueue3";public static final String FANOUT_EXCHANGE = "fanoutExchange";public static final String FANOUT_ROUTING_KEY = "fanout";@Beanpublic Queue fanoutQueue() {return new Queue(FANOUT_QUEUE, true);}@Beanpublic Queue fanoutQueue2() {return new Queue(FANOUT_QUEUE2, true);}@Beanpublic FanoutExchange fanoutExchange() {return new FanoutExchange(FANOUT_EXCHANGE, true, false);}@Beanpublic Binding bindingFanoutExchange(Queue fanoutQueue, FanoutExchange fanoutExchange) {return BindingBuilder.bind(fanoutQueue).to(fanoutExchange);}@Beanpublic Binding bindingFanoutExchange2(Queue fanoutQueue2, FanoutExchange fanoutExchange) {return BindingBuilder.bind(fanoutQueue2).to(fanoutExchange);}

这里也是用一个Fanout类型的交换机绑定了两个队列,要注意在这种模式下,是不需要指定routing-Key的,因为所有绑定的队列都会收到消息。

生产者代码如下:

java复制代码@RestController
@Slf4j
@RequestMapping("/fanout")
public class FanoutController {private final RabbitTemplate rabbitTemplate;public FanoutController(RabbitTemplate rabbitTemplate) {this.rabbitTemplate = rabbitTemplate;}/*** fanout交换机为扇形模式交换机*      消息会发送到所有绑定的队列上。* @return*/@GetMapping("send")public Object sendMsg() {rabbitTemplate.convertAndSend(FanoutExchangeConfig.FANOUT_EXCHANGE, null, "发送一条测试消息:fanout");return "fanout消息发送成功!!";}
}

消息的消费者:

java复制代码@Slf4j
@Component
public class FanoutQueueListener {/*** fanout交换机: 扇型交换机,这个交换机没有路由键概念,就算你绑了路由键也是无视的。 这个交换机在接收到消息后,会直接转发到绑定到它上面的所有队列* 同一个队列监听多次,只会消费一次。* 交换机绑定的多个队列都可以收到消息* @param testMessage*/@RabbitHandler@RabbitListener(queues = FanoutExchangeConfig.FANOUT_QUEUE)public void process(String testMessage) {System.out.println("FanoutReceiver消费者收到消息1  : " + testMessage);}@RabbitHandler@RabbitListener(queues = FanoutExchangeConfig.FANOUT_QUEUE)public void process2(String testMessage) {System.out.println("FanoutReceiver消费者收到消息2  : " + testMessage);}@RabbitHandler@RabbitListener(queues = FanoutExchangeConfig.FANOUT_QUEUE2)public void process3(String testMessage) {System.out.println("FanoutReceiver消费者收到消息3  : " + testMessage);}}

打印结果:

FanoutReceiver消费者收到消息1 : 发送一条测试消息:fanout FanoutReceiver消费者收到消息3 : 发送一条测试消息:fanout

因为方法1和方法2监听的是同一个队列,只有一个可以消费成功。多次执行,两个方法交替执行。

3.3 topic

主题交换机,会根据routing-Key的匹配规则,将消息发送到符合规则的队列中。

配置类:

java复制代码/*** @className: TopicExchangeConfig* @description:* *  (星号) 用来表示一个单词 (必须出现的)* #  (井号) 用来表示任意数量(零个或多个)单词* @author: sh.Liu* @date: 2021-08-23 15:49*/
@Configuration
public class TopicExchangeConfig {public static final String TOPIC_QUEUE = "topicQueue";public static final String TOPIC_QUEUE2 = "topicQueue2";public static final String TOPIC_QUEUE3 = "topicQueue3";public static final String TOPIC_EXCHANGE = "topicExchange";public static final String TOPIC_ROUTING_KEY = "topic*";@Beanpublic Queue topicQueue() {return new Queue(TOPIC_QUEUE, true);}@Beanpublic Queue topicQueue2() {return new Queue(TOPIC_QUEUE2, true);}@Beanpublic Queue topicQueue3() {return new Queue(TOPIC_QUEUE3, true);}@Beanpublic TopicExchange topicExchange() {return new TopicExchange(TOPIC_EXCHANGE, true, false);}@Beanpublic Binding bindingTopicExchange(Queue topicQueue, TopicExchange topicExchange) {return BindingBuilder.bind(topicQueue).to(topicExchange).with("topic.#");}@Beanpublic Binding bindingTopicExchange2(Queue topicQueue2, TopicExchange topicExchange) {return BindingBuilder.bind(topicQueue2).to(topicExchange).with("test.#");}@Beanpublic Binding bindingTopicExchange3(Queue topicQueue3, TopicExchange topicExchange) {return BindingBuilder.bind(topicQueue3).to(topicExchange).with("#");}
}

这里要注意我们的绑定管关系。分别是topic.#, test.*, #

#: 代表所有,* 代表有且只有一个。

消息的发送者,我们将routingKey作为参数方便我们看效果:

java复制代码@RestController
@Slf4j
@RequestMapping("/topic")
public class TopicController {private final RabbitTemplate rabbitTemplate;public TopicController(RabbitTemplate rabbitTemplate) {this.rabbitTemplate = rabbitTemplate;}@GetMapping("send")public Object sendMsg(String routingKey) {rabbitTemplate.convertAndSend(TopicExchangeConfig.TOPIC_EXCHANGE, routingKey, "发送一条测试消息:topic");return "topic消息发送成功!!";}

消息的消费者:

java复制代码/*** @className: TopicQueueListener* @description: 主题交换机的监听器* @author: sh.Liu* @date: 2021-08-23 16:03*/
@Slf4j
@Component
public class TopicQueueListener {/*** topic: 主题交换机* @param testMessage*/@RabbitHandler@RabbitListener(queues = TopicExchangeConfig.TOPIC_QUEUE)public void process(String testMessage) {System.out.println("TopicReceiver消费者收到消息1  : " + testMessage);}@RabbitHandler@RabbitListener(queues = TopicExchangeConfig.TOPIC_QUEUE)public void process2(String testMessage) {System.out.println("TopicReceiver消费者收到消息2  : " + testMessage);}@RabbitHandler@RabbitListener(queues = TopicExchangeConfig.TOPIC_QUEUE2)public void process3(String testMessage) {System.out.println("TopicReceiver消费者收到消息3  : " + testMessage);}@RabbitHandler@RabbitListener(queues = TopicExchangeConfig.TOPIC_QUEUE3)public void process4(String testMessage) {System.out.println("TopicReceiver消费者收到消息4  : " + testMessage);}}

请求:http://localhost:7890/topic/send?routingKey=test.a

结果:

TopicReceiver消费者收到消息3 : 发送一条测试消息:topic TopicReceiver消费者收到消息4 : 发送一条测试消息:topic

代表: test.* 和 # 与路由key匹配成功

请求:http://localhost:7890/topic/send?routingKey=topic.123

TopicReceiver消费者收到消息1 : 发送一条测试消息:topic TopicReceiver消费者收到消息4 : 发送一条测试消息:topic

代表: topic.# 和 # 匹配成功

请求: http://localhost:7890/topic/send?routingKey=test

TopicReceiver消费者收到消息4 : 发送一条测试消息:topic

test.* 后面必须要有一个单词

请求: http://localhost:7890/topic/send?routingKey=test.aaa

TopicReceiver消费者收到消息4 : 发送一条测试消息:topic TopicReceiver消费者收到消息3 : 发送一条测试消息:topic

test.*和 #匹配成功

请求:http://localhost:7890/topic/send?routingKey=test.aaa.b

TopicReceiver消费者收到消息4 : 发送一条测试消息:topic

只对# 匹配成功, 因为test.*只能匹配一个单词,aaa.b代表两个

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

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

相关文章

边缘型人格障碍测试

边缘性人格障碍属于常见多发的人格障碍类型&#xff0c;在2015年美国的相关调查显示&#xff0c;边缘型人格障碍的患病率在初级医疗环境中为6%&#xff0c;在精神科住院病人中则达到20%。 边缘性人格障碍治疗难度高&#xff0c;对于个人造成的危害大。应该及早了解边缘性人格障…

软件测试|Git:fatal: refusing to merge unrelated histories错误分析与解决

问题介绍 在使用Git时&#xff0c;有时我们可能会遇到以下错误消息&#xff1a; fatal: refusing to merge unrelated histories这个错误通常发生在尝试合并两个不相关的Git仓库历史时。在本文中&#xff0c;我们将详细解释为什么会出现这个错误以及如何解决它。 问题分析 …

DataFunSummit:2023年云原生大数据峰会:核心内容与学习收获(附大会核心PPT下载)

随着数字化转型的深入推进&#xff0c;大数据技术已经成为企业获取竞争优势的关键因素之一。本次峰会汇聚了业界顶尖的大数据专家、企业领袖和技术精英&#xff0c;共同探讨云原生大数据领域的最新技术和趋势。本文将深入分析峰会的核心内容&#xff0c;并探讨参会者从中能学到…

Python字符串验证与正则表达式【第23篇—python基础】

文章目录 引言方法1&#xff1a;使用 isalpha() 方法方法2&#xff1a;使用正则表达式方法3&#xff1a;遍历字符检查应用场景示例与比较优化与扩展方法4&#xff1a;考虑空格和其他字符应用场景扩展 示例与比较优化与扩展方法4&#xff1a;考虑空格和其他字符方法5&#xff1a…

【期末不挂科-C++考前速过系列P5】大二C++实验作业-多态性(3道代码题)【解析,注释】

前言 大家好吖&#xff0c;欢迎来到 YY 滴C考前速过系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的《…

Docker容器(二)安装与初体验wordpress

一、安装 1.1关闭SeLinux SeLinux&#xff08;Security-Enhanced Linux&#xff09;是一种基于Linux内核的安全模块&#xff0c;旨在提供更严格的访问控制和安全策略。它通过强制实施安全策略来限制系统资源的访问&#xff0c;从而保护系统免受恶意软件和未经授权的访问。 在…

2023国赛 陕西省省级二等奖得主 数学建模学习资源推荐

美国最为权威的数学建模参考书Mathematical Modeling 在前言部分对数学建模有一个比较通俗易懂的解释&#xff1a; Mathematical modeling is the link between mathematics and the rest of the world. You ask a question. You think a bit, and then you refine the questi…

pandas字符串操作(上)

目录 数据预览&#xff1a; 一、split分割列 1.需求&#xff1a; 2.完整代码展示 3.讲解 &#xff08;1&#xff09;分割 &#xff08;2&#xff09;写入 4.效果展示 二、partition分割列 1.需求&#xff1a; 2.完整代码展示 3.讲解 &#xff08;1&#xff09;分割…

推荐系统模型(一) DFN 详解 Deep Feedback Network for Recommendation

背景 在大多数的推荐系统中&#xff0c;往往注重于隐式正反馈(例如&#xff1a;点击)&#xff0c;而忽略掉用户的其他行为(例如大多数CTR模型只考虑用户的喜欢&#xff0c;而忽略了不喜欢)。腾讯在Deep Feedback Network for Recommendation 一文中&#xff0c;提出了一个新颖…

【网络安全】【密码学】【北京航空航天大学】实验一、数论基础(上)【C语言和Java实现】

实验一、数论基础&#xff08;上&#xff09; 一、实验目的 1、通过本次实验&#xff0c;熟悉相关的编程环境&#xff0c;为后续的实验做好铺垫&#xff1b; 2、回顾数论学科中的重要基本算法&#xff0c;并加深对其的理解&#xff0c;为本学期密码学理论及实验课程打下良好…

蓝桥杯备赛 | 洛谷做题打卡day5

蓝桥杯备赛 | 洛谷做题打卡day5 图论起航&#xff0c;一起来看看深&#xff08;广&#xff09;度优先吧 ~ 文章目录 蓝桥杯备赛 | 洛谷做题打卡day5图论起航&#xff0c;一起来看看深&#xff08;广&#xff09;度优先吧 ~【深基18.例3】查找文献题目描述 输入格式输出格式样例…

易基因:表观遗传学和表观转录组修饰在植物金属和准金属暴露中的作用 | 抗逆综述

大家好&#xff0c;这里是专注表观组学十余年&#xff0c;领跑多组学科研服务的易基因。 非必需金属&#xff08;non-essential metal&#xff09;和准金属&#xff08;metalloid&#xff0c;也称类金属&#xff09;对土壤的污染是全球许多地区面临的严重问题。这些非必需金属…

python 爬虫 生成markdown文档

本文介绍的案例为使用python爬取网页内容并生成markdown文档&#xff0c;首先需要确定你所需要爬取的框架结构&#xff0c;根据网页写出对应的爬取代码 1.分析总网页的结构 我选用的是redis.net.com/order/xxx.html (如:Redis Setnx 命令_只有在 key 不存在时设置 key 的值。…

【Azure 架构师学习笔记】- Azure Databricks (6) - 配置Unity Catalog

本文属于【Azure 架构师学习笔记】系列。 本文属于【Azure Databricks】系列。 接上文 【Azure 架构师学习笔记】- Azure Databricks (5) - Unity Catalog 简介 UC的关键特性 之所以DataBricks要用UC&#xff0c; 很大程度是对安全的管控。从上文可以了解到它的四大特性&#…

C++ OJ基础

C OJ基础 在学校学习C程序设计基础课程的OJ题目 缺少第二十题 这里写目录标题 C OJ基础习题练习(一)打印图形习题练习(二)数据的输入输出习题练习(三)函数重载习题练习(四)设计矩形类习题练习(五)定义Tree类习题练习(六)完善职工工资类Salary的设计习题练习(七)设计矩形类recta…

双目测距工程Stereo-Vision-master学习笔记

硬件&#xff1a; 首先要要把两个摄像头固定到支架上&#xff0c;并且两个摄像头的间距应该在110mm&#xff0c;两个摄像头没有落差 相机的内参数包括焦距、主点坐标、像素尺寸等&#xff0c;这些参数决定了相机成像的几何变换关系。内参数是相机固有的属性&#xff0c;不会随…

UE5 UE4 修复GPU驱动程序崩溃

原贴链接&#xff1a;https://mp.weixin.qq.com/s/e5l9XtfwEFWgwhHi1b2idg UE5 UE4在处理含有大量图形的项目时&#xff0c;你有可能会遇到GPU崩溃 可以通过修改注册表&#xff0c;修复崩溃。 GPU崩溃情况概述 UE5 UE4在处理含有大量图形的项目时&#xff0c;你有可能会遇到G…

通过生成mcs、bin文件将程序固化到FPGA

通过将程序固化到FPGA&#xff0c;可以做到断电不丢失程序&#xff0c;上电之后就自动启动程序的作用&#xff0c;整个固化步骤主要分为3步&#xff0c;一是修改约束文件&#xff0c;二是生成mcs或bin文件&#xff0c;三是将程序固化到开发板flash 1.修改约束文件 生成固化文…

弟12章 网络编程

文章目录 网络协议概述 p164TCP协议与UDP协议的区别 p165TCP服务器端代码的编写 p166TCP服务器端流程 TCP客户端代码的编写 p167TCP客户端流程主机和客户端的通信流程 tcp多次通信服务器端代码 p168TCP多次通信客户端代码 p169UDP的一次双向通信 p170udp通信模型udp接收方代码u…

Git核心知识总结

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a; 工具教程 ✨特色专栏&#xff1a; MyS…