網絡編程—Socket套接字(TCP)

上篇文章:

網絡編程—Socket套接字(UDP)https://blog.csdn.net/sniper_fandc/article/details/146923670?fromshare=blogdetail&sharetype=blogdetail&sharerId=146923670&sharerefer=PC&sharesource=sniper_fandc&sharefrom=from_link

目錄

1 TCP流套接字

2 模擬實現TCP服務器


1 TCP流套接字

????????基于TCP的Socket主要有:ServerSocket和Socket,ServerSocket用于創建TCP服務器端的Socket,而Socket用于創建TCP客戶端的Socket。操作方式也類似文件。

構造方法/方法

含義

ServerSocket(int port)

構造方法,創建一個服務端流套接字Socket,并綁定到指定端口

Socket?accept()

普通方法,開始監聽指定端口(創建時綁定的端口),有客戶端連接后,返回一個服務端Socket對象,并基于該Socket建立與客戶端的連接,否則阻塞等待

void close()

關閉TCP套接字

????????因為TCP是面向流的數據讀寫方式,因此沒有像DatagramPacket數據報的API,只需創建Socket后,采用類似InputStream和OutputStream的操作方式。也可以對InputStream和OutputStream進行Scanner和PrintWriter的包裝,便于字符數據的讀寫。

構造方法/方法

含義

Socket(String host, int?port)

構造方法,創建一個客戶端流套接字Socket,并與對應IP的主機上,對應端口的進程建立連接

InetAddress getInetAddress()

從套接字中獲取連接的IP地址

InputStream getInputStream()

返回套接字中的輸入流(讀請求)

OutputStream getOutputStream()

返回套接字中的輸出流(寫響應)

????????注意:Socket可能有兩種獲得的方式,1是使用Socket構造方法,2是使用ServerSocket的方法accept()。也就是說ServerSocket的主要作用就是創建TCP服務器的全局連接監聽,客戶端作為連接發起方,因此直接創建Socket表示申請建立連接,而ServerSocket的accept()方法一旦監聽到有客戶端申請建立連接,就返回一個Socket用于建立服務器和客戶端之間的連接。

????????上述分析方式也透露了ServerSocket和Socket的生命周期,ServerSocket的生命周期伴隨整個服務器進程,而Socket的生命周期只是一次連接周期。

2 模擬實現TCP服務器

public class TcpServer {//服務器端口號private final int PORT = 8000;//創建服務器private ServerSocket serverSocket = null;public TcpServer() throws IOException {serverSocket = new ServerSocket(PORT);}//啟動服務器public void start() throws IOException {System.out.println("服務器啟動成功");ExecutorService executorService = Executors.newCachedThreadPool();while(true){//將建立的TCP連接拿到應用程序中(accept()會阻塞,直到建立連接)Socket clientSocket = serverSocket.accept();//[版本1]直接調用processConnect()就會導致第一個客戶端連接執行到該方法while中,服務器線程從而無法執行accept//進而無法一個服務器為多個客戶端服務//[版本2]解決方案:多線程(一個線程accept(),一個線程processConnect())(新的問題:頻繁創建銷毀線程)// ???????????Thread t = new Thread(() ->{// ???????????????try {// ???????????????????processConnect(clientSocket);// ???????????????} catch (IOException e) {// ???????????????????e.printStackTrace();// ???????????????}// ???????????});// ???????????t.start();//[版本3]解決方案:線程池(新的問題:線程數量太多了(IO多路復用->NIO))executorService.submit(new Runnable() {@Overridepublic void run() {try {processConnect(clientSocket);} catch (IOException e) {e.printStackTrace();}}});}}//給當前連接的客戶端提供服務(一個連接只進行一次數據交互服務(短連接)||一個連接進行多次數據交互服務(長連接))//長連接版本(去掉循環就是短連接版本)public void processConnect(Socket clientSocket) throws IOException {System.out.printf("[%s:%d] 建立連接\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());try(InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()){Scanner scanner = new Scanner(inputStream);PrintWriter printWriter = new PrintWriter(outputStream);while(true){if(!scanner.hasNext()){//如果沒有請求說明客戶端斷開連接System.out.printf("[%s:%d] 斷開連接\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());break;}//1.讀取請求并解析String request = scanner.next();//2.根據請求計算響應String response = process(request);//3.響應寫回客戶端// (注意此處不能使用next()類的函數,因為這類函數讀取結束標志是空白符:換行符、回車符等,輸入沒有這些符號服務器就會被阻塞在這類函數)printWriter.println(response);//刷新一下緩沖區printWriter.flush();System.out.printf("[%s:%d] request:%s, response:%s\n",clientSocket.getInetAddress().toString(),clientSocket.getPort(),request,response);}}finally {//連接用完需要關閉(clientSocket生命周期是一次連接周期,而serverSocket生命周期是整個服務器運行周期)clientSocket.close();}}public String process(String request) {return request;}public static void main(String[] args) throws IOException {TcpServer tcpServer = new TcpServer();tcpServer.start();}}public class TcpClient {//創建客戶端private Socket socket = null;public TcpClient() throws IOException {//new對象時就是和TCP服務器建立連接(因此需要直到服務器地址)socket = new Socket("127.0.0.1",8000);}//啟動服務器public void start() throws IOException {Scanner scanner = new Scanner(System.in);try(InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream()){Scanner scannerNet = new Scanner(inputStream);PrintWriter printWriter = new PrintWriter(outputStream);while(true){//1.讀取用戶輸入System.out.print(">");//注意此時next()讀取到換行就結束了,但是讀取的數據不含空白符,即沒有回車符String request = scanner.next();//2.發送請求// (注意此處不能使用next()類的函數,因為這類函數讀取結束標志是空白符:換行符、回車符等,輸入沒有這些符號服務器就會被阻塞在這類函數)printWriter.println(request);printWriter.flush();//3.接收響應String response = scannerNet.next();//4.將響應返回給用戶System.out.printf("request:%s, response:%s\n",request,response);}}}public static void main(String[] args) throws IOException {TcpClient tcpClient= new TcpClient();tcpClient.start();}}

運行結果如下:

????????上述代碼需要注意3點:

????????1.服務器端什么時候該關閉clientSocket(即關閉連接)?當服務器端processConnect方法內部從循環跳出時,證明此時客戶端沒有數據要發送,此時可以關閉連接,采用try-catch-finally方式,防止出現異常無法正常關閉。

????????2.如何處理next()引起的阻塞問題?上述代碼很多地方可能要用到Scanner的next()方法,但是該方法會讀取到空白符(回車換行等)才能結束,當客戶端輸入數據時可能不會攜帶空白符(在命令行中敲回車,該回車會被接收數據的next識別,發送的請求中并不攜帶回車符),此時就會導致服務器端一直未識別到結束,從而一直無響應。解決的辦法就是在發送的數據中添加空白符,比如使用println()方法會自動在數據結尾添加回車符。

????????3.如何解決服務器端只能為一個客戶端服務?當不采用多線程方案時,第一個客戶端建立連接發送請求,進入processConnect方法內部時,服務器端的主線程就會進入while中,從而其他客戶端申請建立連接時,服務器主線程無法通過accept()監聽建立連接的申請。采用多線程方案,線程池實現一個線程為一個客戶端服務(注意,當并發量很大時,線程池的線程數量很多,就會導致資源浪費調度困難等問題,此時需要采用NIO(非阻塞IO)的方式,這是一種I/O多路復用的技術,可以實現一個線程管理多個客戶端)。

下篇文章:

網絡編程—TCP/IP模型(UDP協議與自定義協議)https://blog.csdn.net/sniper_fandc/article/details/146923934?fromshare=blogdetail&sharetype=blogdetail&sharerId=146923934&sharerefer=PC&sharesource=sniper_fandc&sharefrom=from_link

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

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

相關文章

SkyWalking+Springboot實戰(最詳細)

本篇文章記錄了作者在0到1學習SkyWalking的過程,記錄了對SkyWalking的部署,學習,使用Bug解決等等過程 一、什么是SkyWalking 官方文檔: Apache SkyWalkinghttps://skywalking.apache.org/ SkyWalking 是一個開源的分布式追蹤、性…

Arduino示例代碼講解:Row-Column Scanning an 8x8 LED matrix with X-Y input LED矩陣

Arduino示例代碼講解:Row-Column Scanning an 8x8 LED matrix with X-Y input LED矩陣 Row-Column Scanning an 8x8 LED matrix with X-Y input LED矩陣功能概述硬件部分:軟件部分:代碼逐行解釋定義常量定義變量`setup()` 函數`loop()` 函數`readSensors()` 函數`refreshScr…

多線程編程中的鎖策略

目錄 1.悲觀鎖vs樂觀鎖 關鍵總結 悲觀鎖: 樂觀鎖: 選擇建議 用 悲觀鎖 當: 用 樂觀鎖 當: 2.重量級鎖vs輕量級鎖 選擇建議 用 輕量級鎖: 用 重量級鎖: 3.掛起等待鎖vs自旋鎖 關鍵細節說明 選擇…

負載均衡是什么,Kubernetes如何自動實現負載均衡

負載均衡是什么? 負載均衡(Load Balancing) 是一種網絡技術,用于將網絡流量(如 HTTP 請求、TCP 連接等)分發到多個服務器或服務實例上,以避免單個服務器過載,提高系統的可用性、可擴…

React-01React創建第一個項目(npm install -g create-react-app)

1. React特點 JSX是javaScript語法的擴展,React開發不一定使用JSX。單向響應的數據流,React實現單向數據流,減少重復代碼,比傳統數據綁定更簡單。等等 JSX是js的語法擴展,允許在js中編寫類似HTML的代碼 const …

小程序中的網絡請求

在小程序中,使用 wx.request( ) 這個方法來發送網路請求,整個請求的方式和 jQuery 里面的 $.ajax 方法是非常相似的。 在 wx.request( ) 這個方法中,接收一個配置對象,該配置對象中能夠配置的項目如下表: 關于服務器…

jvm 的attach 和agent機制

Java 的 Attach 和 Agent 機制在實際應用中得到了廣泛的成功應用,尤其是在監控、調試、性能分析、故障排查等方面。以下是這兩種機制在實際場景中的一些成功應用案例: 1. 性能監控與分析 Java Agent 和 Attach 機制廣泛應用于性能監控和分析&#xff0…

基于SpringBoot的“留守兒童網站”的設計與實現(源碼+數據庫+文檔+PPT)

基于SpringBoot的“留守兒童網站”的設計與實現(源碼數據庫文檔PPT) 開發語言:Java 數據庫:MySQL 技術:SpringBoot 工具:IDEA/Ecilpse、Navicat、Maven 系統展示 系統整體功能圖 局部E-R圖 系統首頁界面 系統注冊…

iPhone XR:一代神機,止步于此

什么樣的 iPhone ,才配稱為一代神機? 我曾經用過iPhone 4S、iPhone 6S Plus、iPhone 8 Plus,iPhone SE2、iPhone XR、iPhone 13、iPhone 14 Plus、iPhone 15/Pro。 不管硬件再怎么卷,不管囊中是否羞澀,主力機基本沒考…

【VUE】RuoYi-Vue3項目結構的分析

【VUE】RuoYi-Vue3項目結構的分析 1. 項目地址2. RuoYi-Vue3項目結構2.1 整體結構2.2 package.json2.2.1 🧾 基本信息2.2.2 🔧 腳本命令(scripts)2.2.3 🌍 倉庫信息2.2.4 📦 項目依賴(dependenc…

架構師面試(二十五):分布式存儲 Leader 設計

問題 在非常多的分布式存儲系統中,如:Zookeeper、Etcd、Kafka等,往往會存在一個 【Leader】 角色,并由該角色負責數據的寫入,這樣設計最主要的原因是什么呢? A. 唯一負責數據寫入的 Leader 角色可以避免并…

使用YoloV5和Mediapipe實現——上課玩手機檢測(附完整源碼)

目錄 效果展示 應用場景舉例 1. 課堂或考試監控(看到這個學生黨還會愛我嗎) 2. 駕駛安全監控(防止開車玩手機) 3. 企業辦公管理(防止工作時間玩手機) 4. 監獄、戒毒所、特殊場所安保 5. 家長監管&am…

GPT-4o從語義分割到深度圖生成,大模型狂潮下的計算機視覺:技術進步≠替代危機

隨著上周,GPT-4o原生多模態圖像生成功能的推出,更多玩法也被開發出來。一夜之間,GPT-4o原生多模態能力的釋放,讓圖像生成、語義分割、深度圖構建這些曾需要專業工具鏈支持的復雜任務,變成了普通人輸入一句話就能實現的…

Pytorch 張量操作

在深度學習中,數據的表示和處理是至關重要的。PyTorch 作為一個強大的深度學習框架,其核心數據結構是張量(Tensor)。張量是一個多維數組,類似于 NumPy 的數組,但具有更強大的功能,尤其是在 GPU …

小程序中跨頁面組件共享數據的實現方法與對比

小程序中跨頁面/組件共享數據的實現方法與對比 在小程序開發中,實現不同頁面或組件之間的數據共享是常見需求。以下是幾種主要實現方式的詳細總結與對比分析: 一、常用數據共享方法 全局變量(getApp())、本地緩存(w…

vue中的 拖拽

拖拽總結 實現方式特點適用場景HTML5 原生拖拽 API? 直接使用 dataTransfer 進行數據傳輸 ? 兼容性好(大部分瀏覽器支持) ? 適合簡單的拖拽場景低代碼平臺、表單生成器、組件拖拽Vue/React 組件庫(如 Vue Draggable、SortableJS&#xff…

MySQL 函數(入門版)

目錄 一、字符串函數 1、常用的字符串函數 2、函數演示 3、具體案例 二、數值函數 1、常用的數值函數 2、函數演示 3、具體案例 三、日期函數 1、常用的日期函數 2、函數演示 3、具體案例 四、流程函數 1、常用的流程函數 2、函數演示 3、具體案例 在MySQL中&a…

基于快速開發平臺與智能手表的區域心電監測與AI預警系統(源碼+論文+部署講解等)

需要源代碼,演示視頻,ppt設計原稿資料,請文末卡片聯系 !](https://i-blog.csdnimg.cn/direct/242d53cd069940b5b7a6db2bb031d406.png#pic_center)

【神經網絡】python實現神經網絡(三)——正向學習的模擬演練

有了之前的經驗(【神經網絡】python實現神經網絡(二)——正向推理的模擬演練),我們繼續來介紹如何正向訓練神經網絡中的超參(包含權重以及偏置),本章大致的流程圖如下: 一.損失函數 神經網絡以某個指標為基準尋求最優權重參數,而這個指標即可稱之為 “損失函數” 。(…

分區格式變RAW故障深度解析與數據恢復實戰指南?

分區格式變RAW的本質? 當存儲設備(如硬盤、U盤或移動硬盤)的分區突然顯示為RAW格式時,意味著操作系統無法識別其原有的文件系統結構(如NTFS、FAT32等)。此時,用戶訪問該分區會提示“需要格式化”或直接顯示…