Spring Boot 從Socket 到Netty網絡編程(上):SOCKET 基本開發(BIO)與改進(NIO)

前言

無論是軟件還是硬件的本質都是要解決IO問題(輸入、輸出),再說回網絡編程本質上都是基于TCP/UP的開發,socket是在此基礎上做的擴展與封裝,而Netty又是對socket做的封裝。本文旨在通過相關案例對socket進行探討。

一、基本知識

交互方式

  • 連接三次握手:1.客戶-服務 (請求)2.服務-客戶(同意)3.客戶-服務(連接)
  • 斷開四次握手:1.客戶-服務(請求斷開)2.服務-客戶(接受請求)3.服務-客戶(斷開) 4.客戶-服務(斷開完成)

Java類

? ? ? ? ServerSocket? 服務類

? ? ? ? accept(()? 開啟連接

? ? ? ? close() 停止服務

? ? ? ? Socket 客戶端類

? ? ? ? getInputStream()? 輸入內存流(接收)

? ? ? ? getOutputStream()? 輸出內存流(發布)

二、基于線程阻塞式socket(BIO)開發示例

實現

Server代碼


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;public class SockerServer {public static void main(String[] args) throws IOException {ServerSocket serverSocket= new ServerSocket(1200);while (true){Socket socket = serverSocket.accept();System.out.println("有新的客戶端連接了:"+socket.getInetAddress());new Thread(new ClientHandler(socket)).start();}}
}class ClientHandler implements Runnable {private Socket socket;public ClientHandler(Socket socket) {this.socket = socket;}@Overridepublic void run() {try {BufferedReader inStreamReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));PrintWriter outStreamWriter = new PrintWriter(socket.getOutputStream(), true);outStreamWriter.println("請輸入內容:");String message;while ((message = inStreamReader.readLine()) != null) {System.out.println("收到客戶端消息: " + message);if ("bye".equalsIgnoreCase(message)) {outStreamWriter.println("服務器:連接已關閉,再見!");break; // 結束連接}// 回顯客戶端消息outStreamWriter.println("服務器回顯:" + message);}} catch (IOException e) {throw new RuntimeException(e);}}
}

Client 代碼

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;public class SocketClient {public static void main(String[] args) throws Exception {Socket socket = new Socket("127.0.0.1",1200);BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));PrintWriter out = new PrintWriter(socket.getOutputStream(), true);BufferedReader console = new BufferedReader(new InputStreamReader(System.in));System.out.println("已連接到服務器!");System.out.println(in.readLine()); // 讀取服務器歡迎消息String userInput;System.out.println("請輸入消息(輸入 exit 斷開連接):");while ((userInput = console.readLine()) != null) {out.println(userInput); // 發送消息給服務器String serverResponse = in.readLine(); // 接收服務器響應System.out.println(serverResponse);if ("exit".equalsIgnoreCase(userInput)) {System.out.println("連接已關閉。");break;}}}
}

效果

缺點

雖然這個已經實現通信,由于它是基于線程控制通信的,換言之每個客戶端連接后都會創建一個線程,客戶端數量與線程增長成正比就意味著會吃更多的內存,這顯然是不合理的。(如果你足夠細心會有一些程序要隔一段時間需要重新啟動一下否則會卡死,這或許是設計之初犯類似的錯誤),這就是BIO的致命缺點;

三、基于單線程socket(NIO)

前言

基于上面的問題,我們通過改造實現非隔斷性Socket實現,而非多線程方式;這可以極大的提高交互效率與并發問題。

實現

服務端


import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;public class NioServer {public static void main(String[] args) throws IOException {// 打開服務端Socket通道ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();// 綁定端口1200serverSocketChannel.socket().bind(new java.net.InetSocketAddress(1200));// 設置為非阻塞模式serverSocketChannel.configureBlocking(false);// 創建Selector選擇器Selector selector = Selector.open();// 將ServerSocketChannel注冊到Selector,監聽連接事件serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);System.out.println("系統運行中.......");while (true){// 阻塞直到有事件發生selector.select();// 獲取所有發生的事件鍵Set<SelectionKey> selectionKeys = selector.selectedKeys();Iterator<SelectionKey> iterator = selectionKeys.iterator();while (iterator.hasNext()){SelectionKey key = iterator.next();if (key.isAcceptable()){ // 如果是客戶端連接事件// 處理客戶端連接ServerSocketChannel server = (ServerSocketChannel) key.channel(); // 獲取觸發事件的通道SocketChannel sc = server.accept(); // 接受客戶端連接sc.configureBlocking(false); // 設置為非阻塞模式sc.register(selector, SelectionKey.OP_READ); // 注冊讀取事件System.out.println("有新的客戶端連接了:"+sc.getRemoteAddress());}else if (key.isReadable()){ // 如果是客戶端讀取事件// 處理客戶端讀取請求SocketChannel sc = (SocketChannel) key.channel(); // 獲取觸發事件的通道ByteBuffer buffer = ByteBuffer.allocate(1024); // 創建緩沖區int len = sc.read(buffer); // 讀取數據if (len > 0){ // 如果有數據String msg = new String(buffer.array(), 0, len); // 解析消息System.out.println("收到客戶端"+sc.getRemoteAddress()+"的消息:"+msg);// 將收到的消息回傳給客戶端ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes()); // 包裝響應數據sc.write(outBuffer); // 發送響應}else if (len == -1){ // 如果客戶端斷開連接System.out.println("客戶端"+sc.getRemoteAddress()+"斷開了連接");sc.close(); // 關閉通道}}iterator.remove(); // 移除當前事件鍵}}}
}

客戶端

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.nio.channels.SocketChannel;public class NioClient {public static void main(String[] args) throws Exception{// 打開SocketChannel并連接到服務器SocketChannel socketChannel = SocketChannel.open();socketChannel.connect(new InetSocketAddress("localhost", 1200));// 創建控制臺輸入流,用于讀取用戶輸入BufferedReader console = new BufferedReader(new InputStreamReader(System.in));System.out.println("已連接到服務器!");// 初始化PrintWriter對象,用于向服務器發送數據PrintWriter writer = new PrintWriter(socketChannel.socket().getOutputStream(), true);String message;// 循環讀取用戶輸入并發送給服務器while ((message = console.readLine()) != null) {if ("exit".equalsIgnoreCase(message)) {System.out.println("即將退出連接...");break;}// 向服務器發送消息writer.println(message);// 讀取服務器返回的響應數據BufferedReader serverResponse = new BufferedReader(new InputStreamReader(socketChannel.socket().getInputStream()));String response;// 打印服務器返回的每一行數據while ((response = serverResponse.readLine()) != null) {System.out.println("服務器響應: " + response);}}// 關閉SocketChannel連接socketChannel.close();System.out.println("客戶端已退出");}
}

效果

缺點

綜上所述,NIO雖好,上面的代碼僅做到了實現,但是進行性能調優、異常處理等等需要更寫更多的代碼;作為程序員目的是用好輪子而非造輪子,那么Netty這個網絡通信工具以它的高性能、高并發、易配置的特點脫穎而出! 下篇我將進一步進行介紹。

下一篇《Spring Boot 從Socket 到Netty網絡編程(下):Netty基本開發與改進【心跳、粘包與拆包、閑置連接】》

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/908703.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/908703.shtml
英文地址,請注明出處:http://en.pswp.cn/news/908703.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【大模型LLM學習】function call/agent學習記錄

【大模型LLM學習】function call/agent學習記錄 0 前言1 langchain實現function call2 調用本地模型3 微調本地模型3.1 few-shot調用Claude生成Q-A對3.2 tools格式3.3 agent微調格式3.4 swift微調 p.s. 0 前言 記錄一下使用langchain做簡單的function call/agent(或者說意圖識別…

【碎碎念】寶可夢 Mesh GO : 基于MESH網絡的口袋妖怪 寶可夢GO游戲自組網系統

目錄 游戲說明《寶可夢 Mesh GO》 —— 局域寶可夢探索Pokmon GO 類游戲核心理念應用場景Mesh 特性 寶可夢玩法融合設計游戲構想要素1. 地圖探索&#xff08;基于物理空間 廣播范圍&#xff09;2. 野生寶可夢生成與廣播3. 對戰系統4. 道具與通信5. 延伸玩法 安全性設計 技術選…

Puppeteer測試框架 - Node.js

??親愛的技術愛好者們,熱烈歡迎來到 Kant2048 的博客!我是 Thomas Kant,很開心能在CSDN上與你們相遇~?? 本博客的精華專欄: 【自動化測試】

compose 組件 ---無ui組件

在 Jetpack Compose 中&#xff0c;確實存在不直接參與 UI 渲染的組件&#xff0c;它們主要用于邏輯處理、狀態管理或副作用控制。這些組件雖然沒有視覺界面&#xff0c;但在架構中扮演重要角色。以下是常見的非 UI 組件及其用途&#xff1a; 1. 無 UI 的 Compose 組件分類 (…

圖像超分辨率

圖像超分辨率 用AI當“像素偵探”&#xff0c;從模糊中重建合理高清細節&#xff0c;讓看不見的細節“無中生有”。 舉個生活例子 假設你有一張模糊的老照片&#xff0c;通過超分辨率技術&#xff0c;它能變成清晰的高清照片&#xff1a; 低分辨率圖像超分辨率結果 傳統放…

多線程語音識別工具

軟件介紹 本文介紹一款支持大廠接口的語音轉文字工具&#xff0c;具備免配置、免費使用的特點。 軟件特性 該工具是一款完全免費的桌面端應用程序&#xff0c;部署于開源社區平臺&#xff0c;其核心優勢在于整合了多家技術供應商的接口資源。 操作方式 用戶只需將音頻…

金融預測模型開發:數據預處理、機器學習預測與交易策略優化

金融預測模型開發:數據預處理、機器學習預測與交易策略優化 概述 本文將詳細介紹一個完整的金融預測模型開發流程,包含數據預處理、機器學習預測和交易策略優化三個核心模塊。我們使用Python實現一個端到端的解決方案,適用于股票價格預測和量化交易策略開發。 # 導入必要…

triton學習筆記7: GEMM相關

這是之前的學習筆記 triton puzzles part1triton puzzles part2triton puzzles part3triton tutorials part1triton tutorials: part2triton tutorails: part3 這是triton tutorials里最后一篇關于GEMM的系列了 GEMM的知識可以參考這篇&#xff0c;寫的非常詳細具體https://…

食養有方:進行性核上性麻痹患者的健康飲食指南

進行性核上性麻痹是一種罕見的神經系統變性疾病&#xff0c;患者常出現吞咽困難、肢體運動障礙等癥狀&#xff0c;合理的飲食安排不僅能保證營養供給&#xff0c;還能緩解不適&#xff0c;提高生活質量。以下是適合這類患者的健康飲食建議。 ?患者飲食應遵循 “均衡、細軟、易…

使用ORM Bee (ormbee) ,如何利用SQLAlchemy的模型生成數據庫表.

使用ORM Bee (ormbee) &#xff0c;如何利用SQLAlchemy的模型生成數據庫表. 將原來SQLAlchemy的模型&#xff0c;修改依賴為&#xff1a; from bee.helper import SQLAlchemy 然后就可以開始生成了。很簡單&#xff0c;主要是兩個接口。 db.create_all(True) #創建所有模型的表…

C# 使用正則表達式

C# 使用正則表達式 /// <summary> /// 測試正則表達式 /// </summary> private static void test022() {//檢查是否匹配&#xff1a;Regex.IsMatch(currencyValue, pattern); 或 new Regex(...).IsMatch(currencyValue)string pattern "\d{3,}";bool b…

LLMs之RLVR:《Absolute Zero: Reinforced Self-play Reasoning with Zero Data》翻譯與解讀

LLMs之RLVR&#xff1a;《Absolute Zero: Reinforced Self-play Reasoning with Zero Data》翻譯與解讀 導讀&#xff1a;Absolute Zero范式通過讓模型在沒有外部數據的情況下&#xff0c;自主提出和解決任務&#xff0c;實現了推理能力的顯著提升。Absolute Zero Reasoner (AZ…

信息最大化(Information Maximization)

信息最大化在目標域無標簽的域自適應任務中&#xff0c;它迫使模型在沒有真實標簽的情況下&#xff0c;對未標記數據產生高置信度且類別均衡的預測。此外&#xff0c;這些預測也可以作為偽標簽用于自訓練。 例如&#xff0c;在目標域沒有標簽時&#xff0c;信息最大化損失可以…

AUTOSAR實戰教程--標準協議棧實現DoIP轉DoCAN的方法

目錄 軟件架構 關鍵知識點 第一:PDUR的緩存作用 第二:CANTP的組包拆包功能 第三:流控幀的意義 配置過程 步驟0:ECUC模塊中PDU創建 步驟1:SoAD模塊維持不變 步驟2:DoIP模塊為Gateway功能添加Connection ?步驟3:DoIP模塊為Gateway新增LA/TA/SA ?步驟4:PDUR模…

設備驅動與文件系統:05 文件使用磁盤的實現

從文件使用磁盤的實現邏輯分享 我們現在講第30講&#xff0c;內容是文件使用磁盤的具體實現&#xff0c;也就是相關代碼是如何編寫的。上一節我們探討了如何從字符流位置算出盤塊號&#xff0c;這是文件操作磁盤的核心。而這節課&#xff0c;我們將深入研究實現這一核心功能的…

【PCIe總線】-- inbound、outbound配置

PCI、PCIe相關知識整理匯總 【PCIe總線】 -- PCI、PCIe相關實現 由之前的PCIe基礎知識可知&#xff0c;pcie的組成有&#xff1a;RC&#xff08;根節點&#xff09;、siwtch&#xff08;pcie橋&#xff09;、EP&#xff08;設備&#xff09;。 RC和EP&#xff0c;以及EP和EP能…

20250607在榮品的PRO-RK3566開發板的Android13系統下實現長按開機之后出現插入適配器不會自動啟動的問題的解決

20250607在榮品的PRO-RK3566開發板的Android13系統下實現長按開機之后出現插入適配器不會自動啟動的問題的解決 2025/6/7 17:20 緣起&#xff1a; 1、根據RK809的DATASHEET&#xff0c;短按開機【100ms/500ms】/長按關機&#xff0c;長按關機。6s/8s/10s 我在網上找到的DATASHE…

AIGC 基礎篇 Python基礎 02

1.bool類型 書接上回&#xff0c;我們上次最后講了三大數據類型&#xff0c;除了這三個之外&#xff0c;Python也有bool類型&#xff0c;也就是True和False。 a 2 print(a1) print(a2) 像這里&#xff0c;輸出的內容第一個是False&#xff0c;因為a的值為2&#xff0c;而第…

華為大規模——重塑生產力

華為大模型通過以下幾個方面重塑生產力&#xff1a; 提供強大算力支持 華為致力于構建領先的昇騰人工智能算力平臺&#xff0c;推出高性能昇騰AI集群&#xff0c;支持月級長期穩定訓練&#xff0c;可靠性業界領先。同時打造開放的昇騰計算平臺&#xff0c;兼容主流算子、框…

iOS上傳應用包錯誤問題 “Invalid bundle. The “UIInterfaceOrientationPortrait”“

引言 在開發 iOS 應用的整個生命周期中&#xff0c;打包上傳到 App Store 是一個至關重要的步驟。每一次提交&#xff0c;Xcode 都會在后臺執行一系列嚴格的校驗流程&#xff0c;包括對 Info.plist 配置的檢查、架構兼容性的驗證、資源完整性的審查等。如果某些關鍵項配置不當…