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
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...
  • 目錄前言PostgreSql安裝測試額外Nuget安裝Person.cs模擬運行Navicate連postgresql解決方案Garnet為什麼要選擇Garnet而不是RedisRedis不再開源Windows版的Redis是由微軟維護的Windows Redis版本老舊,後續可能不再更新Garne ...
  • C#TMS系統代碼-聯表報表學習 領導被裁了之後很快就有人上任了,幾乎是無縫銜接,很難讓我不想到這早就決定好了。我的職責沒有任何變化。感受下來這個系統封裝程度很高,我只要會調用方法就行。這個系統交付之後不會有太多問題,更多應該是做小需求,有大的開發任務應該也是第二期的事,嗯?怎麼感覺我變成運維了?而 ...
  • 我在隨筆《EAV模型(實體-屬性-值)的設計和低代碼的處理方案(1)》中介紹了一些基本的EAV模型設計知識和基於Winform場景下低代碼(或者說無代碼)的一些實現思路,在本篇隨筆中,我們來分析一下這種針對通用業務,且只需定義就能構建業務模塊存儲和界面的解決方案,其中的數據查詢處理的操作。 ...
  • 對某個遠程伺服器啟用和設置NTP服務(Windows系統) 打開註冊表 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpServer 將 Enabled 的值設置為 1,這將啟用NTP伺服器功 ...
  • title: Django信號與擴展:深入理解與實踐 date: 2024/5/15 22:40:52 updated: 2024/5/15 22:40:52 categories: 後端開發 tags: Django 信號 松耦合 觀察者 擴展 安全 性能 第一部分:Django信號基礎 Djan ...
  • 使用xadmin2遇到的問題&解決 環境配置: 使用的模塊版本: 關聯的包 Django 3.2.15 mysqlclient 2.2.4 xadmin 2.0.1 django-crispy-forms >= 1.6.0 django-import-export >= 0.5.1 django-r ...
  • 今天我打算整點兒不一樣的內容,通過之前學習的TransformerMap和LazyMap鏈,想搞點不一樣的,所以我關註了另外一條鏈DefaultedMap鏈,主要調用鏈為: 調用鏈詳細描述: ObjectInputStream.readObject() DefaultedMap.readObject ...
  • 後端應用級開發者該如何擁抱 AI GC?就是在這樣的一個大的浪潮下,我們的傳統的應用級開發者。我們該如何選擇職業或者是如何去快速轉型,跟上這樣的一個行業的一個浪潮? 0 AI金字塔模型 越往上它的整個難度就是職業機會也好,或者說是整個的這個運作也好,它的難度會越大,然後越往下機會就會越多,所以這是一 ...
  • @Autowired是Spring框架提供的註解,@Resource是Java EE 5規範提供的註解。 @Autowired預設按照類型自動裝配,而@Resource預設按照名稱自動裝配。 @Autowired支持@Qualifier註解來指定裝配哪一個具有相同類型的bean,而@Resourc... ...