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
  • 下麵是一個標準的IDistributedCache用例: public class SomeService(IDistributedCache cache) { public async Task<SomeInformation> GetSomeInformationAsync (string na ...
  • 這個庫提供了在啟動期間實例化已註冊的單例,而不是在首次使用它時實例化。 單例通常在首次使用時創建,這可能會導致響應傳入請求的延遲高於平時。在註冊時創建實例有助於防止第一次Request請求的SLA 以往我們要在註冊的時候實例單例可能會這樣寫: //註冊: services.AddSingleton< ...
  • 最近公司的很多項目都要改單點登錄了,不過大部分都還沒敲定,目前立刻要做的就只有一個比較老的項目 先改一個試試手,主要目標就是最短最快實現功能 首先因為要保留原登錄方式,所以頁面上的改動就是在原來登錄頁面下加一個SSO登錄入口 用超鏈接寫的入口,頁面改造後如下圖: 其中超鏈接的 href="Staff ...
  • Like運算符很好用,特別是它所提供的其中*、?這兩種通配符,在Windows文件系統和各類項目中運用非常廣泛。 但Like運算符僅在VB中支持,在C#中,如何實現呢? 以下是關於LikeString的四種實現方式,其中第四種為Regex正則表達式實現,且在.NET Standard 2.0及以上平... ...
  • 一:背景 1. 講故事 前些天有位朋友找到我,說他們的程式記憶體會偶發性暴漲,自己分析了下是非托管記憶體問題,讓我幫忙看下怎麼回事?哈哈,看到這個dump我還是非常有興趣的,居然還有這種游戲幣自助機類型的程式,下次去大玩家看看他們出幣的機器後端是不是C#寫的?由於dump是linux上的程式,剛好win ...
  • 前言 大家好,我是老馬。很高興遇到你。 我們為 java 開發者實現了 java 版本的 nginx https://github.com/houbb/nginx4j 如果你想知道 servlet 如何處理的,可以參考我的另一個項目: 手寫從零實現簡易版 tomcat minicat 手寫 ngin ...
  • 上一次的介紹,主要圍繞如何統一去捕獲異常,以及為每一種異常添加自己的Mapper實現,並且我們知道,當在ExceptionMapper中返回非200的Response,不支持application/json的響應類型,而是寫死的text/plain類型。 Filter為二方包異常手動捕獲 參考:ht ...
  • 大家好,我是R哥。 今天分享一個爽飛了的面試輔導 case: 這個杭州兄弟空窗期 1 個月+,面試了 6 家公司 0 Offer,不知道問題出在哪,難道是杭州的 IT 崩盤了麽? 報名面試輔導後,經過一個多月的輔導打磨,現在成功入職某上市公司,漲薪 30%+,955 工作制,不咋加班,還不捲。 其他 ...
  • 引入依賴 <!--Freemarker wls--> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.30</version> </dependency> ...
  • 你應如何運行程式 互動式命令模式 開始一個互動式會話 一般是在操作系統命令行下輸入python,且不帶任何參數 系統路徑 如果沒有設置系統的PATH環境變數來包括Python的安裝路徑,可能需要機器上Python可執行文件的完整路徑來代替python 運行的位置:代碼位置 不要輸入的內容:提示符和註 ...