Java EE(13)——網絡編程——UDP/TCP回顯服務器

前言

本文主要介紹UDP和TCP相關的API,并且基于這兩套API實現回顯服務器

UDP和TCP

UDP和TCP屬于網絡五層模型中傳輸層的協議
特點:
UDP:無連接,不可靠,面向數據包,全雙工
TCP:有連接,可靠,面向字節流,全雙工
1.無連接和有連接
這里所說的連接不是指物理意義上的通過實物來進行綁定,而是虛擬的連接。舉個例子,你打電話給對方,對方接通之后你才能進行后續通信,這就是有連接;無連接就相當于QQ發消息,無論對方是否同意,消息都能發過去
2.可靠和不可靠
不論是哪種通信方式,實際上都無法保證數據一定能傳輸成功。所以,這里的可靠指的是,能獲取到數據的傳輸情況。即使傳輸失敗了我能知道它傳輸失敗了,重傳就是了;不可靠指的是信息傳輸之后就不管了,傳輸成功與否都和我無關,更不會重傳
3.全雙工和半雙工
全雙工通信允許通信的雙方可以同時發送和接收數據,半雙工通信同一時間內只能在同一方向上傳輸

什么是回顯服務器

回顯的意思是無論客戶端給服務器發送什么請求,服務器會把客戶端的請求原樣返回

1.UCP回顯服務器

1.1API介紹

Java中UDP協議的API有兩個,一個是DatagramSocket,一個是DatagramPacket

1.1.1DatagramSocket類

作用:用于應用程序之間發送和接收UDP數據報
構造方法:

//不指定端口號,由系統隨機分配
DatagramSocket()
//指定端口號
DatagramSocket(int port)

其他方法:

//接收一個數據報
receive(DatagramPacket p)
//發送一個數據報
send(DatagramPacket p)

1.1.2DatagramPacket類

作用:封裝UDP數據報的數據和目標地址信息。它包含要發送的數據,目的主機的端口號和IP地址
構造方法:

//字節數組,字節數組長度,服務器IP,服務器端口號
//這是傳入的IP地址和端口號是固定值,因為服務器的IP和端口號一般不變
//所以作為DatagramSocket類的send方法的參數,用于客戶端向服務器發送請求
DatagramPacket(byte buf[], int length,InetAddress address, int port)//字節數組,字節數組長度
//這個和上面有所不同,主要用于服務器向客戶端返回請求
//一會代碼再具體講解
DatagramPacket(byte buf[], int length, SocketAddress address)//字節數組,字節數組長度
//作為DatagramSocket類的receive方法的參數,用于客戶端接收服務器的響應   或者   用于服務器接收客戶端的請求
DatagramPacket(byte buf[], int length)

1.2UDP回顯服務器代碼

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;public class EchoSever {private final DatagramSocket socket;//傳入端口號public EchoSever(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);//receive從網卡讀取到UDP數據報,載荷部分放到byte數組里//UDP報頭存放在requestPacket其他屬性里//還能通過requestPacket知道源IP/端口//receive具有阻塞功能socket.receive(requestPacket);//將接收到的字節數組轉換為字符串//requestPacket.getLength()得到的長度是有效長度,不一定是4096String request = new String(requestPacket.getData(),0,requestPacket.getLength());//2.根據請求計算相應(回顯服務器啥都不用做)String response = process(request);//3.返回響應到客戶端//response.getBytes()得到String內部的字節數組//當String里面全都是英文字符的時候,response.length()是可以的//因為一個英文字母對于一個字節,但是一個漢字對應多個字節//requestPacket.getSocketAddress()找到對應客戶端的IP和端口號DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,requestPacket.getSocketAddress());socket.send(responsePacket);//打印日志//IP,端口號,請求,響應System.out.printf("[%s:%d] request:%s,response:%s\n",requestPacket.getAddress().toString(),responsePacket.getPort(),request,response);}}//根據請求計算相應public String process(String request){return request;}//public static void main(String[] args) throws IOException {EchoSever sever = new EchoSever(9090);sever.start();}
}

注意1:DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),
response.getBytes().length,requestPacket.getSocketAddress());
因為requestPacket數據報保存了客戶端的IP地址和端口號,所以通過getSocketAddress()能獲取到客戶端的IP地址和端口號
//
其次,客戶端的IP地址和端口號是系統隨機分配的,而且服務器會同時處理多個客戶端請求,這些客戶端的IP和端口號都不一樣,通過getSocketAddress()才能正確的找到對應客戶端,而不是輸入固定的IP和端口號
//
這就是為什么該方法用于服務器返回客戶端的響應,而DatagramPacket(byte buf[], int length,InetAddress address, int port)用于客戶端向服務器發送請求
//
對比兩個方法不難發現,前者獲取到的IP和端口號是不固定的,而后者是指定IP和端口號
注意2:為什么服務器要指定端口號?而客戶端的端口號是隨機分配?
講服務器比作餐廳,客戶端比作顧客。顧客來餐廳吃飯,坐的桌子就相當于端口號,顧客今天坐001號桌,改天坐002號桌,人家樂意坐哪就坐那;但是餐廳的位置肯定是固定的,不可能今天餐廳在河邊,明天餐廳就跑到半山腰去了嗎,餐廳的位置不能改變,否則顧客找不到餐廳的位置

1.2UDP客戶端代碼

import java.io.IOException;
import java.net.*;
import java.util.Scanner;public class EchoClient {private final DatagramSocket socket;//這里的IP是Stringprivate final String severIP;private final int severPort;//傳入服務器IP和端口public EchoClient(String severIP,int severPort) throws SocketException {this.socket = new DatagramSocket();this.severIP = severIP;this.severPort = severPort;}//啟動客戶端public void start() throws IOException {System.out.println("客戶端啟動");Scanner in = new Scanner(System.in);while (true){//提示用戶要輸入請求了System.out.print("-> ");//1.從控制臺讀取要發送的請求數據//在用戶輸入之前有阻塞效果if (!in.hasNext()){break;}String request = in.next();//2.請求并發送//字節數組,字節數組長度,服務器IP,服務器端口號DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,InetAddress.getByName(severIP),severPort);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 {EchoClient client = new EchoClient("127.0.0.1",9090);client.start();}
}

2.TCP回顯服務器

2.1API介紹

Java中TCP協議的API有兩個,一個是SeverSocket,一個是Socket

2.1.1SeverSocket類

作用:用于服務器監聽來自客戶端的TCP連接請求
構造方法:

//不指定端口號,由系統隨機分配
ServerSocket()
//指定端口號
ServerSocket(int port)

其他方法:

//監聽并接受客戶端傳入的連接請求。此方法有阻塞效果,直到有客戶端連接
Socket accept():

2.1.2Socket類

作用:主要用于客戶端和服務器之間建立TCP連接
構造方法:

//通過傳入IP和端口號連接到指定的主機(服務器)
Socket(String host, int port)

其他方法:

//返回此套接字(實例)的輸入流,用于接收數據
getInputStream()
//返回此套接字(實例)的輸出流,用于發送數據
getOutputStream()

2.1TCP回顯服務器代碼

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;public class TCPEchoSever {private final ServerSocket socket;//public TCPEchoSever(int port) throws IOException {socket = new ServerSocket(port);}//啟動服務器private void start() throws IOException {System.out.println("服務器啟動");while (true){//將服務器和客戶端連接//accept()有阻塞效果,等待客戶端建立聯系Socket clientSocket = socket.accept();//每與一個客戶端建立連接,都創建一個線程來執行客戶端的請求Thread thread = new Thread(()->{//服務器和客戶端交互try {processConnection(clientSocket);} catch (IOException e) {throw new RuntimeException(e);}});thread.start();}}//public void processConnection(Socket clientSocket) throws IOException {System.out.printf("[%s:%d] 服務器上線\n",clientSocket.getInetAddress(),clientSocket.getPort());//inputStream從網卡讀數據try(InputStream inputStream = clientSocket.getInputStream();//OutputStream往網卡寫數據OutputStream outputStream = clientSocket.getOutputStream()) {//從網卡讀數據//byte[] array = new byte[1024];int ret = inputStream.read(array);PrintWriter printWriter = new PrintWriter(outputStream);Scanner scanner = new Scanner(inputStream);while (true){//讀取完畢,當客戶端下線的時候產生//在用戶輸入之前,hasNext()有阻塞效果//當客戶端斷開連接時,scanner.hasNext()返回false并中斷循環if (!scanner.hasNext()){System.out.printf("[%s:%d] 客戶端下線\n",clientSocket.getInetAddress(),clientSocket.getPort());break;}//1.讀取請求并解析//用戶傳過來的請求必須帶有空白符,沒有的話就會阻塞String request = scanner.next();//2.計算響應String response = process(request);//3.返回響應//outputStream.write(response.getBytes(),0,response.getBytes().length);//這個方式不方便添加空白符//通過PrintWriter來封裝outputStream//添加\nprintWriter.println(response);//刷新緩沖區printWriter.flush();//打印日志System.out.printf("[%s:%d] request:%s,response:%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 {TCPEchoSever sever = new TCPEchoSever(9090);sever.start();}
}

注意:為什么要調用clientSocket.close()?
因為每和一個客戶端連接都會創建一個clientSocket套接字,它負責和客戶端交互,即便客戶端進程終止了,客戶端的socket會被操作系統回收,但服務器中的clientSocket仍然會占用文件描述符和內存資源。當文件資源耗盡時,就無法與新的客戶端建立連接

2.2TCP客戶端代碼

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;public class TCPEchoClient {private final Socket socket;public TCPEchoClient(String severIp,int port) throws IOException {//與服務器建立聯系socket = new Socket(severIp,port);}//public void start(){System.out.println("客戶端啟動");try(InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream()) {//讀取控制臺Scanner scannerConsole = new Scanner(System.in);Scanner scannerNetWork = new Scanner(inputStream);PrintWriter printWriter = new PrintWriter(outputStream);while (true){System.out.print("->");//在用戶輸入之前,hasNext()有阻塞效果if (!scannerConsole.hasNext()){break;}//1.從控制臺輸入請求String request = scannerConsole.next();//2.發送請求//讓請求的結尾有\nprintWriter.println(request);//刷新緩沖區printWriter.flush();//3.從服務器讀取響應String response = scannerNetWork.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();}
}

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

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

相關文章

【藍橋杯】12111暖氣冰場(多源BFS 或者 二分)

思路 這題可以用BFS做,也可以用二分來做。 用二分這里只提供一個思路:對時間來二分查找,check函數就是檢查在特定的時間 t 0 t_0 t0?內每一個暖氣爐的傳播距離能否覆蓋所有格子。 用BFS做: 由幾個點開始向外擴散,知道…

使用bat批量獲取WORD中包含對應字符的段落,段落使用回車換行

get_word_paragraphs.vbs 獲取命令行參數 If WScript.Arguments.Count 0 ThenWScript.Quit 1 End If 獲取 Word 文檔路徑 docPath WScript.Arguments(0) 創建 Word 應用程序對象 Set objWord CreateObject("Word.Application") objWord.Visible False 打開 Word …

DeepSeek自學手冊:《從理論(模型訓練)到實踐(模型應用)》|73頁|附PPT下載方法

導 讀INTRODUCTION 今天分享是由ai呀蔡蔡團隊帶來的DeepSeek自學手冊:《從理論(模型訓練)到實踐(模型應用)》,這是一篇關于DeepSeek模型訓練、應用場景及替代方案的綜合指南文章,主要介紹了Deep…

WEB API 設計規范

REST API 簡介 REST 是 Representational State Transfer 的縮寫,它將資源作為核心概念,通過 HTTP 方法對資源進行操作。其本身是一套圍繞資源進行操作的架構規范。在實際應用中,更多的是體現在 API 的設計上。 企業在進行產品設計開發時&a…

QT軟件匠心開發,塑造卓越設計服務

在當今這個數字化飛速發展的時代,軟件已經成為我們生活中不可或缺的一部分。而QT,作為一款跨平臺的C圖形用戶界面應用程序開發框架,憑借其強大的功能和靈活性,在眾多軟件開發工具中脫穎而出。我們深知,在軟件開發領域&…

標貝科技入選2025年市級數據要素市場化配置改革“揭榜掛帥”名單

近日,山東省大數據局、青島市大數據局公布2025年數據要素市場化配置改革“揭榜掛帥”名單。標貝科技聯合嶗山區電子政務和大數據中心申報的“政務熱線通話錄音數據價值挖掘與權益保護”項目成功入選。這一成果不僅彰顯了標貝科技在數據領域的創新實力,更…

Flutter TextField 從入門到精通:掌握輸入框的完整指南

目錄 1. 引言 2. TextField 的基本用法 3. 主要屬性 4. 自定義 TextField 樣式 4.1 自定義邊框與提示文本 4.2 增加前綴/后綴圖標 4.3 只允許輸入數字 4.4 表單驗證系統 4.5 動態樣式修改 4.6 防抖搜索(Debounce) 5. 結論 相關推薦 1. 引言…

藍橋杯備賽 背包問題

背包問題 ![[背包問題.png]] 01背包 1.題意概要:有 n n n個物品和一個容量為 V V V的背包,每個物品有重量 w i w_i wi?和價值 v i v_i vi? 兩種屬性,要求選若干物品放入背包使背包中物品的總價值最大且背包中物品的總重量不超過背包的容…

MyBatis-Plus 自動填充:優雅實現創建/更新時間自動更新!

目錄 一、什么是 MyBatis-Plus 自動填充? 🤔二、自動填充的原理 ??三、實際例子:創建時間和更新時間字段自動填充 ?四、注意事項 ??五、總結 🎉 🌟我的其他文章也講解的比較有趣😁,如果喜歡…

arduino R4 SD卡讀寫測試

使用買來的 st7789LCD 顯示器背面就帶著一個 tf 卡槽,可以直接連接 tf 卡。使用 Sdfat 庫就可以實現對 sd 卡的讀寫操作。這里嘗試測試 sd 卡的讀寫功能。 LCD 顯示器的初始化 //定義LCD的對象 Adafruit_ST7789 tft Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);tf…

【武漢·4月11日】Parasoft聯合光庭信息研討會|邀您共探AI賦能新機遇

Parasoft聯合光庭信息Workshop邀您共探AI賦能新機遇 AI浪潮已至,你準備好了嗎? 在智能網聯汽車飛速發展的今天,AI技術正以前所未有的速度重塑行業生態。如何把握AI機遇,賦能企業創新? 4月11日,自動化軟件…

VLLM專題(三十九)—自動前綴緩存(二)

前綴緩存(Prefix Caching)是一種在LLM推理中廣泛使用的優化技術,旨在避免冗余的提示詞(prompt)計算。其核心思想很簡單——我們緩存已處理請求的鍵值緩存(kv-cache)塊,并在新請求的前綴與之前請求相同時重用這些塊。由于前綴緩存幾乎是一種“免費的午餐”,并且不會改變…

自動駕駛系統的車輛動力學建模:自行車模型與汽車模型的對比分析

在自動駕駛系統的車輛動力學建模中,自行車模型(Bicycle Model)和更復雜的汽車模型(如雙軌模型或多體動力學模型)各有其適用場景和優缺點。以下是兩者的詳細對比及選擇原因解析: 1. 模型定義與核心差異 特性…

C語言入門教程100講(6)類型修飾符

文章目錄 1. 什么是類型修飾符?2. 常見的類型修飾符3. 類型修飾符的使用3.1 short 和 long3.2 signed 和 unsigned 4. 類型修飾符的組合5. 示例代碼代碼解析:輸出結果: 6. 常見問題問題 1:short 和 long 的具體大小是多少&#xf…

Linux-Ubuntu 系統學習筆記 | 從入門到實戰

📘 Linux-Ubuntu 系統學習筆記 | 從入門到實戰 📜 目錄 環境安裝基本操作Linux操作系統介紹文件系統常用命令用戶權限管理編輯器vimGCC編譯器動態庫與靜態庫Makefile 1. 環境安裝 🌟 下載鏡像 推薦使用清華大學開源鏡像站下載Ubuntu鏡像&a…

防火墻帶寬管理

拓撲 配置 [fw]interface GigabitEthernet 0/0/0 [fw-GigabitEthernet0/0/0]service-manage all permit [fw]interface GigabitEthernet 1/0/0 [fw-GigabitEthernet1/0/0]ip address 12.0.0.1 24 [fw]interface GigabitEthernet 1/0/1 [fw-GigabitEthernet1/0/1]ip ad…

一人系統 之 為什么要做一人系統?

一人系統 之 賺錢認知篇(下) 本文 2119個字,大概閱讀時間 16分鐘。 在上一篇文章中,主要講了以下三個內容: 什么是好的工作?時薪高,并且有能力提升,而且最終可以獨立創業的工作&…

基于springboot的電影院管理系統(源碼+lw+部署文檔+講解),源碼可白嫖!

摘要 互聯網技術的成熟和普及,勢必會給人們的生活方式帶來不同程度的改變。越來越多的經營模式中都少不了線上運營,互聯網正強力推動著社會和經濟發展。國人對民族文化的自信和不同文化的包容,再加上電影行業的發展,如此繁榮吸引…

Java安全-類的動態加載

類的加載過程 先在方法區找class信息,有的話直接調用,沒有的話則使用類加載器加載到方法區(靜態成員放在靜態區,非靜態成功放在非靜態區),靜態代碼塊在類加載時自動執行代碼,非靜態的不執行;先父類后子類,…

ROS多機通信功能包——Multibotnet

引言 這是之前看到一位大佬做的集群通信中間件,突發奇想,自己也來做一個,實現更多的功能、更清楚的架構和性能更加高效的ROS多機通信的功能包 鏈接:https://blog.csdn.net/benchuspx/article/details/128576723 Multibotnet Mu…