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 Framework 4.8 開發的深度學習模型部署測試平臺,提供了YOLO框架的主流系列模型,包括YOLOv8~v9,以及其系列下的Det、Seg、Pose、Obb、Cls等應用場景,同時支持圖像與視頻檢測。模型部署引擎使用的是OpenVINO™、TensorRT、ONNX runti... ...
  • 十年沉澱,重啟開發之路 十年前,我沉浸在開發的海洋中,每日與代碼為伍,與演算法共舞。那時的我,滿懷激情,對技術的追求近乎狂熱。然而,隨著歲月的流逝,生活的忙碌逐漸占據了我的大部分時間,讓我無暇顧及技術的沉澱與積累。 十年間,我經歷了職業生涯的起伏和變遷。從初出茅廬的菜鳥到逐漸嶄露頭角的開發者,我見證了 ...
  • C# 是一種簡單、現代、面向對象和類型安全的編程語言。.NET 是由 Microsoft 創建的開發平臺,平臺包含了語言規範、工具、運行,支持開發各種應用,如Web、移動、桌面等。.NET框架有多個實現,如.NET Framework、.NET Core(及後續的.NET 5+版本),以及社區版本M... ...
  • 前言 本文介紹瞭如何使用三菱提供的MX Component插件實現對三菱PLC軟元件數據的讀寫,記錄了使用電腦模擬,模擬PLC,直至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1. PLC開發編程環境GX Works2,GX Works2下載鏈接 https:// ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • 1、jQuery介紹 jQuery是什麼 jQuery是一個快速、簡潔的JavaScript框架,是繼Prototype之後又一個優秀的JavaScript代碼庫(或JavaScript框架)。jQuery設計的宗旨是“write Less,Do More”,即倡導寫更少的代碼,做更多的事情。它封裝 ...
  • 前言 之前的文章把js引擎(aardio封裝庫) 微軟開源的js引擎(ChakraCore))寫好了,這篇文章整點js代碼來測一下bug。測試網站:https://fanyi.youdao.com/index.html#/ 逆向思路 逆向思路可以看有道翻譯js逆向(MD5加密,AES加密)附完整源碼 ...
  • 引言 現代的操作系統(Windows,Linux,Mac OS)等都可以同時打開多個軟體(任務),這些軟體在我們的感知上是同時運行的,例如我們可以一邊瀏覽網頁,一邊聽音樂。而CPU執行代碼同一時間只能執行一條,但即使我們的電腦是單核CPU也可以同時運行多個任務,如下圖所示,這是因為我們的 CPU 的 ...
  • 掌握使用Python進行文本英文統計的基本方法,並瞭解如何進一步優化和擴展這些方法,以應對更複雜的文本分析任務。 ...
  • 背景 Redis多數據源常見的場景: 分區數據處理:當數據量增長時,單個Redis實例可能無法處理所有的數據。通過使用多個Redis數據源,可以將數據分區存儲在不同的實例中,使得數據處理更加高效。 多租戶應用程式:對於多租戶應用程式,每個租戶可以擁有自己的Redis數據源,以確保數據隔離和安全性。 ...