JAVA SE 實戰篇 C2 網路程式設計基礎

雫#1999發表於2020-12-04

P1 關於網路程式設計的幾個知識點

1 TCP/IP協議

TCP/IP協議並非一個協議,由兩個協議族組成,IP協議族和TCP協議族,其中IP協議工作在網路層,TCP工作在傳輸層

TCP協議是端對端的通訊協議,通過IP地址和IP協議可以找到通訊的物件,即某裝置,但還需要知道是哪個程式在進行本次通訊,為了解決這個問題,TCP提出了“端”的概念,即埠號,埠號是一個2B無符號編號,從0-65535,不同的通訊雙方需要先指定一個埠號,以便將資訊傳輸給指定的應用程式

2 TCP和UDP

TCP協議族有兩個重要的協議:TCP協議和UDP協議,前者稱為面向連線後者稱為面向無連線

面向連線是保證質量的資料傳輸面向無連線是不保證質量的資料傳輸

對於速度要求更高的應用場景,可以選擇面向無連線的資料傳輸方式(UDP),對於每個位元組都不能出錯的應用場景,使用面向連線的資料傳輸方式(TCP)

3 MAC地址和IP地址

連入網路的計算機或其它裝置,都必須要有至少一塊聯網電路板,這塊電路板叫做MAC,俗稱網路卡,現在的計算機網路卡一般被整合到了主機板上,每塊網路卡都有全世界唯一的6位元組編號,這個編號即MAC地址,也稱區域網地址,乙太網地址,實體地址

但是MAC地址很難記,如果網路卡可以拆卸下來,那麼MAC地址也會變化,這對網路通訊是很棘手的問題,為此出現了IP地址,作業系統會將IP地址和MAC地址繫結,由此避免了MAC地址變化的麻煩

127.0.0.1在網路程式設計中代替本地地址

P2 簡單網路通訊形式

1 P2P模式

P2P(對等網路模式):對端到對端,這是一種對等網路通訊形式,在P2P網路中,各個聯網的節點的身份都是一樣的,沒有高低主從之分,沒有許可權差異

實現P2P通訊,通訊雙方必須知道對方的IP,並且事先確定好埠號,因為這種方式不存在管理者和被管理者,通訊方式比較自由,其程式設計邏輯也比較簡單

但這種通訊形式,不利於叢集管理,如對於聊天室一類的應用程式,需要一個“管理員”,能夠統一管理各個接入聊天室的終端,以便完成類似群發訊息,遮蔽訊息,強制下線等操作

2 C/S模式

C/S(伺服器/客戶機模式):與P2P工作模式不同,C/S對接入網路的終端將分為伺服器客戶機兩種,它們的身份不同,許可權不同,有主有從

伺服器和客戶機指的都是軟體,執行著伺服器軟體的機器就是伺服器,執行客戶機軟體的機器就是客戶機,並不是由硬體決定,也有機器可以同時作為伺服器和客戶機

(1) 伺服器

伺服器是C/S網路裡的核心,如果伺服器沒有執行,或者伺服器出現故障,那麼整個網路就會癱瘓

伺服器的幾個特點:

1,伺服器是必須眾所周知的,伺服器的IP地址和埠號必須公之於眾,否則客戶機找不到伺服器
2,伺服器需要響應客戶機的聯網請求,伺服器需要不斷地偵聽來自客戶機的聯網請求,並將客戶機加入到網路中,才能形成網路
3,伺服器與多個客戶機連線並通訊,對於每一個客戶機,伺服器要用一個獨立的執行緒實現與該客戶機的通訊
4,伺服器負責處理來自客戶機的資訊,包括資訊轉發,資源請求等等
5,伺服器也可以主動和客戶機通訊,實現對客戶機的主動管理,如強制下線某客戶機,通知一個客戶機其它客戶機的狀態等等
6,伺服器一旦終止工作,整個C/S網路服務崩潰

(2) 客戶機

客戶機的幾個特點:
1,客戶機必須知道伺服器的IP地址和埠號,以便向伺服器發出連線請求
2,客戶機必須先連線到伺服器,才能獲得服務
3,客戶機只能從伺服器那裡得知其它客戶機的狀態
4,客戶機之間的通訊,必須由伺服器進行轉發
5,客戶機可以隨時斷開與伺服器的連線,下線的客戶機可以再次與伺服器連線,只要伺服器仍在正常工作

P3 簡單的一對一C/S連線

對於C/S模式,一定有兩套軟體:Server和Client,無論是Server還是Client,都是基於網路通訊,網路通訊基於通訊通道,通訊通道基於端對端的TCP工作協議

配置資訊介面:

package com.mec.net.one2one.core;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public interface INetConfig {
	
	//預設ip地址
	String ip = "127.0.0.1";
	//預設埠號,要避免使用已經被使用過的埠號
	int port = 54188;
	
	//關閉輸入輸出流,關閉連線
	default void close
		(InputStream is, OutputStream os, Socket socket) throws IOException {
		
		if(is != null) {
			is.close();
		}
		is = null;
		
		if(os != null) {
			os.close();
		}
		os = null;
		
		if(socket != null && !socket.isClosed()) {
			socket.close();
		}
		socket = null;
		
	}

}

1 Server類

package com.mec.net.one2one.core;

import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class Server implements INetConfig{
	
	//伺服器埠
	private ServerSocket server;
	//伺服器埠號,建議不要使用10000號前的埠號
	private int port;
	
	//連線需要用到的輸入,輸出流
	private DataInputStream dis;
	private DataOutputStream dos;
	
	
	public Server() {
		this.port = INetConfig.port;
	}

	public void setPort(int port) {
		this.port = port;
	}
	
	public void startServer() throws IOException {
		
		//初始化伺服器連線
		server = new ServerSocket(port);
		
		//偵聽連線請求,建立連線
		//這裡在沒有等到連線請求時,37行後的程式碼會被阻塞
		Socket socket = server.accept();
		
		//從建立好的連線中獲取輸入,輸出流,類似於電話必須需要麥克風和耳機
		//接收者的接收流接收的是傳送者傳送流傳來的訊息
		//即你的耳機輸出的是和你通訊的人的麥克風的輸入
		this.dis = new DataInputStream(socket.getInputStream());
		this.dos = new DataOutputStream(socket.getOutputStream());
		
		//連線成功後,且建立輸入,輸出流後就可以通訊
		String mess = this.dis.readUTF();
		System.out.println("來自客戶端的訊息:" + mess);
		
		this.dos.writeUTF("[" + "成功建立連線!" + "]");
	
		//關閉輸入輸出流,斷開連線
		close(this.dis, this.dos, socket);
		
	}
		

}

2 Client類

package com.mec.net.one2one.core;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;

public class Client implements INetConfig{
	
	//連線埠
	private Socket socket;
	//確定伺服器的ip地址
	private String ip;
	//客戶端連線埠號
	private int port;
	
	//連線需要用到的輸入,輸出流
	private DataInputStream dis;
	private DataOutputStream dos;
	
	public Client() {
		this.ip = INetConfig.ip;
		this.port = INetConfig.port;
	}
	
	public void setIp(String ip) {
		this.ip = ip;
	}

	public void setPort(int port) {
		this.port = port;
	}

	public void startConnect() 
			throws UnknownHostException, IOException {
		
		//發出連線到伺服器的請求,建立連線
		socket = new Socket(ip, port);
		
		//從連線中獲取輸入,輸出流
		this.dis = new DataInputStream(socket.getInputStream());
		this.dos = new DataOutputStream(socket.getOutputStream());
		
		//連線成功後,且建立輸入,輸出流後就可以通訊
		this.dos.writeUTF("hello,server!");
		
		String mess = this.dis.readUTF();
		System.out.println("來自伺服器的訊息" + mess);
		
		//關閉輸入輸出流,斷開連線
		close(this.dis, this.dos, socket);
		
	}
	
}

3 測試,小結

(1) 連線測試

對於Server和Client類,各自都有一個測試類:

TestServer:
在這裡插入圖片描述
TestClient:
在這裡插入圖片描述
啟動時,需要先啟動伺服器,在啟動客戶端,如果先啟動客戶機,那麼將會丟擲找不到伺服器IP地址的異常,拒絕連線,測試結果:

先啟動伺服器:
在這裡插入圖片描述
此時的伺服器在偵聽來自客戶機的連線

啟動客戶機,伺服器的控制檯:
在這裡插入圖片描述
成功建立連線,伺服器收到了來自客戶機的訊息

客戶機的控制檯:
在這裡插入圖片描述
伺服器收到客戶機的訊息後,向客戶機傳送一條訊息

(2) 小結

單對單的C/S連線,很像兩個人打電話時的情況,A向B撥打電話,A是客戶機,B是伺服器,A發起連線,B偵聽到連線後與A通訊 ,這裡的“連線 socket”可以理解為電話線,二者間存在一個連線,這個連線socket內包含了雙方各自的輸入,輸出流,A傳送訊息需要使用輸出流,B接收訊息需要使用輸入流,通訊結束後,需要關閉輸入輸出流,即斷開連線

相關文章