JavaEE初階-網絡編程

文章目錄

  • 前言
  • 一、UDP與TCP
    • 1.1 有連接與無連接
    • 1.2 全雙工
    • 1.3 可靠傳輸與不可靠傳輸
    • 1.4 面向子節流與面向數據報
  • 二、UDP回顯服務器及客戶端編寫
  • 三、UDP字典服務器
  • 四、TCP回顯服務器及客戶端編寫
  • 五、數據序列化的方式
    • 5.1 基于行文本的方式傳輸
    • 5.2 基于XML的格式
    • 5.3 基于json
    • 5.4 yml
    • 5.5 protobuffer(pb)


前言

目前見到socket這個詞,就可以認為是網絡編程api的統稱。操作系統提供的socket api不是一套而是有好幾套。
(1)流式套接字:給TCP使用。
(2)數據包套接字:給UDP使用。
(3)Unix域套接字:不能跨主機通信,只是本地主機上的線程與線程之間的通信方式。

一、UDP與TCP

UDP和TCP都是傳輸層的協議,是給應用層提供服務的,但是兩個協議之間的差異還是很大的。

1.1 有連接與無連接

UDP是無連接的,TCP是有連接的。
舉例子來理解的話就是有連接相當于打電話,你必須等到對方接通你才能和他通話;無連接相當于短信,你想發就發,不用先接通。
對于UDP的無連接就是直接發送數據,TCP的有連接就是建立連接之后才能發送數據。
另外在計算機中的連接是一個抽象的概念,生活中我們談到連接往往是把兩個東西連起來,但是在計算機的領域,連接就是認為建立連接的雙方各自保存對方的信息,此時就認為是建立了一個抽象的連接。

1.2 全雙工

UDP和TCP都是全雙工的。
至于全雙工就是指一條通信鏈路能夠雙向通信,與之相對的就是半雙工指的就是一條通信鏈路只能夠單向通信。
對于全雙工在代碼中的體現就是后續使用socket既可以讀也可以寫。

1.3 可靠傳輸與不可靠傳輸

UDP是不可靠傳輸,TCP是可靠傳輸。
可靠指的不是一定百分百安全,只是說TCP會盡力去保證數據在傳輸的過程中少發生意外,盡可能多的去保留數據。而UDP就不一樣了,它是不可靠的,只要把數據發送過去就行了,不管在這個過程中會發生什么。
通過上述不難發現,可靠與不可靠也決定了UDP與TCP的一部分特點。TCP要想實現可靠性必然會付出代價,因此它數據傳輸的速度是不如UDP的,但是UDP的可靠性也因此不如TCP。兩者有著不同的適用場景,TCP適用于對數據要求高的,不能出錯的場景,UDP適用于對數據的準確性要求不高,但是對傳輸速率要求高的場景。一般來說的話現在一些應用軟件都是UDP和TCP混用。

1.4 面向子節流與面向數據報

文件操作是面向字節流的,TCP與其有著相同的特點也是面向字節流的,UDP則是面向數據報的。面向數據報就是指數據傳輸的單位是數據報,一次讀寫只能讀寫完整的數據報,不能搞半個也不搞一個半。
另外網絡傳輸數據的基本單位涉及到幾個術語:
(1)數據報Datagram UDP
(2)數據段Segment TCP
(3)數據包 Packet IP
(4)數據幀 Frame 數據鏈路層
雖然以上術語是有差別的,但是程序員對于以上術語都是混著用的不會做刻意區分,因此后續使用過程中也不必刻意區分。

二、UDP回顯服務器及客戶端編寫

Echo稱為“回顯”,正常的服務器你給它發送不同的請求就會返回不同的響應,此處回顯的意思就是請求是什么就返回什么。實際上這就是最簡單的客戶端服務器程序,用來認識socket api的用法。
UdpEchoServer:

package network;import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;public class UdpEchoServer {private DatagramSocket datagramSocket = null;public UdpEchoServer(int port) throws SocketException {datagramSocket = new DatagramSocket(port);}public void start() throws IOException {System.out.println("服務器啟動!!!");while (true) {// 1.讀取請求并且解析DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);datagramSocket.receive(requestPacket);String request = new String(requestPacket.getData(), 0, requestPacket.getLength());// 2.處理計算請求信息String response = this.process(request);// 3.把響應返回客戶端 客戶端的ip以及端口號可以通過請求的數據包中獲取DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), 0, response.getBytes().length,requestPacket.getSocketAddress());datagramSocket.send(responsePacket);// 打印日志 ip port 請求以及返回內容System.out.printf("[%s:%d] req=%s reps=%s", requestPacket.getAddress(), requestPacket.getPort(), request, response);System.out.println();}}public String process(String request) {return request;}public static void main(String[] args) throws IOException {UdpEchoServer udpEchoServer = new UdpEchoServer(4090);udpEchoServer.start();}}

UdpEchoClient:

package network;import java.io.IOException;
import java.net.*;
import java.util.Scanner;public class UdpEchoClient {private DatagramSocket datagramSocket = null;private String serverIp;private int port;public UdpEchoClient(String ip, int port) throws SocketException {datagramSocket = new DatagramSocket();this.serverIp = ip;this.port = port;}public void start() throws IOException {System.out.println("客戶端啟動!!!");Scanner sc = new Scanner(System.in);while (true) {// 輸入請求System.out.println("請輸入請求:");String request = sc.nextLine();// 打包請求并且發送請求DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), 0, request.getBytes().length, InetAddress.getByName(serverIp), port);datagramSocket.send(requestPacket);// 接收響應DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);datagramSocket.receive(responsePacket);// 打印響應的內容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("localhost", 4090);client.start();}}

三、UDP字典服務器

對于字典服務器,和回顯的區別在于你請求的是一個中文字符串,響應也就是要返回一個英語單詞,因此我們需要在服務器端去存儲對應的單詞鍵值對即可,又因為我們前面實現了回顯服務器,所以我們可以直接繼承回顯服務器的代碼,然后添加單詞鍵值對并且重寫posses函數即可,至于客戶端還以一樣不需要去改變。
UdpDictServer:

package network;import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;public class UdpDictServer extends UdpEchoServer {private HashMap<String, String> dict = null;public UdpDictServer(int port) throws SocketException {super(port);dict = new HashMap<>();dict.put("hello", "你好");dict.put("pig", "小豬");dict.put("dog", "小狗");dict.put("cat", "小貓");}@Overridepublic String process(String request) {return (String) dict.getOrDefault(request, "未搜索到單詞");}public static void main(String[] args) throws IOException {UdpDictServer dictServer = new UdpDictServer(4090);dictServer.start();}
}

四、TCP回顯服務器及客戶端編寫

TcpEchoServer:

package network;import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class TcpEchoServer {ServerSocket serverSocket = null;public TcpEchoServer(int port) throws IOException {serverSocket = new ServerSocket(port);}public void start() throws IOException {System.out.println("服務器啟動!!!");while (true) {// 建立連接Socket clientSocket = serverSocket.accept();// 建立線程池 這里建立的是可以自動擴容的線程池ExecutorService pool=Executors.newCachedThreadPool();// 為了方便多個客戶端對服務器發起請求// 這里使用主線程來處理這里的循環 然后使用多線程的放式去去處理每一個客戶端的請求
//            Thread t = new Thread(() -> {
//                try {
//                    processConnection(clientSocket);
//                } catch (IOException e) {
//                    throw new RuntimeException(e);
//                }
//
//            });
//
//            t.start();// 使用線程池的方式pool.submit(new Runnable() {@Overridepublic void run() {try {processConnection(clientSocket);} catch (IOException e) {throw new RuntimeException(e);}}});}}private void processConnection(Socket clientSocket) throws IOException {System.out.printf("[%s:%d] 客戶端上線!\n", clientSocket.getInetAddress(), clientSocket.getPort());// 獲取字節流對象try (InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()) {Scanner sc = new Scanner(inputStream);while (true) {// 兩種情況if (!sc.hasNext()) {System.out.printf("[%s:%d] 客戶端下線!\n", clientSocket.getInetAddress(), clientSocket.getPort());break;}// 獲取請求String request = sc.next();// 處理請求 String response = process(request);// 返回請求outputStream.write(response.getBytes());// 服務器打印日志System.out.printf("[%s:%d] req=%s resp=%s", clientSocket.getInetAddress(), clientSocket.getPort(), request, response);}} catch (IOException e) {throw new RuntimeException(e);} finally {// 每次一個客戶端請求的連接最后都要關閉 否則當多個客戶端連接同一個服務器的時候就會出現文件描述符表爆滿的問題// 這個問題簡單想一下就會理解clientSocket.close();}}private String process(String request) {return request + '\n';}public static void main(String[] args) throws IOException {TcpEchoServer server = new TcpEchoServer(4090);server.start();}}

TcpEchoClient:

package network;import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;public class TcpEchoClient {Socket socket = null;public TcpEchoClient(String ip, int port) throws IOException {// 這里根據ip和port號自動和服務器建立連接// 具體完成的操作都是系統內核完成的socket = new Socket(ip,port);}private void start() {System.out.println("客戶端啟動!!!");Scanner sc = new Scanner(System.in);// 獲取字節流對象try (InputStream inputStream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream()) {Scanner scNetwork = new Scanner(inputStream);while (true) {System.out.println("請輸入要發送的內容:");// 輸入請求String request = sc.next();request += '\n';// 發送請求outputStream.write(request.getBytes());// 兩中情況// 第一種:tcp連接斷開 返回false// 第二種:有請求返回if (!scNetwork.hasNext()) {break;}String response = scNetwork.next();System.out.println(response);}} catch (IOException e) {throw new RuntimeException(e);}}public static void main(String[] args) throws IOException {TcpEchoClient client = new TcpEchoClient("localhost", 4090);client.start();}}

在tcp服務器代碼的編寫中我們發現服務器無法去應對多個客戶端的請求,因此我們使用多線程或者線程池的方式,讓每一個線程去處理一個請求。此時不免去聯想到一個問題如果在一個場景當中,服務器收到的不同客戶端的請求越來越多,我們難道需要不停的去創建線程嗎,如果真是這樣服務器肯定支撐不住。事實上對于這種情況可以使用IO多路復用+分布式的方法。
分布式我們都知道,那么IO多路復用指的是什么?其實就是使用一個線程去管理多個socket(可以將socket理解成客戶端的請求),這些socket往往不是同時有數據需要處理,而是同一時刻只有少數的socket需要去讀取數據。

五、數據序列化的方式

5.1 基于行文本的方式傳輸

這種格式是自定義的,只要確保客戶端與服務器使用的是同一套規則即可。缺點就是不好用,可維護性差。

5.2 基于XML的格式

XML是通過成對的標簽來進行組織的。

<request><userId>1234</userId>
</request>

5.3 基于json

當前最流行最廣泛的使用方式,是以鍵值對的形式,可讀性非常好而且比XML簡潔。

{userId: 1234
}

5.4 yml

和前兩種是類似的,是基于縮進的格式,使用縮進來表達包含/嵌套關系。

requestuserId: 1234position: "180E40N"

5.5 protobuffer(pb)

前面幾種說到底還是文本格式,肉眼還能看懂,這里的pb就是二進制格式了,可以對數據進行進一步的整理和壓縮,雖然可讀性不好但是對空間進行最充分的利用,節省網絡帶寬,效率也最高,適用于對傳輸效率高的場景。

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

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

相關文章

STM32芯片系列與產品后綴解讀

一. 產品系列 STM32單片機是一系列基于ARM Cortex-M內核的32位微控制器&#xff0c;廣泛應用于嵌入式系統中。 STM32系列由STMicroelectronics&#xff08;意法半導體&#xff09;開發和生產&#xff0c;并憑借其靈活的設計、豐富的外設和強大的生態系統&#xff0c;成為嵌入式…

咬文嚼字:詞元是當今生成式人工智能失敗的一個重要原因

生成式人工智能模型處理文本的方式與人類不同。了解它們基于"標記"的內部環境可能有助于解釋它們的一些奇怪行為和頑固的局限性。從 Gemma 這樣的小型設備上模型到 OpenAI 業界領先的 GPT-4o 模型&#xff0c;大多數模型都建立在一種稱為轉換器的架構上。由于轉換器在…

Ubuntu24.04清理常見跟蹤軟件tracker

盡量一天一更&#xff0c;不刷視頻&#xff0c;好好生活 打開系統監視器&#xff0c;發現開機有個tracker-miner-fs-fs3的跟蹤程序&#xff0c;而且上傳了10kb的數據。 搜索知&#xff0c;該程序會搜集應用和文件的信息。 刪除tracker 顯示帶tracker的apt程序 sudo apt lis…

ThreadLocal的內存泄漏

什么是內存泄漏 程序在申請內存后&#xff0c;無法釋放已申請的內存空間在定義變量時&#xff0c;需要一段內存空間來存儲數據信息&#xff0c;而這段內存如果一直不被釋放&#xff0c;那么就會導致內存被占用光&#xff0c;而被占用的這個對象&#xff0c;一直不能被回收掉&am…

書生·浦語2.5開源,推理能力再創新標桿

導讀 2024 年 7 月 3 日&#xff0c;上海人工智能實驗室與商湯科技聯合香港中文大學和復旦大學正式發布新一代大語言模型書?浦語2.5&#xff08;InternLM2.5&#xff09;。相比上一代模型&#xff0c;InternLM2.5 有三項突出亮點&#xff1a; 推理能力大幅提升&#xff0c;在…

VUE與React的生命周期對比

前言 在前端開發中&#xff0c;Vue和React是兩個非常流行的JavaScript框架&#xff0c;它們各自有著獨特的生命周期機制。了解并熟練掌握這些生命周期&#xff0c;對于開發高效、可維護的前端應用至關重要。本文將詳細對比Vue和React的生命周期&#xff0c;幫助開發者更好地理…

Python | Leetcode Python題解之第222題完全二叉樹的節點個數

題目&#xff1a; 題解&#xff1a; # Definition for a binary tree node. # class TreeNode: # def __init__(self, val0, leftNone, rightNone): # self.val val # self.left left # self.right right class Solution:def countNodes(self,…

好玩的珠璣妙算-加作弊帶概率空間+日志存儲240705mindMaster

Python代碼 import random import time import datetimeNUM_DIGITS 10 #NUM_NON_ZERO_DIGITS 9failFlag 0class Mastermind:def __init__(self, code_length, max_attempts, secret01code, game_id): # def __init__(self, code_length, max_attempts):self.code_length…

【Elasticsearch】Elasticsearch倒排索引詳解

文章目錄 &#x1f4d1;引言一、倒排索引簡介二、倒排索引的基本結構三、Elasticsearch中的倒排索引3.1 索引和文檔3.2 創建倒排索引3.3 倒排索引的存儲結構3.4 詞典和倒排列表的優化 四、倒排索引的查詢過程4.1 過程4.2 示例 五、倒排索引的優缺點5.1 優點5.2 缺點 六、倒排索…

【Excel】求和帶文字的數據

目錄標題 1. 給出樣例2. CtrlE3. CtrlH → A替換為 → 全部替換 1. 給出樣例 2. CtrlE 3. CtrlH → A替換為 → 全部替換

算法期末函數題

R6-1 可重復選擇的組合數問題 【考核知識點】可重復選擇的組合計數 【問題描述】 有n個不同元素&#xff08;1<n<20&#xff09;&#xff0c;每個元素可以選多次&#xff0c;一共需要選出k個元素出來&#xff08;1<k<20&#xff09;&#xff0c;問有多少種選取的…

監控易V7.6.6.15升級詳解2:設備管理功能

隨著企業IT架構的日益復雜&#xff0c;對設備管理的需求也在不斷提升。為了滿足廣大用戶對于設備管理的高效、精準需求&#xff0c;我們榮幸地宣布監控易系統已完成了一次重要的版本升級。本次升級不僅優化了原有功能&#xff0c;還新增了一系列實用特性&#xff0c;旨在為用戶…

仿qq音樂播放微信小程序模板源碼

手機qq音樂應用小程序&#xff0c;在線音樂播放器微信小程序網頁模板。包含&#xff1a;音樂歌曲主頁、推薦、排行榜、搜索、音樂播放器、歌單詳情等。 仿qq音樂播放微信小程序模板源碼

【ubuntu自啟shell腳本】——在ubuntu中如何使用系統自帶的啟動應用程序設置開機自啟自己的本地shell腳本

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄 前言一、設置開機自啟shell腳本1.使用 gnome-session-properties2.測試的shell例程代碼 總結 前言 在Ubuntu系統中設置開機自啟腳本是一種重要的自動化方法。開機自…

YOLO-World實時開集檢測論文閱讀

論文&#xff1a;《YOLO-World: Real-Time Open-Vocabulary Object Detection》 代碼&#xff1a;https://github.com/AILab-CVC/YOLO-World 1.Abstract 我們介紹了YOLO World&#xff0c;這是一種創新的方法&#xff0c;通過在大規模數據集上進行視覺語言建模和預訓練&#…

js之彈性布局使用方法

彈性布局&#xff08;Flexbox&#xff09;是一種現代化的 CSS 布局方法&#xff0c;它可以讓您更方便地創建響應式和動態布局。在本篇文檔中&#xff0c;我們將介紹彈性布局的基本概念以及如何在項目中使用它。 一、基本概念 容器&#xff08;Container&#xff09;&#xff…

WPF中邏輯樹和視覺樹

在WPF&#xff08;Windows Presentation Foundation&#xff09;中&#xff0c;“邏輯樹”&#xff08;Logical Tree&#xff09;和“可視樹”&#xff08;Visual Tree&#xff09;是兩個重要的概念&#xff0c;它們代表了不同的對象層次結構&#xff0c;用于描述應用程序的組織…

洛谷 [SNCPC2024] 寫都寫了,交一發吧 題解

分析 顯然&#xff0c;兩個相同的數去按位與的結果還是該數。 由于一個代碼可以提交多次&#xff0c;那么可以把得分最高的代碼提交兩次&#xff0c;這樣的得分就是這個代碼的得分&#xff0c;很明顯&#xff0c;這樣是最優的。 Code #include<iostream> using names…

STM32微控制器的SPI存儲解決方案:W25Q64 Flash存儲器深度應用

摘要 在嵌入式系統設計中&#xff0c;存儲解決方案對于數據的持久化至關重要。W25Q64 Flash存儲器以其高效的存儲能力和與SPI總線的兼容性&#xff0c;成為STM32微控制器項目中的優選。本文將深入探討STM32微控制器的SPI存儲解決方案&#xff0c;重點介紹W25Q64 Flash存儲器的…

vue3+antd 實現點擊按鈕彈出對話框

格式1&#xff1a;確認對話框 按鈕&#xff1a; 點擊按鈕之后&#xff1a; 完整代碼&#xff1a; <template><div><a-button click"showConfirm">Confirm</a-button></div> </template> <script setup> import {Mod…