Java如何實作原生socket通訊機制的原理詳解
本篇文章主要介紹了JAVA中實現原生的 socket 通訊機制原理,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟著小編過來看看吧
本文介紹了JAVA中實作原生的socket 通訊機制原理,分享給大家,具體如下:
##目前環境
知識點
- socket 的連線處理
- #IO 輸入、輸出流的處理
- 請求資料格式處理
- 請求模型最佳化
場景
建立socket 連線
import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketAddress; // 初始化 socket Socket socket = new Socket(); // 初始化远程连接地址 SocketAddress remote = new InetSocketAddress(host, port); // 建立连接 socket.connect(remote);
處理socket 輸入輸出流
// 输入流 InputStream in = socket.getInputStream(); // 输出流 OutputStream out = socket.getOutputStream();
BufferedReader 與
PrintWriter 作為輸入輸出的包裝類別進行處理。
// 获取 socket 输入流 private BufferedReader getReader(Socket socket) throws IOException { InputStream in = socket.getInputStream(); return new BufferedReader(new InputStreamReader(in)); } // 获取 socket 输出流 private PrintWriter getWriter(Socket socket) throws IOException { OutputStream out = socket.getOutputStream(); return new PrintWriter(new OutputStreamWriter(out)); }
資料請求與回應
public class HttpUtil { public static String compositeRequest(String host){ return "GET / HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "User-Agent: curl/7.43.0\r\n" + "Accept: */*\r\n\r\n"; } }
##
// 发起请求 PrintWriter writer = getWriter(socket); writer.write(HttpUtil.compositeRequest(host)); writer.flush(); 接收响应数据代码如下: // 读取响应 String msg; BufferedReader reader = getReader(socket); while ((msg = reader.readLine()) != null){ System.out.println(msg); }
至此,講完了原生socket 下的創建連線、發送請求與接收回應的所有核心程式碼。
完整程式碼如下:
import java.io.*; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketAddress; import com.test.network.util.HttpUtil; public class SocketHttpClient { public void start(String host, int port) { // 初始化 socket Socket socket = new Socket(); try { // 设置 socket 连接 SocketAddress remote = new InetSocketAddress(host, port); socket.setSoTimeout(5000); socket.connect(remote); // 发起请求 PrintWriter writer = getWriter(socket); System.out.println(HttpUtil.compositeRequest(host)); writer.write(HttpUtil.compositeRequest(host)); writer.flush(); // 读取响应 String msg; BufferedReader reader = getReader(socket); while ((msg = reader.readLine()) != null){ System.out.println(msg); } } catch (IOException e) { e.printStackTrace(); } finally { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } private BufferedReader getReader(Socket socket) throws IOException { InputStream in = socket.getInputStream(); return new BufferedReader(new InputStreamReader(in)); } private PrintWriter getWriter(Socket socket) throws IOException { OutputStream out = socket.getOutputStream(); return new PrintWriter(new OutputStreamWriter(out)); } }
下面,我們透過實例化一個客戶端,來展示 socket 通訊的結果。
public class Application { public static void main(String[] args) { new SocketHttpClient().start("www.baidu.com", 80); } }
結果輸出:
這種方式,雖然實作功能沒什麼問題。但我們細看,發現在 IO 寫入與讀取過程,是發生了 IO 阻塞的情況。即:
// 会发生 IO 阻塞 writer.write(HttpUtil.compositeRequest(host)); reader.readLine();
所以如果要同時請求10個不同的站點,如下:
public class SingleThreadApplication { public static void main(String[] args) { // HttpConstant.HOSTS 为 站点集合 for (String host: HttpConstant.HOSTS) { new SocketHttpClient().start(host, HttpConstant.PORT); } } }
它一定是第一個請求回應結束後,才會發起下一個站點處理。
這在服務端更明顯,雖然這裡的程式碼是客戶端連接,但是具體的操作和服務端是差不多的。請求只能一個個串列處理,這在回應時間上絕對不能達標。
- 多執行緒處理
- 有人覺得這根本不是問題,JAVA 是多執行緒的程式語言。對於這種情況,採用多執行緒的模型再適合不過。
public class MultiThreadApplication { public static void main(String[] args) { for (final String host: HttpConstant.HOSTS) { Thread t = new Thread(new Runnable() { public void run() { new SocketHttpClient().start(host, HttpConstant.PORT); } }); t.start(); } } }
這種方式一開始看起來挺有用的,但並發量一大,應用會起很多的執行緒。都知道,在伺服器上,每個執行緒實際上都會佔據一個檔案句柄。而伺服器上的句柄數是有限的,而且大量的線程,造成的線程間切換的消耗也會相當的大。所以這種方式在並發量大的場景下,一定是承載不住的。
- 多執行緒 + 執行緒池 處理
- #既然執行緒太多不行,那我們控制一下執行緒建立的數目不就行了。只啟動固定的執行緒數來進行 socket 處理,既利用了多執行緒的處理,也控制了系統的資源消耗。
public class ThreadPoolApplication { public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(8); for (final String host: HttpConstant.HOSTS) { Thread t = new Thread(new Runnable() { public void run() { new SocketHttpClient().start(host, HttpConstant.PORT); } }); executorService.submit(t); new SocketHttpClient().start(host, HttpConstant.PORT); } } }
關於啟動的執行緒數,一般 CPU 密集型會設定在 N+1(N為CPU核數),IO 密集型設定在 2N + 1。
這種方式,看起來是最優的了。那有沒有更好的呢,如果一個線程能同時處理多個 socket 連接,並且在每個 socket 輸入輸出數據沒有準備好的情況下,不進行阻塞,那是不是更優呢。這種技術叫做「IO多路復用」。在 JAVA 的 nio 包中,提供了相應的實作。
以上是Java如何實作原生socket通訊機制的原理詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

熱門話題

Java 8引入了Stream API,提供了一種強大且表達力豐富的處理數據集合的方式。然而,使用Stream時,一個常見問題是:如何從forEach操作中中斷或返回? 傳統循環允許提前中斷或返回,但Stream的forEach方法並不直接支持這種方式。本文將解釋原因,並探討在Stream處理系統中實現提前終止的替代方法。 延伸閱讀: Java Stream API改進 理解Stream forEach forEach方法是一個終端操作,它對Stream中的每個元素執行一個操作。它的設計意圖是處

膠囊是一種三維幾何圖形,由一個圓柱體和兩端各一個半球體組成。膠囊的體積可以通過將圓柱體的體積和兩端半球體的體積相加來計算。本教程將討論如何使用不同的方法在Java中計算給定膠囊的體積。 膠囊體積公式 膠囊體積的公式如下: 膠囊體積 = 圓柱體體積 兩個半球體體積 其中, r: 半球體的半徑。 h: 圓柱體的高度(不包括半球體)。 例子 1 輸入 半徑 = 5 單位 高度 = 10 單位 輸出 體積 = 1570.8 立方單位 解釋 使用公式計算體積: 體積 = π × r2 × h (4

Spring Boot簡化了可靠,可擴展和生產就緒的Java應用的創建,從而徹底改變了Java開發。 它的“慣例慣例”方法(春季生態系統固有的慣例),最小化手動設置
