精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

基于Netty和SpringBoot的TCP長連接通訊方案

jf_ro2CN3Fa ? 來源:芋道源碼 ? 2023-09-13 09:39 ? 次閱讀

項目背景

最近公司物聯網項目需要使用socket長連接進行消息通訊,搗鼓了一版代碼上線,結果BUG不斷,本猿寢食難安,于是求助度娘,數日未眠項目終于平穩運行了,本著開源共享的精神,本猿把項目代碼提煉成了一個demo項目,盡量摒棄了其中丑陋的業務部分,希望與同學們共同學習進步。

基于 Spring Boot + MyBatis Plus + Vue & Element 實現的后臺管理系統 + 用戶小程序,支持 RBAC 動態權限、多租戶、數據權限、工作流、三方登錄、支付、短信、商城等功能

正文

一、項目架構

本項目使用了netty、redis以及springboot2.2.0

二、項目模塊

本項目目錄結構如下圖:

745589fe-51d5-11ee-a25d-92fbcf53809c.png

netty-tcp-core是公共模塊,主要是工具類。netty-tcp-server是netty服務端,服務端僅作測試使用,實際項目中我們只使用了客戶端。netty-tcp-client是客戶端,也是本文的重點。

三、業務流程

我們實際項目中使用RocketMQ作為消息隊列,本項目由于是demo項目于是改為了BlockingQueue。數據流為:

生產者->消息隊列->消費者(客戶端)->tcp通道->服務端->tcp通道->客戶端。

當消費者接收到某設備發送的消息后,將判斷緩存中是否存在該設備與服務端的連接,如果存在并且通道活躍則使用該通道發送消息,如果不存在則創建通道并在通道激活后立即發送消息,當客戶端收到來自服務端的消息時進行響應的業務處理。

四、代碼詳解

1.消息隊列

由于本demo項目移除了消息中間件,于是需要自己創建一個本地隊列模擬真實使用場景

packageorg.example.client;

importorg.example.client.model.NettyMsgModel;

importjava.util.concurrent.ArrayBlockingQueue;

/**
*本項目為演示使用本地隊列實際生產中應該使用消息中間件代替(rocketmq或rabbitmq)
*
*@authorReWind00
*@date2023/2/1511:20
*/
publicclassQueueHolder{

privatestaticfinalArrayBlockingQueuequeue=newArrayBlockingQueue<>(100);

publicstaticArrayBlockingQueueget(){
returnqueue;
}
}

使用一個類保存隊列的靜態實例以便在任何類中都可以快速引用。接下來我們需要啟動一個線程去監聽隊列中的消息,一但消息投遞到隊列中,我們就取出消息然后異步多線程處理該消息。

publicclassLoopThreadimplementsRunnable{
@Override
publicvoidrun(){
for(inti=0;i{
while(true){
//取走BlockingQueue里排在首位的對象,若BlockingQueue為空,阻斷進入等待狀態直到
try{
NettyMsgModelnettyMsgModel=QueueHolder.get().take();
messageProcessor.process(nettyMsgModel);
}catch(InterruptedExceptione){
log.error(e.getMessage(),e);
}
}
});
}
}
}

使用take方法會使該線程一直阻塞直到隊列收到消息后進入下一次循環。

2.執行類

process方法來自于MessageProcessor類,該類為單例,但是會有多線程同時執行。

publicvoidprocess(NettyMsgModelnettyMsgModel){
Stringimei=nettyMsgModel.getImei();
try{
synchronized(this){//為避免收到同一臺設備多條消息后重復創建客戶端,必須加鎖
if(redisCache.hasKey(NETTY_QUEUE_LOCK+imei)){//上一條消息處理中
log.info("imei={}消息處理中,重新入列",imei);
//放回隊列重新等待消費延遲x秒(實際項目中應該使用rocketmq或者rabbitmq實現延遲消費)
newTimer().schedule(newTimerTask(){
@Override
publicvoidrun(){
QueueHolder.get().offer(nettyMsgModel);
}
},2000);
log.info("imei={}消息處理中,重新入列完成",imei);
return;
}else{
//如果沒有在連接中的直接加鎖
redisCache.setCacheObject(NETTY_QUEUE_LOCK+imei,"1",120,TimeUnit.SECONDS);
}
}
//緩存中存在則發送消息
if(NettyClientHolder.get().containsKey(imei)){
NettyClientnettyClient=NettyClientHolder.get().get(imei);
if(null!=nettyClient.getChannelFuture()&&nettyClient.getChannelFuture().channel().isActive()){//通道活躍直接發送消息
if(!nettyClient.getChannelFuture().channel().isWritable()){
log.warn("警告,通道不可寫,imei={},channelId={}",nettyClient.getImei(),
nettyClient.getChannelFuture().channel().id());
}
nettyClient.send(nettyMsgModel.getMsg());
}else{
log.info("clientimei={},通道不活躍,主動關閉",nettyClient.getImei());
nettyClient.close();
//重新創建客戶端發送
this.createClientAndSend(nettyMsgModel);
}
}else{//緩存中不存在則創建新的客戶端
this.createClientAndSend(nettyMsgModel);
}
}catch(Exceptione){
log.error(e.getMessage(),e);
}finally{
//執行完后解鎖
redisCache.deleteObject(NETTY_QUEUE_LOCK+imei);
}

}

其中imei是我們設備的唯一標識,我們可以用imei作為緩存的key來確認是否已創建過連接。由于我們消息的并發量可能會很大,所以存在當某設備的連接正在創建的過程中,另一個線程收到該設備消息也開始創建連接的情況,所以我們使用synchronized 代碼塊以及redis分布式鎖來避免此情況的發生。當一條消息獲得鎖后,在鎖釋放前,后續消息將會被重新放回消息隊列并延遲消費。

獲取鎖的線程會根據imei判斷緩存是否存在連接,如果存在直接發送消息,如果不存在則進入創建客戶端的方法。

privatevoidcreateClientAndSend(NettyMsgModelnettyMsgModel){
log.info("創建客戶端執行中imei={}",nettyMsgModel.getImei());
//此處的DemoClientHandler可以根據自己的業務定義
NettyClientnettyClient=SpringUtils.getBean(NettyClient.class,nettyMsgModel.getImei(),nettyMsgModel.getBizData(),
this.createDefaultWorkGroup(this.workerThread),DemoClientHandler.class);
executor.execute(nettyClient);//執行客戶端初始化
try{
//利用鎖等待客戶端激活
synchronized(nettyClient){
longc1=System.currentTimeMillis();
nettyClient.wait(5000);//最多阻塞5秒5秒后客戶端仍然未激活則自動解鎖
longc2=System.currentTimeMillis();
log.info("創建客戶端wait耗時={}ms",c2-c1);
}
if(null!=nettyClient.getChannelFuture()&&nettyClient.getChannelFuture().channel().isActive()){//連接成功
//存入緩存
NettyClientHolder.get().put(nettyMsgModel.getImei(),nettyClient);
//客戶端激活后發送消息
nettyClient.send(nettyMsgModel.getMsg());
}else{//連接失敗
log.warn("客戶端創建失敗,imei={}",nettyMsgModel.getImei());
nettyClient.close();
//可以把消息重新入列處理
}
}catch(Exceptione){
log.error("客戶端初始化發送消息異常===>{}",e.getMessage(),e);
}
}

當netty客戶端實例創建后使用線程池執行初始化,由于是異步執行,我們此時立刻發送消息很可能客戶端還沒有完成連接,因此必須加鎖等待。進入synchronized 代碼塊,使用wait方法等待客戶端激活后解鎖,參數5000為自動解鎖的毫秒數,意思是如果客戶端出現異常情況遲遲未能連接成功并激活通道、解鎖,則最多5000毫秒后該鎖自動解開。

這參數在實際使用時可以視情況調整,在并發量很大的情況下,5秒的阻塞可能會導致線程池耗盡,或內存溢出。待客戶端創建成功并激活后則立即發送消息。

3.客戶端

packageorg.example.client;

importio.netty.bootstrap.Bootstrap;
importio.netty.buffer.Unpooled;
importio.netty.channel.*;
importio.netty.channel.socket.SocketChannel;
importio.netty.channel.socket.nio.NioSocketChannel;
importio.netty.handler.codec.DelimiterBasedFrameDecoder;
importio.netty.handler.codec.string.StringDecoder;
importio.netty.handler.codec.string.StringEncoder;
importio.netty.handler.timeout.IdleStateHandler;
importio.netty.util.CharsetUtil;
importlombok.Getter;
importlombok.NoArgsConstructor;
importlombok.extern.slf4j.Slf4j;
importorg.example.client.handler.BaseClientHandler;
importorg.example.core.util.SpringUtils;
importorg.springframework.beans.factory.annotation.Value;
importorg.springframework.context.annotation.Scope;
importorg.springframework.stereotype.Component;
importorg.springframework.util.StringUtils;

importjava.util.Map;
importjava.util.concurrent.TimeUnit;
importjava.util.concurrent.atomic.AtomicBoolean;
importjava.util.concurrent.atomic.AtomicInteger;

/**
*@authorReWind00
*@date2023/2/159:59
*/
@Slf4j
@Component
@Scope("prototype")
@Getter
@NoArgsConstructor
publicclassNettyClientimplementsRunnable{

@Value("${netty.server.port}")
privateintport;

@Value("${netty.server.host}")
privateStringhost;
//客戶端唯一標識
privateStringimei;
//自定義業務數據
privateMapbizData;

privateEventLoopGroupworkGroup;

privateClassclientHandlerClass;

privateChannelFuturechannelFuture;

publicNettyClient(Stringimei,MapbizData,EventLoopGroupworkGroup,ClassclientHandlerClass){
this.imei=imei;
this.bizData=bizData;
this.workGroup=workGroup;
this.clientHandlerClass=clientHandlerClass;
}

@Override
publicvoidrun(){
try{
this.init();
log.info("客戶端啟動imei={}",imei);
}catch(Exceptione){
log.error("客戶端啟動失敗:{}",e.getMessage(),e);
}
}

publicvoidclose(){
if(null!=this.channelFuture){
this.channelFuture.channel().close();
}
NettyClientHolder.get().remove(this.imei);
}

publicvoidsend(Stringmessage){
try{
if(!this.channelFuture.channel().isActive()){
log.info("通道不活躍imei={}",this.imei);
return;
}
if(!StringUtils.isEmpty(message)){
log.info("隊列消息發送===>{}",message);
this.channelFuture.channel().writeAndFlush(message);
}
}catch(Exceptione){
log.error(e.getMessage(),e);
}
}

privatevoidinit()throwsException{
//將本實例傳遞到handler
BaseClientHandlerclientHandler=SpringUtils.getBean(clientHandlerClass,this);
Bootstrapb=newBootstrap();
//2通過輔助類去構造server/client
b.group(workGroup)
.channel(NioSocketChannel.class)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS,3000)
.option(ChannelOption.SO_RCVBUF,1024*32)
.option(ChannelOption.SO_SNDBUF,1024*32)
.handler(newChannelInitializer(){
@Override
protectedvoidinitChannel(SocketChannelch)throwsException{
ch.pipeline().addLast(newDelimiterBasedFrameDecoder(1024*1024,Unpooled.copiedBuffer("
".getBytes())));
ch.pipeline().addLast(newStringEncoder(CharsetUtil.UTF_8));//String解碼。
ch.pipeline().addLast(newStringDecoder(CharsetUtil.UTF_8));//String解碼。
////心跳設置
ch.pipeline().addLast(newIdleStateHandler(0,0,600,TimeUnit.SECONDS));
ch.pipeline().addLast(clientHandler);
}
});
this.connect(b);
}

privatevoidconnect(Bootstrapb)throwsInterruptedException{
longc1=System.currentTimeMillis();
finalintmaxRetries=2;//重連2次
finalAtomicIntegercount=newAtomicInteger();
finalAtomicBooleanflag=newAtomicBoolean(false);
try{
this.channelFuture=b.connect(host,port).addListener(
newChannelFutureListener(){
publicvoidoperationComplete(ChannelFuturefuture)throwsException{
if(!future.isSuccess()){
if(count.incrementAndGet()>maxRetries){
log.warn("imei={}重連超過{}次",imei,maxRetries);
}else{
log.info("imei={}重連第{}次",imei,count);
b.connect(host,port).addListener(this);
}

}else{
log.info("imei={}連接成功,連接IP:{}連接端口:{}",imei,host,port);
flag.set(true);
}
}
}).sync();//同步連接
}catch(Exceptione){
log.error(e.getMessage(),e);
}
log.info("設備imei={},channelId={}連接耗時={}ms",imei,channelFuture.channel().id(),System.currentTimeMillis()-c1);
if(flag.get()){
channelFuture.channel().closeFuture().sync();//連接成功后將持續阻塞該線程
}
}
}

netty客戶端為多實例,每個實例綁定一個線程,持續阻塞到客戶端關閉為止,每個客戶端中可以保存自己的業務數據,以便在后續與服務端交互時處理業務使用。客戶端執行連接時,給了2次重試的機會,如果3次都沒連接成功則放棄。后續可以選擇將該消息重新入列消費。我們實際項目中,此處還應該預先給服務端發送一條登錄消息,待服務端確認后才能執行后續通訊,這需要視實際情況進行調整。

另一個需要注意的點是EventLoopGroup是從構造函數傳入的,而不是在客戶端中創建的,因為當客戶端數量非常多時,每個客戶端都創建自己的線程組會極大的消耗服務器資源,因此我們在實際使用中是按業務去創建統一的線程組給該業務下的所有客戶端共同使用的,線程組的大小需要根據業務需求靈活配置。

在init方法中,我們給客戶端加上了一個handler來處理與服務端的交互,下面來看一下具體實現。

packageorg.example.client.handler;

importio.netty.channel.ChannelHandlerContext;
importio.netty.handler.timeout.IdleState;
importio.netty.handler.timeout.IdleStateEvent;
importlombok.extern.slf4j.Slf4j;
importorg.example.client.NettyClient;
importorg.springframework.context.annotation.Scope;
importorg.springframework.stereotype.Component;

importjava.util.Map;

/**
*@authorReWind00
*@date2023/2/1510:09
*/
@Slf4j
@Component
@Scope("prototype")
publicclassDemoClientHandlerextendsBaseClientHandler{

privatefinalStringimei;

privatefinalMapbizData;

privatefinalNettyClientnettyClient;

privateintallIdleCounter=0;

privatestaticfinalintMAX_IDLE_TIMES=3;

publicDemoClientHandler(NettyClientnettyClient){
this.nettyClient=nettyClient;
this.imei=nettyClient.getImei();
this.bizData=nettyClient.getBizData();
}

@Override
publicvoidchannelActive(ChannelHandlerContextctx)throwsException{
log.info("客戶端imei={},通道激活成功",this.imei);
synchronized(this.nettyClient){//當通道激活后解鎖隊列線程,然后再發送消息
this.nettyClient.notify();
}
}

@Override
publicvoidchannelInactive(ChannelHandlerContextctx)throwsException{
log.warn("客戶端imei={},通道斷開連接",this.imei);
}

@Override
publicvoidchannelRead(ChannelHandlerContextctx,Objectmsg)throwsException{
log.info("客戶端imei={},收到消息:{}",this.imei,msg);
//處理業務...
if("shutdown".equals(msg)){
this.nettyClient.close();
}
}

@Override
publicvoiduserEventTriggered(ChannelHandlerContextctx,Objectevt)throwsException{
if(evtinstanceofIdleStateEvent){
IdleStateEvente=(IdleStateEvent)evt;
booleanflag=false;
if(e.state()==IdleState.ALL_IDLE){
this.allIdleCounter++;
log.info("客戶端imei={}觸發閑讀或寫第{}次",this.imei,this.allIdleCounter);
if(this.allIdleCounter>=MAX_IDLE_TIMES){
flag=true;
}
}
if(flag){
log.warn("讀寫超時達到{}次,主動斷開連接",MAX_IDLE_TIMES);
ctx.channel().close();
}
}
}

@Override
publicvoidexceptionCaught(ChannelHandlerContextctx,Throwablecause)throwsException{
log.error("客戶端imei={},連接異常{}",imei,cause.getMessage(),cause);
}
}

DemoClientHandler也是多實例bean,每個實例持有自己的NettyClient引用,以便在后續處理具體業務。在channelActive方法中,我們可以看到執行了客戶端實例的notify方法,此處就是在客戶端創建成功并且通道激活后解除wait鎖的地方。channelRead方法就是我們處理服務端發送過來的消息的方法,我們的具體業務應該在該方法執行,當然不建議長時間阻塞客戶端的工作線程,可以考慮異步處理。

最后我們看一下客戶端緩存類。

packageorg.example.client;

importjava.util.concurrent.ConcurrentHashMap;

/**
*@authorReWind00
*@date2023/2/1511:01
*/
publicclassNettyClientHolder{

privatestaticfinalConcurrentHashMapclientMap=newConcurrentHashMap<>();

publicstaticConcurrentHashMapget(){
returnclientMap;
}

}

由于netty的通道無法序列化,因此不能存入redis,只能緩存在本地內存中,其本質就是一個ConcurrentHashMap。

五、測試

packageorg.example.client.controller;

importorg.example.client.QueueHolder;
importorg.example.client.model.NettyMsgModel;
importorg.springframework.web.bind.annotation.GetMapping;
importorg.springframework.web.bind.annotation.RequestMapping;
importorg.springframework.web.bind.annotation.RequestParam;
importorg.springframework.web.bind.annotation.RestController;

/**
*@authorReWind00
*@date2023/2/1513:48
*/
@RestController
@RequestMapping("/demo")
publicclassDemoController{

/**
*間隔發送兩條消息
*/
@GetMapping("testOne")
publicvoidtestOne(){
QueueHolder.get().offer(NettyMsgModel.create("87654321","HelloWorld!"));
try{
Thread.sleep(5000);
}catch(InterruptedExceptione){
e.printStackTrace();
}
QueueHolder.get().offer(NettyMsgModel.create("87654321","HelloWorldToo!"));
}

/**
*任意發送消息
*
*@paramimei
*@parammsg
*/
@GetMapping("testTwo")
publicvoidtestTwo(@RequestParamStringimei,@RequestParamStringmsg){
QueueHolder.get().offer(NettyMsgModel.create(imei,msg));
}

/**
*連續發送兩條消息第二條由于redis鎖將會重新放回隊列延遲消費
*/
@GetMapping("testThree")
publicvoidtestThree(){
QueueHolder.get().offer(NettyMsgModel.create("12345678","HelloWorld!"));
QueueHolder.get().offer(NettyMsgModel.create("12345678","HelloWorldToo!"));
}
}

測試接口代碼如上,調用testOne,日志如下:

74dc722a-51d5-11ee-a25d-92fbcf53809c.png

可以看到第一條消息觸發了客戶端創建流程,創建后發送了消息,而5秒后的第二條消息直接通過已有通道發送了。

測試接口代碼如上,調用testTwo,日志如下:

75284664-51d5-11ee-a25d-92fbcf53809c.png

發送shutdown可以主動斷開已有連接。

測試接口代碼如上,調用testThree,日志如下:

755d02aa-51d5-11ee-a25d-92fbcf53809c.png

可以看到第二條消息重新入列并被延遲消費了。

審核編輯:湯梓紅

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 物聯網
    +關注

    關注

    2903

    文章

    44282

    瀏覽量

    371307
  • 通訊
    +關注

    關注

    9

    文章

    893

    瀏覽量

    34835
  • TCP
    TCP
    +關注

    關注

    8

    文章

    1351

    瀏覽量

    78989
  • spring
    +關注

    關注

    0

    文章

    338

    瀏覽量

    14311
  • SpringBoot
    +關注

    關注

    0

    文章

    173

    瀏覽量

    169

原文標題:Netty+SpringBoot 打造一個 TCP 長連接通訊方案

文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    Modbus Tcp通訊中斷問題

    Labview 2013 + Modbus Tcp + AC500(PLC) 通訊,數據量很小,也就發幾個指令。連線為筆記本通過一根2米的網線直接連在PLC上,中間沒有路由器。在公司測試,一切正常
    發表于 03-13 14:54

    springboot集成mqtt

    springboot集成mqtt,大綱一.數據入庫1.數據入庫解決方案二.開發實時訂閱發布展示頁面1.及時通訊技術2.技術整合
    發表于 07-16 07:53

    NETTY自定義協議的TCP服務器

    一、想法及需求1.1最初設想1.2需求分析二、硬件2.1原理圖解釋2.2PCB繪制2.3焊接及成品三、軟件3.1NETTY自定義協議的TCP服務器3.1.1使用原因為什么要使用自定義的協議呢,原因有
    發表于 12-21 08:30

    怎樣使用springboot整合netty來開發一套高性能的通信系統呢

    怎樣使用springboot整合netty來開發一套高性能的通信系統呢?為什么要用這兩個框架來實現通信服務呢?如何去實現呢?
    發表于 02-22 06:09

    基于Netty連接客戶端

    netty開發都是作為服務端來的大致思路如下:(1)創建一個eventLoopGroup,用于維護nio的io事件(2)創建一個niosocketchanel,然后將其注冊到
    發表于 11-27 15:52 ?7401次閱讀

    mpu6050與單片機串口連接通訊實驗資料下載

    mpu6050與單片機串口連接通訊實驗資料下載
    發表于 04-23 09:11 ?15次下載
    mpu6050與單片機串口<b class='flag-5'>連接通訊</b>實驗資料下載

    如何使用SpringBoot集成Netty開發一個基于WebSocket的聊天室說明

    本文檔的主要內容詳細介紹的是基于SpringBoot,借助Netty控制鏈接,使用WebSocket協議做一個實時的聊天室。
    發表于 05-29 17:56 ?1次下載
    如何使用<b class='flag-5'>SpringBoot</b>集成<b class='flag-5'>Netty</b>開發一個基于WebSocket的聊天室說明

    TCP, ISO- on- TCP, UDP連接

    TSEND“ & ?TRCV “ 發送和接收數據(TCP 和ISO - on- TCP)?TUSEND“ & ?TURCV“ 發送和接收數據(UDP) 自動連接管理的通訊
    的頭像 發表于 06-12 15:11 ?5070次閱讀
    <b class='flag-5'>TCP</b>, ISO- on- <b class='flag-5'>TCP</b>, UDP<b class='flag-5'>連接</b>

    Springboot整合netty框架實現終端、通訊板子(單片機)TCP/UDP通信案例

    如何springbootnetty案例的源代碼一個springboot整合netty框架的開發小案例,實現服務端與單片機終端實時通信的通訊
    發表于 12-29 18:55 ?20次下載
    <b class='flag-5'>Springboot</b>整合<b class='flag-5'>netty</b>框架實現終端、<b class='flag-5'>通訊</b>板子(單片機)<b class='flag-5'>TCP</b>/UDP通信案例

    netty推送消息接口及實現

    學過 Netty 的都知道,Netty 對 NIO 進行了很好的封裝,簡單的 API,龐大的開源社區。深受廣大程序員喜愛。基于此本文分享一下基礎的 netty 使用。實戰制作一個 Netty
    的頭像 發表于 11-02 16:14 ?1449次閱讀

    一步步解決連接Netty服務內存泄漏

    線上應用連接 Netty 服務出現內存泄漏了!真讓人頭大
    的頭像 發表于 04-27 14:06 ?1053次閱讀
    一步步解決<b class='flag-5'>長</b><b class='flag-5'>連接</b><b class='flag-5'>Netty</b>服務內存泄漏

    使用Netty+SpringBoot打造的TCP連接通訊方案

    最近公司某物聯網項目需要使用socket連接進行消息通訊,搗鼓了一版代碼上線,結果BUG不斷,本猿寢食難安,于是求助度娘,數日未眠項目終于平穩運行了,本著開源共享的精神,本猿把項目代碼提煉成了一個demo項目,盡量摒棄了其中丑
    的頭像 發表于 04-27 14:25 ?1476次閱讀
    使用<b class='flag-5'>Netty+SpringBoot</b>打造的<b class='flag-5'>TCP</b><b class='flag-5'>長</b><b class='flag-5'>連接通訊</b><b class='flag-5'>方案</b>

    TCP通信過程中的連接與短連接是什么?

    當面試官問你:TCP 通信過程中的連接與短連接是什么?
    的頭像 發表于 08-08 11:30 ?1125次閱讀
    <b class='flag-5'>TCP</b>通信過程中的<b class='flag-5'>長</b><b class='flag-5'>連接</b>與短<b class='flag-5'>連接</b>是什么?

    SpringBoot 連接ElasticSearch的使用方式

    SpringBoot,今天我們就以 SpringBoot 整合 ElasticSearch 為例,給大家詳細的介紹 ElasticSearch 的使用! SpringBoot 連接
    的頭像 發表于 10-09 10:35 ?1019次閱讀

    TCP連接和短連接

    TCP在真正開始進行數據傳輸之前,Server 和 Client 之間必須建立一個連接。當數據傳輸完成后,雙方不再需要這個連接時,就可以釋放這個連接
    的頭像 發表于 11-13 10:46 ?984次閱讀