【網絡編程】理解客戶端和服務器并使用Java提供的api實現回顯服務器

目錄

一、網絡編程

二、客戶端和服務器

三、客戶端和服務器的交互模式

四、TCP 和 UDP

UDP socket api 的使用

1、DatagramSoket

2、DatagramPacket

TCP socket api 的使用

1、ServerSocket

2、Socket?



一、網絡編程

本質上就是學習傳輸層給應用層提供的 api,通過 api 把數據交給傳輸層,進一步地層層封裝將數據通過網卡發送出去,這也是網絡程序的基本工作流程。

掌握了基礎 api 就能更好的理解實際開發中使用的框架(spring,dubbo)的工作過程,也提供了魔改/自己實現框架的能力。

二、客戶端和服務器

在網絡中,主動發起通信的一方稱為“客戶端”,被動接受的一方稱為“服務器”。同一個程序在不同的場景中,可能是客戶端也可能是服務器。

客戶端給服務器發送的數據,稱為“請求”(request)

服務器給客戶端返回的數據,稱為“響應”(response)

三、客戶端和服務器的交互模式

1、“一問一答”

一個請求對于一個響應,這是交互模式是最常見的,后續進行的“網站開發”(web開發)都是這種模式。

2、“一問多答”

主要在“下載”場景中涉及

3、“多問一答”

主要在“上傳”場景中涉及

4、“多問多答”

主要在“遠程控制/遠程桌面”場景中涉及

四、TCP 和 UDP

進行網絡編程,需要使用系統的 API,【本質上是傳輸層提供的協議】

傳輸層主要涉及到兩個協議:TCP 和 UDP。

連接性可靠性面向數據傳輸方式
TCP面向連接可靠傳輸面向字節流全雙工
UDP無連接不可靠傳輸面向數據報全雙工
  • 連接:此處說的“連接”不是物理意義的連接,是抽象虛擬的“連接”。所謂計算機中的“網絡連接”是指通信雙方各自保存對方的信息。客戶端的數據結構中記錄了誰是它的服務器;服務器的數據結構中記錄了誰是它的客戶端;本質上就是記錄對方的信息
  • 可靠傳輸/不可靠傳輸:無論如何都不能保證100%的信息傳輸。可靠傳輸主要是指發送方能夠感知數據有沒有傳輸給接收方,如果沒接收到,可以采取相應的措施補救,例如重傳機制。
  • 面向字節流:與文件中的字節流完全一致,網絡中傳輸數據的基本單位就是字節
  • 面向數據報:每次傳輸的基本單位是一個數據報(有一系列字節構成)。
  • 全雙工:一個信道,可以雙向通信,就叫全雙工。可以理解成馬路的多車道,就是全雙工。
  • 半雙工:可以理解為吸管,同一時刻只能吸或者呼。

UDP socket api 的使用

Java 把系統原生 api 封裝了,UDP socket 提供的兩個核心的類

1、DatagramSoket

操作系統中有一類文件,就叫 socket 文件,這類文件抽象地表示了“網卡”這樣的硬件設備。而進行網絡通信最核心的硬件設備就是網卡。

DatagramSocket 類就是負責對 socket 文件進行讀寫,從而借助網卡發送接收數據。

2、DatagramPacket

UDP 面向數據報,每次發送接收數據的基本單位是一個 UDP 數據報

DatagramPacket 類就表示了一個 UDP 數據報。

關于 receive 接收數據報的底層實現過程

UdpEchoServer 實例

public class UdpEchoServer {private DatagramSocket socket = null;public UdpEchoServer(int port) throws SocketException {socket = new DatagramSocket(port);}public void start() throws IOException {System.out.println("服務器啟動");while (true) {//每次循環,都是一次處理請求,進行響應的過程//1. 讀取請求并解析DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);socket.receive(requestPacket);// 將讀到的字節數組轉換成 String 方便后續操作String request = new String(requestPacket.getData(),0,requestPacket.getLength());//2. 根據請求計算響應String response = process(request);//3. 把響應返回到客戶端// 與請求數據報創建不同,請求數據報是使用空白字節數組,而此處直接把 String 里包含的字節數組作為參數創建,DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length,requestPacket.getSocketAddress());  // 因為 UDP 無連接,因此必須從【請求數據報】中獲取對應客戶端的 ip 和端口socket.send(responsePacket);//打印日志System.out.printf("[%s:%d] req: %s, resp: %s\n",requestPacket.getAddress().toString(),requestPacket.getPort(), request, response);}}private String process(String request) {return request;}public static void main(String[] args) throws IOException {UdpEchoServer server = new UdpEchoServer(9090);server.start();}
}

?上述代碼中:

1、可以看到 23 行需要從【請求數據報】中獲取對應客戶端 ip 和端口號才能完成發送響應,證明了 UDP socket 自身不保存對端的 ip 和端口號,體現了無連接

2、不可靠傳輸,代碼中沒有體現。

3、receive 和 socket 都是以DatagramPacket 為單位,體現了面向數據報

4、一個 socket 既能發送(send)有能接收(receive),體現了全雙工

UdpEchoClient 示例?

public class UdpEchoClient {private DatagramSocket socket = null;private String serverIp;private int serverPort;public UdpEchoClient(String serverIp, int serverPort) throws SocketException {// 客戶端,正常情況下不需要指定端口socket = new DatagramSocket();this.serverIp = serverIp;this.serverPort = serverPort;  // 客戶端對應的服務器端口號}public void start() throws IOException {System.out.println("客戶端啟動");Scanner sc = new Scanner(System.in);while (true) {//1. 從控制臺讀取要發送的數據System.out.print("-> "); //表示提示用戶輸入if (!sc.hasNext()) {   //hasNext 具有阻塞功能break;}String request = sc.next();//2. 構造請求并發送DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,InetAddress.getByName(serverIp), serverPort);socket.send(requestPacket);//3. 讀取服務器的響應DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096);// 阻塞等待響應數據返回socket.receive(responsePacket);//4. 把響應顯示到控制臺String response = new String(responsePacket.getData(),0,responsePacket.getLength());System.out.println(response);}}public static void main(String[] args) throws IOException {UdpEchoClient client = new UdpEchoClient("127.0.0.1", 9090);client.start();}
}

TCP socket api 的使用

由于 TCP 是面向字節流的,傳輸的基本單位是字節,因此沒有像 UDP 中 DatagramPacket 這樣的類。
Java 把系統原生 api 封裝了,TCP socket 提供的兩個核心的類

1、ServerSocket

這是Socket 類,同樣抽象地表示了“網卡”但是這個類與 UDP 中使用的 DatagramSocket 不同,這個類只能給服務器進行使用。只負責處理對客戶端的連接,主要 api 是 accept()

2、Socket?

對應到“網卡”,既能給服務器使用,又能給客戶端使用。相當于電話的兩個聽筒,通過 Socket 完成對端之間的通信。主要的 api 是 getInputStream 和 getOutputStream
需要注意:由于服務器端的 Socket 對象與客戶端時一一對應的,為了避免無限占用文件描述符表,使用完畢后需要 close 關閉。

TcpEchoServer 示例

public class TcpEchoServer {private ServerSocket serverSocket = null;public TcpEchoServer(int port) throws IOException {serverSocket = new ServerSocket(port);}public void start() throws IOException {System.out.println("服務器啟動!");while (true) {// 當客戶端創建出 socket 后(new socket),就會和對應的服務器進行 tcp 連接建立流程// 此時通過 accept 方法來“接聽電話”,然后才能進行通信Socket clientSocket = serverSocket.accept();Thread t = new Thread(() -> {processConnection(clientSocket);});t.start();}}private void processConnection(Socket clientSocket) {System.out.printf("[%s:%d] 客戶端上線!\n", clientSocket.getInetAddress(), clientSocket.getPort());// 循環讀取客戶端的請求并返回響應try (InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()){// 可以使用 inputStream 原本的 read 方法進行讀取// 但是比較繁瑣,為了【方便讀入】,這里使用 Scanner 對輸入流進行輸入Scanner sc = new Scanner(inputStream);while (true) {   // 長連接if (!sc.hasNext()) {// 讀取完畢,客戶端斷開連接System.out.printf("[%s:%d] 客戶端下線!\n", clientSocket.getInetAddress(), clientSocket.getPort());break;}//1. 讀取請求并解析,此處使用 next ,需要注意 next 的讀入規則String request = sc.next();//2. 根據請求計算響應String response = process(request);//3. 把響應返回給客戶端/*  通過這種方式也可以寫回,但是這種方式不方便添加 \noutputStream.write(response.getBytes(),0,response.getBytes().length);*/// 因此為了【方便寫入】,給 outputStream 也套一層,即使用 printWriter// 此處的 printWriter 就類似于 Scanner 將輸入流包裝了一下,而 printWriter 對輸出流包裝了一下PrintWriter printWriter = new PrintWriter(outputStream);// 通過 println 在末尾添加了 \n,與客戶端的 scNetwork.next 呼應printWriter.println(response);// 刷新緩沖區,確保數據能夠發送出去printWriter.flush();// 打印日志System.out.printf("[%s:%d] req: %s, resp: %s\n", clientSocket.getInetAddress(), clientSocket.getPort(),request,response);}} catch (IOException e) {throw new RuntimeException(e);} finally {clientSocket.close();}} private String process(String request) {return request;}public static void main(String[] args) throws IOException {TcpEchoServer server = new TcpEchoServer(9090);server.start();}
}

需要注意的是:
1、理解ServerSocket 和Socket 的不同作用,Socket作為接收對象。
2、只有當客戶端 new Socket 時,ServerSocket 才能通過 accept 完成連接。
3、Scanner 和 PrintWriter 。
4、flush 刷新緩沖區。
5、finaly{ clientSocket.close(); }?每個客戶端對應一個Socket,因此每個客戶端完成任務后,需要關閉文件,從而銷毀文件描述符表。而 try()自動關閉的是流對象,而沒有釋放文件本體。

TcpEchoClient 示例

public class TcpEchoClient {private Socket socket = null;public TcpEchoClient(String serverIp, int serverPort) throws IOException {// 這里直接將 ip 和 port 傳入,是由于 tcp 是有連接的,socket 里能夠保存 ip 和 portsocket = new Socket(serverIp,serverPort);// 因此也不需要額外創建【類成員對象】來保存 ip 和 port}public void start() {System.out.println("客戶端啟動!");try (InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream()){// 此處的 scanner 用于控制臺讀取數據Scanner scConsole = new Scanner(System.in);// 此處的 scanner 用于讀取服務器響應回來的數據Scanner scNetwork = new Scanner(inputStream);// 此處 printWriter 用于向服務器寫入請求數據PrintWriter printWriter = new PrintWriter(outputStream);while (true) {// 這里流程和 UDP 的客戶端類似//1. 從控制臺讀取輸入的字符串System.out.print("-> ");if (!scConsole.hasNext()) {break;}String request = scConsole.next();//2. 把請求發送給服務器,// 使用 printWriter 是為了使發送的請求末尾帶有 \n,與服務器的 sc.next 呼應printWriter.println(request);// 刷新緩沖區,確保數據能夠發送出去printWriter.flush();//3. 從服務器讀取響應String response = scNetwork.next();//4. 打印響應System.out.println(response);}} catch (IOException e) {throw new RuntimeException(e);}}public static void main(String[] args) throws IOException {TcpEchoClient client = new TcpEchoClient("127.0.0.1", 9090);client.start();}
}

需要注意的是:
1、TCP 是有連接的,因此 Socket 能夠直接保存 ip 和 port。
2、flush 刷新緩沖區。

【博主推薦】

【Java多線程】面試常考——鎖策略、synchronized的鎖升級優化過程以及CAS(Compare and swap)-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/zzzzzhxxx/article/details/136288256?spm=1001.2014.3001.5501【Java多線程】對線程池的理解并模擬實現線程池-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/zzzzzhxxx/article/details/136160003?spm=1001.2014.3001.5501【Java多線程】分析線程加鎖導致的死鎖問題以及解決方案-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/zzzzzhxxx/article/details/136150237?spm=1001.2014.3001.5501

如果覺得作者寫的不錯,求給博主一個大大的點贊支持一下,你們的支持是我更新的最大動力!

如果覺得作者寫的不錯,求給博主一個大大的點贊支持一下,你們的支持是我更新的最大動力!

如果覺得作者寫的不錯,求給博主一個大大的點贊支持一下,你們的支持是我更新的最大動力!

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

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

相關文章

Leetcode 128. 最長連續序列

最長連續序列 給定一個未排序的整數數組 nums ,找出數字連續的最長序列(不要求序列元素在原數組中連續)的長度。 請你設計并實現時間復雜度為 O(n) 的算法解決此問題。 示例 1: 輸入:nums [100,4,200,1,3,2] 輸出&am…

ARM簡介

ARM:ARM是Advanced RISC Machine的縮寫,意為高級精簡指令集計算機。 英國ARM公司,2016年被軟銀創始人孫正義斥資320億美元收購了。現在是軟銀旗下的芯片設計公司,總部位于英國劍橋,專注于設計芯片,賣芯片生…

揭秘:頭部房企如何借助數據分析實現穩健發展?

房地產行業是我國國民經濟中的重要支柱產業之一,在房地產市場供求關系發生重大變化的當下,房企面臨多重挑戰。Kyligence 服務的這家頭部房企把發展的重點聚焦于內生,關注內生的轉化率、接管的效率以及內生毛利率的提升,引入 Kylig…

基于springboot實現保險信息網站系統項目【項目源碼+論文說明】計算機畢業設計

基于springboot實現保險信息網站系統演示 摘要 隨著互聯網的不斷發展,現在人們獲取最新資訊的主要途徑來源于網上新聞,當下的網上信息宣傳門戶網站的發展十分的迅速。而保險產品,作為當下人們非常關注的一款能夠給人們帶來醫療、生活、養老或…

面試筆記系列七之多線程+分布式系統基礎知識點整理及常見面試題

目錄 多線程 介紹一下線程的生命周期及狀態? 線程的sleep、wait、join、yield如何使用? sleep與yield方法的區別在于, 進程調度算法 創建線程有哪些方式? 什么是守護線程? ThreadLocal的原理是什么,…

當大語言模型遇到AI繪畫-google gemma與stable diffusion webui融合方法-礦卡40hx的AI一體機

你有想過建一臺主機,又能AI聊天又能AI繪畫,還可以直接把聊天內容直接畫出來的機器嗎? 當Google最新的大語言模型Gemma碰到stable diffusion webui會怎么樣? 首先我們安裝stable diffusion webui(automatic1111開源項目&#xff…

微信小程序構建npm失敗解決方式

安裝完所需要的依賴后,在微信開發者工具菜單欄中選擇:“工具” -> “構建 npm”,但是失敗。 解決方法:修改 project.config.json 開發者工具創建的項目,miniprogramRoot 默認為 miniprogram,package.js…

數據遷移DTS | 云上MySQL 數據庫遷移至達夢數據庫

引入 云上 MySQL 數據庫 —> 向達夢國產化數據庫遷移 下載&安裝 達夢客戶端工具 DM->可參考之前國產化專欄達夢文章 創建模式 在客戶端分別依次執行以下命令腳本(這里沒有通過客戶端管理工具去創建達夢數據庫的模式,當然也可以通過圖形化界…

WordPress通過寶塔面板的入門安裝教程【保姆級】

WordPress安裝教程【保姆級】【寶塔面板】 前言一:安裝環境二:提前準備三:域名解析四:開始安裝五:安裝成功 前言 此教程適合新手,即使不懂代碼,也可輕松安裝wordpress 一:安裝環…

node如何解析前端傳遞過來的命令行字符串

node如何解析前端傳遞過來的命令行字符串 在Node.js中,如果你想處理從前端傳遞過來的命令行字符串,你可以根據你的應用程序的架構來決定如何接收這些字符串,然后進行解析。一般來說,命令行字符串可能會通過HTTP請求(如…

視頻在線轉換,四種方法任你選!(建議收藏)

在當今的數字時代,視頻已經成為人們日常生活中不可或缺的一部分。我們通過視頻分享知識、表達創造力、觀看娛樂節目,甚至參與遠程學習和工作。然而,隨著視頻格式的多樣化和設備的激增,我們經常會遇到一個常見的問題:視…

Linux(CentOS)學習

一、認識Linux 1、如何修改Linux時區 2、配置固定IP 3、重啟網絡服務 3、小技巧快捷鍵 4、環境變量設置 5、Linux文件的上傳和下載 6、壓縮和解壓 二、基礎命令 1、目錄命令 (1、)查看目錄內容(ls) 1、ls //查看當前目錄內容 2、- a //顯示隱藏內容 3…

深入理解Lucene:開源全文搜索引擎的核心技術解析

1. 介紹 Lucene是什么? Lucene是一個開源的全文搜索引擎庫,提供了強大的文本搜索和檢索功能。它由Apache軟件基金會維護和開發,采用Java語言編寫,因其高性能、可擴展性和靈活性而備受歡迎。 Lucene的作用和應用場景 Lucene主要…

Linux下創建用戶并且賦root權限

背景:好幾次都要求自己在服務器上創建用戶,并且賦權限給這個用戶的root權限,因為生產服務器上不讓用root用戶操作,之前沒怎么記錄,因為這個操作不多,但是又記不住這個操作,一到用上,…

【算法】二叉搜索樹的插入、刪除、轉換操作

1 二叉搜索樹的插入操作 給定二叉搜索樹(BST)的根節點 root 和要插入樹中的值 value ,將值插入二叉搜索樹。 返回插入后二叉搜索樹的根節點。 輸入數據 保證 ,新值和原始二叉搜索樹中的任意節點值都不同。 注意,可能…

小程序原生 API

微信原生 API 1. API 基礎 小程序開發框架提供豐富的微信原生 API,可以方便的調起微信提供的能力,如獲取用戶信息,本地存儲,支付功能等,幾乎所有小程序的 API 都掛載在 wx 對象底下,例如:wx.c…

#LLM入門|Prompt#2.2_ AI 應用開發的范式_Language_Models,the_Chat_Format_and_Tokens

在本章中,我們將和您分享大型語言模型(LLM)的工作原理、訓練方式以及分詞器(tokenizer)等細節對 LLM 輸出的影響。 我們還將介紹 LLM 的提問范式(chat format),這是一種指定系統消息…

STM32合并燒錄IAP+APP

STM32合并燒錄IAPAPP 通過查找相關資料 有以下幾種合并方法 第一種直接將二進制文件用記事本合并 而要合并的就是就將IAP最后的一行刪除,然后將APP程序追加在后面。 (修改前) 把APP的.hex 全部內容拷貝復制到 剛才刪掉結束語句的 IAP的.…

Win32匯編ListView控件學習

此控件比較復雜;和基礎win32控件不同;需要先初始化Windows公共控件庫, invoke InitCommonControls 之后才可使用; lvdemo.asm, .386.model flat, stdcalloption casemap :none ; case sensitiveinclude window…

【OCR識別】使用OCR技術還原加密字體文字

文章目錄 1. 寫在前面2. 頁面分析3. 字符知識4. 加密分析 【作者主頁】:吳秋霖 【作者介紹】:Python領域優質創作者、阿里云博客專家、華為云享專家。長期致力于Python與爬蟲領域研究與開發工作! 【作者推薦】:對JS逆向感興趣的朋…