JAVA Socket編程

来源:https://www.cnblogs.com/yaenli/archive/2023/06/02/17452774.html
-Advertisement-
Play Games

aliases: [] tags : " " summary: [基於TCP/IP和UDP協議的Java Socket網路通信編程] author : [yaenli] notekey: [20230512-143738] # Socket 網路模型 Socket編程是在TCP/IP、UDP協議上的 ...


aliases: []
tags   : " "
summary: [基於TCP/IP和UDP協議的Java Socket網路通信編程]
author : [yaenli]
notekey: [20230512-143738]

Socket 網路模型

Socket編程是在TCP/IP、UDP協議上的網路編程,在此之前,先瞭解下常見的網路模型:

  1. OSI七層模型與TCP模型
    image.png

  2. OSI七層模型詳解OSI七層模型詳解
    image.png

Socket就在應用程式的傳輸層和應用層之間的一個抽象層:

image.png

Socket 知識

Socket概述

  • 在電腦網路編程技術中,兩個進程或者說兩台電腦可以通過一個網路通信連接實現數據的交換,這種通信鏈路的端點就被稱為“套接字”(Socket)。
  • Socket 是網路驅動層提供給應用程式的一個介面或者說一種機制。
  • Socket 方便了應用程式訪問通訊協議TCP/IP、UDP 。
  • 我們可以把套接字看成是 電話機,有了套接字,才有了通訊的工具。我們可以把IP地址看成是話號碼, 埠號看成是分機號。

Java中Socket的實現

Socket的底層機制非常複雜,Java平臺提供了一些簡單但是強大的類,可以簡單有效地使用Socket開發通信程式而無須瞭解底層機制。

java.net包提供了若幹支持基於套接字的客戶端/伺服器通信的類:

ServerSocket 類用來創建 TCP/IP 伺服器端;
Socket 類用來創建 TCP/IP 客戶端;
DatagramSocket 類用來實現 UDP 協議的客戶端和伺服器套接字;
DatagramPacket 類用來封裝、處理 UDP 協議的數據包;
InetAddress 類用於封裝IP和DNS等地址信息,在創建數據報報文和 Socket 對象時,可以使用。

Socket 編程

基於TCP/IP協議的Socket編程

(1)分別使用java.net.Socket和ServerSocket來創建客戶端和伺服器端套接字,它們是基於TCP協議進行工作的,工作過程如同打電話的過程,只有雙方都接通了,才能開始通話。
(2)基於TCP創建的套接字叫做 流套接字。Socket通過數據流來完成數據的傳遞工作。
(3)Socket編程中,遵循client-server模型。伺服器端相當於一個監聽器,用來監聽埠。

相關類

  • Socket 類:用構造方法創建套接字,並將此套接字連接至指定的主機和埠。
// 常用構造
public Socket(@Nullable  String host, int port ) throws UnknownHostException, IOException ;
public Socket(InetAddress address, int port ) throws IOException ;

// 常用方法
public void connect(SocketAddress host, int timeout) throws IOException;// 將此套接字連接到伺服器,並指定一個超時值。
public InetAddress getInetAddress(); // 返回遠程IP信息
public int getPort(); // 返回遠程埠
public int getLocalPort(); // 返回本地埠
public InputStream getInputStream(); // 獲取輸入流
public OutputStream getOutputStream(); // 獲取輸出流
public void close() throws IOException // 關閉此套接字
  • ServerSocket 類:等待客戶端建立連接,連接建立以後進行通信。
// 常用構造方法
public ServerSocket(int port) throws IOException; // 創建指定埠的伺服器套接字
public ServerSocket(int port, int backlog) throws IOException; // 指定最大連接隊列
public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException; // 指定綁定的本地ip地址

// 常用方法:Socket中的方法都能適用,除此之外,還有以下方法
public Socket accept() throws IOException // (阻塞方法)偵聽連接請求,並返回一個新的通信套接字,該 Socket 連接到客戶端的 Socket
public void bind(SocketAddress host, int backlog) // 將ServerSocket綁定到特定地址

開發流程

image.png

詳細交互過程:

image.png

服務端編程步驟:

  1. 實例化ServerSocket 對象,綁定指定埠;
  2. 調用 accept(),監聽連接請求(阻塞等待),並返回通信Socket
  3. Socket 獲取輸出流輸入流,從輸入流中讀取請求信息,向輸出流中寫入響應信息;
  4. 關閉數據流和通信套接字。

客戶端編程步驟:

  1. 實例化 Socket 對象,連接到指定伺服器端;
  2. Socket 獲取輸出流輸入流,向輸出流中寫入請求信息,從輸入流中讀取響應信息;
  3. 關閉數據流和通信套接字。

客戶端和伺服器端的交互,採用一問一答的模式,先啟動伺服器進入監聽狀態,等待客戶端的連接請求,連接成功以後,客戶端先 “發言”,伺服器給予 “回應”。

示例代碼

採用多線程的方式,實現一個服務端響應多個客戶端請求。

服務端代碼: 使伺服器端Socket一直處於監聽狀態。伺服器端每監聽到一個請求,創建一個線程對象並啟動。

import java.net.*;
import java.io.*;
 
public class SocketServer {
	
	public static void main(String[] args) {
		ServerSocket serverSocket = null;
		try {
			// 建立一個伺服器Socket(ServerSocket)指定埠並開始監聽
			serverSocket = new ServerSocket(8800);
 
			// 監聽一直進行中
			while (true) {
				// 使用accept()方法等待客戶發起通信
				Socket socket = serverSocket.accept();
				new SocketThread(socket).start();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
import java.net.*;
import java.io.*;
 
public class SocketThread extends Thread {

	/*
	 * 	(1)創建伺服器端線程類,run()方法中實現對一個請求的響應處理。
	 * 	(2)讓伺服器端Socket一直處於監聽狀態。
	 * 	(3)伺服器端每監聽到一個請求,創建一個線程對象並啟動
	 */

	Socket socket = null;
	//每啟動一個線程,連接對應的Socket
	public LoginThread(Socket socket) {
		this.socket = socket;
	}
	
	//啟動線程,即響應客戶請求
	public void run() {
		InputStream is = null;
		ObjectInputStream ois = null;
		OutputStream os = null;
		try {
			//打開輸入流
			is = socket.getInputStream();
			//反序列化
			ois = new ObjectInputStream(is);
			//獲取客戶端信息,即從輸入流讀取信息,DataObject為自定義數據類
			DataObject data = (DataObject)ois.readObject();
			if(data!=null){
				System.out.println("我是服務端,客戶端傳送信息為:" + data.getMessage());
			}
			
			//給客戶端一個響應,即向輸出流中寫入信息
			os = socket.getOutputStream();
			String reply = "服務端接收成功!";
			os.write(reply.getBytes());
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}finally{
			try {
				os.close();
				ois.close();
				is.close();
				socket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}			
		}	
	}	
}

客戶端代碼:

import java.net.*;
import java.io.*;
 
public class SocketClient {
	/*
	 * 客戶端通過輸出流向伺服器端發送請求信息
	 * 伺服器偵聽客戶端的請求得到一個Socket對象,將這個Socket對象傳遞給線程類
	 * 線程類通過輸入流獲取客戶端的請求並通過輸出流向客戶端發送響應信息
	 * 客戶端通過輸入流讀取伺服器發送的響應信息
	 * 
	 */
 
	public static void main(String[] args) {
		
		Socket socket = null;
		OutputStream os = null;
		ObjectOutputStream oos = null;
		InputStream is = null;
		BufferedReader br = null;
		try {
			// 建立客戶端Socket連接,指定伺服器的位置為本機以及埠為8800
			socket = new Socket("localhost", 8800);
			// 打開輸出流
			os = socket.getOutputStream();
			// 對象序列化
			oos = new ObjectOutputStream(os);
			// 發送客戶端信息,即向輸出流中寫入信息,DataObject為自定義數據類
			DataObject data = new DataObject("服務端你好,我是客戶端");
			oos.writeObject(data);
			socket.shutdownOutput();
 
			// 接收伺服器端的響應,即從輸入流中讀取信息
			is = socket.getInputStream();
			br = new BufferedReader(new InputStreamReader(is));
			String reply;
			while ((reply = br.readLine()) != null) {
				System.out.println("我是客戶端,伺服器的響應為:" + reply);
			}
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				br.close();
				is.close();
				oos.close();
				os.close();
				socket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

基於UDP的socket編程

(1)基於TCP的網路通信是安全的,是雙向的,如同打電話,需要先有服務端,建立雙向連接後,才開始數據通信。
(2)基於UDP的套接字就是 數據報套接字。數據報是表示通信的一種報文類型,使用數據報進行通信時無須事先建立連接,只需要指明對方地址,然後將數據送出去。這樣的網路通信是不安全的,所以只應用在如聊天系統、咨詢系統等場合下。
(3)兩端都要先構造好相應的數據包。數據報套接字發送成功之後,就相當於建立了一個虛連接,雙方可以發送數據。
(4)Java中有兩個可使用數據報實現通信的類,即DatagramPacket和DatagramSocket。
(5)DatagramPacket類起到數據容器的作用,DatagramSocket類用於發送或接收DatagramPacket,以此實現數據報通信。

UDP與TCP通信的區別:

TCP UDP
是否連接 面向連接 面向非連接
傳輸可靠性 可靠 不可靠
速度

相關類

  • java.net. DatagramPacket :數據電報包,用於封裝發送的數據。
  • java.net. DatagramSocket :數據電報套接字,不維護連接狀態,不產生輸入/輸出數據流,用於接收和發送DatagramPacket對象封裝好的數據報。

開發流程

image.png

UDP通信的兩個端點程式是平等的,沒有主次之分,甚至它們的代碼都可以完全是一樣的。

接收端編程步驟:

  1. 實例化DatagramSocket 創建數據報套接字,綁定到指定埠;
  2. 實例化DatagramPacket 建立要接收的UDP包;
  3. 調用DatagramSocket.receive() ,接收UDP包;
  4. 處理接收到的DatagramPacket 數據包,關閉數據報套接字。

發送端編程步驟:

  1. 實例化DatagramSocket 創建數據報套接字,綁定到指定埠;
  2. 實例化DatagramPacket 建立要發送的UDP包;
  3. 調用DatagramSocket.send() ,發送UDP包;
  4. 關閉數據報套接字。

示例代碼

發送方發送咨詢問題,接收方回應咨詢。

接收端代碼:

import java.net.*;
import java.io.*;
 
public class UDPReceive {
 
	public static void main(String[] args) {
		/*
		 * 接收方實現步驟如下: 
		 * (1)創建DatagramPacket對象,準備接收封裝的數據。
		 * (2)創建DatagramSocket對象,接收數據保存於DatagramPacket對象中。
		 * (3)利用DatagramPacket對象處理數據。
		 */
 
		DatagramSocket ds = null;
		DatagramPacket dp = null;
		DatagramPacket dp_reply = null;
		// 創建DatagramPacket對象,用來準備接收數據
		byte[] buf = new byte[1024];
		dp = new DatagramPacket(buf, 1024);
		try {
			// 創建DatagramSocket對象,接收數據
			ds = new DatagramSocket(8800);
			ds.receive(dp);
			// 顯示接收到的信息
			String mess = new String(dp.getData(), 0, dp.getLength());
			System.out.println(dp.getAddress().getHostAddress() + "說:" + mess);
			
            // 給發送端返回數據,需要發送端去接受
			String reply = "接收端:你好,我在,請咨詢!";
			// 創建DatagramPacket對象,封裝數據
			dp_reply = new DatagramPacket(reply.getBytes(),
					reply.getBytes().length, dp.getSocketAddress());
			ds.send(dp_reply);
		} catch (SocketException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			ds.close();
		}
	}
}

發送端代碼:

import java.net.*;
import java.io.*;
 
public class UDPSend {
	/*
	 * 發送方實現步驟如下: 
	 * (1)獲取本地主機的InetAddress對象。 
	 * (2)創建DatagramPacket對象,封裝要發送的信息。
	 * (3)利用DatagramSocket對象將DatagramPacket對象數據發送出去。
	 */
 
	public static void main(String[] args) {
		DatagramSocket ds = null;
		InetAddress ia = null;
		String mess = "發送端:你好,我想咨詢一個問題。";
		try {
			// 獲取本地主機地址
			ia = InetAddress.getByName("localhost");
			// 創建DatagramPacket對象,封裝數據
			DatagramPacket dp = new DatagramPacket(mess.getBytes(),
					mess.getBytes().length, ia, 8800);
			// 創建DatagramSocket對象,向伺服器發送數據
			ds = new DatagramSocket();
			ds.send(dp);
            
            //接受返回來的數據。
			byte[] buf = new byte[1024];
			DatagramPacket dpre = new DatagramPacket(buf, buf.length);
			ds.receive(dpre);
			// 顯示接收到的信息
			String reply = new String(dpre.getData(), 0, dpre.getLength());
			System.out.println(dpre.getAddress().getHostAddress() + "說:"
					+ reply);
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (SocketException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			ds.close();
		}
	}
}

參考文章

Java網路編程——Socket 編程
Java---Socket編程UDP/TCP-CSDN博客
JAVA進階——Socket編程-CSDN博客
Java Socket實現簡單的Http伺服器


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • setTimeout 倒計時誤差的出現主要與 JavaScript 的事件迴圈機制和計時器的執行方式有關。 在 JavaScript 中,事件迴圈是用於管理和調度代碼執行的機制。setTimeout 函數用於設置一個定時器,在指定的延遲時間後執行回調函數。然而,由於事件迴圈的機制,setTimeou ...
  • # 高解析度大圖像可縮放 Web 查看器的實踐 ## 一、使用 vips 將高解析度大圖像轉換為 DZI 1. 安裝 vips 具體安裝步驟請參考[libvips Install](https://www.libvips.org/install.html)。 註意,在 windows 11 中安裝 ...
  • 前面博文有介紹JavaScript中數組的一些特性,通過對這些數組特性的深入梳理,能夠加深我們對數組相關知識的理解,詳見博文: [一文搞懂JavaScript數組的特性](https://www.cnblogs.com/jimojianghu/p/17292277.html) 其實,在前端開發中,除 ...
  • # 前言 本文主要講述設計模式中的**抽象工廠模式**,文中使用通俗易懂的案例,使你更好的學習本章知識點並理解原理,做到有道無術。 # 一.什麼是抽象工廠模式 抽象工廠是23種設計模式中**創建型模式**的一種,抽象工廠是由多個工廠組合而成。 上一章我們提到的工廠模式只存在一個抽象角色,而抽象工廠是 ...
  • ## 概述 Java 對客戶程式的通信過程進行了抽象,提供了通用的協議處理框架,該框架封裝了 Socket,主要包括以下類: - URL 類:統一資源定位符,表示客戶程式要訪問的遠程資源 - URLConnection 類:表示客戶程式與遠程伺服器的連接,客戶程式可以從 URLConnection ...
  • 基於java的美食菜譜分享系統設計與實現,餐飲分享平臺設計與實現,可用於美食線上分享平臺,作為世界各地愛好美食的人們的橋梁,為其創造一個氛圍好的平臺,促進美食世界的文化交流。該系統是一個供商家或者個人推薦美食的網站,網站不支持交易僅供分享。 ...
  • > 本文首發於公眾號:Hunter後端 > 原文鏈接:[celery筆記二之建立celery項目、配置及幾種載入方式](https://mp.weixin.qq.com/s/KZjuypZ-e9EHi7XkKXt0Xg) 接下來我們創建一個 celery 項目,文件夾及目錄如下: ```python ...
  • **原文鏈接:** [為什麼說 Go 語言字元串是不可變的?](https://mp.weixin.qq.com/s/AOb6AjKwyTwLeAUou0AU-Q) 最近有讀者留言說,平時在寫代碼的過程中,是會對字元串進行修改的,但網上都說 Go 語言字元串是不可變的,這是為什麼呢? 這個問題本身並 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...