1. 網路基本概念
1.1 計算機網路
定義:是指將地理位置不同的具有獨立功能(沒有網路可以獨立存在的)的多臺計算機及其外部設備,通過通信線路連接起來,在網路作業系統,網路管理軟體及網路通信協議的管理和協調下,實作資源共享和資訊傳遞的計算機系統
- 主干:計算機網路是計算機系統
- 網路功能:資源共享、資訊傳遞
- 網路組成
- 網路硬體:計算機、外部設備、通信連接
- 網路軟體:網路作業系統、網路管理軟體、網路通信協議
分類–按照規模:
- 局域網 LAN
- 城域網 MAN
- 廣域網 WAN
分類–按照傳輸介質:
-
同軸電纜網路(類似于有線電視網的電纜)
-
雙絞線網路
-
光纖網路(傳輸的為光信號)
-
衛星網路

分類–拓撲結構:
-
星形網路(最常使用)
-
總線網路:信號在傳遞程序中都能收到,辨別是自己的則接收
-
環狀網路:同樣的傳遞方式

1.2 網路通信協議
在網路中實作通信,必須要有一些約定(通信協議),對速率、傳輸代碼、傳輸控制步驟等指定標準(好比交通規則)
問題:網路通信涉及內容很多:源地址、目標地址、加密解密、流量控制、路由控制等,如何實作如此復雜的網路協議?
===》分層:將復雜的成份分解成一些簡單的成份,再將它們復合起來(同層間可以通信、上一層可以呼叫下一層,而不與再下一層發生關系)
網路通信協議分層:
- 名義上的標準:ISO --> OSI 參考模型
- 事實上的標準:TCP/IP協議堆疊
資料的封裝與拆封:在傳輸程序中,經過每一層,都需要添加各種資料,最終發送到另一端,另一端再拆解這些資料

1.3 TCP/IP協議堆疊
網路通信最常采用的協議
- 網路層主要協議:IP協議
- 傳輸層主要協議:TCP 和 UDP 協議
1.4 TCP協議
面向連接的、可靠的、基于位元組流的傳輸層通信協議(打電話的案例)
- 面向連接(一段資訊分段后發送,發送順序和接收順序一致)
- 點到點的通信
- 高可靠性:三次握手
- 占用系統資源多、效率低
應用案列:HTTP、FTP、Telnet、SMTP

1.5 UDP協議
無連接的傳輸層協議,提供面向事務的簡單不可靠資訊傳送服務(發電報、發送群發短信)
- 非面向連接,傳輸不可靠,可能丟失(一段資訊分段后發送,不一定哪一段先到達)
- 發送不管對方是否準備好,接收方收到后也不回復
- 可以廣播發送
- 非常簡單的協議,開銷少
應用案例:DNS、SNMP
1.6 IP地址和埠
IP地址,用來標志網路中的一個通信物體(計算機、路由器)的地址
分類:
- IPV4:32位地址,點分十進制表示,如192.168.0.1
- IPV6:128位寫個8個16位的無符號整數,每個整數用4個十六進制位標識,數之間用 : 分割
特殊的 IP 地址:
- 127.0.0.1:本機地址
- 192.168.0.0 – 192.168.255.255 私有地址,專門為組織機構內部使用
埠(port):
- IP地址用來標志一臺計算機,但一個計算機可以提供多種應用程式,使用埠來區分應用程式
- 范圍:0 – 65535(16位整數)
埠分類:
- 0 – 1023 :公認埠(比如 80給了 WWW,21給了 FTP等)
- 1024 – 49151:注冊埠(分配給用戶或應用程式)
- 49152 – 65535:動態/私有埠
IP和埠API:
- InetAddress 類:封裝計算機的 ip地址,沒有埠
- InetSocketAddress:包含埠,用于 socket 通信
1.7 URL 統一資源定位符
Uniform Resource Locator:由 4部分組成:協議、存放資源的主機域名、埠號、資源檔案名

1.8 Socket 套接字
Socket實際是傳輸層供給應用層的編程介面

類似于寄信:用戶(應用層)將信(資料)投入郵筒即可(郵筒的口,就是socket),進入Socket之后,怎么送信就是郵局、公路交管(傳輸層、網路層)等的事,
2. 網路編程常用類
2.1 封裝IP地址 – InetAddress
// 1.獲取 IP地址
InetAddress ia = InetAddress.getLocalHost(); // 本機的 ip
// 2.操作 IP地址
System.out.println(ia); // DESKTOP-F31QQ1H/192.168.0.102
System.out.println(ia.getHostAddress()); // 192.168.0.102
System.out.println(ia.getHostName()); // DESKTOP-F31QQ1H
InetAddress ia2 = InetAddress.getByName("www.lwclick.com"); // 通過域名獲取ip
System.out.println(ia2);
2.2 封裝 IP 和 Port – InetSocketAddress
// 創建一個 InetSocketAddress 物件
InetSocketAddress isa = new InetSocketAddress("www.lwclick.com", 8888);
// 獲取物件內容
System.out.println(isa); // www.lwclick.com/104.21.41.202:8888
System.out.println(isa.getAddress()); // www.lwclick.com/104.21.41.202
System.out.println(isa.getPort()); // 8888
2.3 URL類
// 創建一個 URL 協議:https 域名/IP地址:lwclick.com 埠:80 路徑:/categories/MySQL/
URL url = new URL("https://lwclick.com:80/categories/MySQL/");
// 獲取 URL 各個組成部分
System.out.println(url.getProtocol()); // https
System.out.println(url.getHost()); // lwclick.com
System.out.println(url.getPort()); // 80
System.out.println(url.getDefaultPort()); // 443 默認的 https 埠
System.out.println(url.getPath()); // /categories/MySQL/
3. TCP編程
3.1 一次單向通信
-
服務器端:
-
創建 ServerSocket,在指定埠監聽(
accept()方法)并處理請求(如果客戶端請求到來,回傳對應的Socket,否則的話一直等待,執行緒也被阻塞)
-
-
客戶端:
- 創建 Socket,需要指定服務器的 ip 和埠號,向服務器發送和接收回應
-
發送資料:
- 需要使用輸出流(OutputStream),可以通過 DataOutputStream 和 ObjectOutputStream 進行包裝,提高效率
-
接收資料:
- 使用輸入流(InputStream),使用 DataInputStream 和 ObjectInputStream 進行包裝
服務器端:
public class LoginServer {
public static void main(String[] args) throws IOException {
// 1.創建一個 ServerSocket,配置監聽埠
ServerSocket serverSocket = new ServerSocket(8080);
// 2.使用 ServerSocket 在指定埠監聽
Socket socket = serverSocket.accept(); // 請求不到,在此阻塞; 請求到了,回傳一個socket,繼續執行
// 3.接收客戶端的請求資料,輸出結果
InputStream is = socket.getInputStream(); // 獲取流
DataInputStream dis = new DataInputStream(is); // 同樣使用資料流進行包裝
String info = dis.readUTF(); // 讀取對應型別的寫入的資料
System.out.println("客戶端的請求:" + info);
// 4.關閉資源
dis.close();
serverSocket.close();
}
}
客戶端:
public class LoginClient {
public static void main(String[] args) throws IOException {
// 1.創建一個 Socket,指明服務器端ip和埠
Socket socket = new Socket(InetAddress.getLocalHost(), 8080); // InetAddress.getByName()獲取ip
// 2.發送資料給服務器端
OutputStream os = socket.getOutputStream(); // 資訊通過流發送,輸出流
DataOutputStream dos = new DataOutputStream(os); // 資料流進行包裝
dos.writeUTF("userName=lwclick&pwd=123");
// 3.關閉資源
dos.close(); // 關閉高層流,低層自動關閉
}
}
注意:測驗時,服務器端需要先啟動,然后再啟動客戶端
3.2 一次雙向通信
服務器端:
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8080);
Socket socket = serverSocket.accept();
InputStream is = socket.getInputStream();
DataInputStream dis = new DataInputStream(is);
String info = dis.readUTF();
System.out.println("客戶端的請求:" + info);
// ============================== 向客戶端發送資料 =====================================
// 4. 給客戶端一個回應
OutputStream os = socket.getOutputStream(); // 此處的socket為接收的客戶端的回應
DataOutputStream dos = new DataOutputStream(os);
dos.writeUTF("登錄成功,歡迎!");
// 5.關閉資源
dos.close();
dis.close();
serverSocket.close();
}
客戶端:
public static void main(String[] args) throws IOException {
Socket socket = new Socket(InetAddress.getL ocalHost(), 8080);
OutputStream os = socket.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
dos.writeUTF("userName=lwclick&pwd=123");
// ============================== 接收服務器端反饋 =====================================
// 3.接收服務器端回應,并輸出
InputStream is = socket.getInputStream();
DataInputStream dis = new DataInputStream(is);
String info = dis.readUTF();
System.out.println("服務器端的回應:" + info);
// 4.關閉資源
dis.close();
dos.close();
}
3.3 傳輸物件
User 類:在網路上傳輸,類一定要實作序列化介面
public class User implements Serializable {
private String userId;
private String password;
// getter / setter / toString / constructor
}
服務器端:
public static void main(String[] args) throws IOException, ClassNotFoundException {
ServerSocket serverSocket = new ServerSocket(8080);
Socket socket = serverSocket.accept();
InputStream is = socket.getInputStream();
// =============== 此處使用【物件流】接收資料 ===================
ObjectInputStream ois = new ObjectInputStream(is);
User user = (User)ois.readObject();
System.out.println("客戶端的請求:" + user);
OutputStream os = socket.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
if (user.getUserId().indexOf("lwclick") >= 0 && user.getPassword().length() > 6) {
dos.writeUTF("登錄成功,歡迎!");
} else {
dos.writeUTF("登錄失敗,請重試!");
}
dos.close();
ois.close();
serverSocket.close();
}
客戶端:
public static void main(String[] args) throws IOException {
Socket socket = new Socket(InetAddress.getLocalHost(), 8080);
// 獲取用戶輸入
Scanner sc = new Scanner(System.in);
System.out.print("userId: ");
String userId = sc.next();
System.out.print("password: ");
String password = sc.next();
User user = new User(userId, password);
OutputStream os = socket.getOutputStream();
// 【物件流】進行包裝
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(user);
InputStream is = socket.getInputStream();
DataInputStream dis = new DataInputStream(is);
String info = dis.readUTF();
System.out.println("服務器端的回應:" + info);
dis.close();
oos.close();
}
3.4 引入多執行緒
將服務器端接到請求后的處理步驟,放到執行緒的 run()方法 中,每過來一個請求,就創建一個執行緒去執行
執行緒類:
public class LoginThread extends Thread {
private Socket socket;
public LoginThread() {
}
public LoginThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
DataOutputStream dos = null;
ObjectInputStream ois = null;
try {
InputStream is = socket.getInputStream();
ois = new ObjectInputStream(is);
User user = (User)ois.readObject();
System.out.println("客戶端的請求:" + user);
OutputStream os = socket.getOutputStream();
dos = new DataOutputStream(os);
if (user.getUserId().indexOf("lwclick") >= 0 && user.getPassword().length() > 6) {
dos.writeUTF("登錄成功,歡迎!");
} else {
dos.writeUTF("登錄失敗,請重試!");
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
if (dos != null) {
dos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (ois != null) {
ois.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
服務器端: 為每一個登錄請求,創建一個執行緒來處理
public static void main(String[] args) throws IOException, ClassNotFoundException {
ServerSocket serverSocket = new ServerSocket(8080);
int i = 1;
while (true) {
Socket socket = serverSocket.accept();
// 為每一個登錄請求,創建一個執行緒來處理
new LoginThread(socket).start();
// 統計客戶端的IP地址和總的請求次數
InetAddress ia = socket.getInetAddress();
System.out.println("這是第" + (i++) + "個請求,對方的IP地址是:" + ia.getHostAddress());
}
}
客戶端:(無需改變)
public static void main(String[] args) throws IOException {
Socket socket = new Socket(InetAddress.getLocalHost(), 8080);
// 獲取用戶輸入
Scanner sc = new Scanner(System.in);
System.out.print("userId: ");
String userId = sc.next();
System.out.print("password: ");
String password = sc.next();
User user = new User(userId, password);
OutputStream os = socket.getOutputStream();
// 【物件流】進行包裝
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(user);
InputStream is = socket.getInputStream();
DataInputStream dis = new DataInputStream(is);
String info = dis.readUTF();
System.out.println("服務器端的回應:" + info);
dis.close();
oos.close();
}
4. UDP編程
無連接的,客戶與咨詢師的在線交流
- 使用基于 UDP 協議的 Socket 網路編程實作
- 不需要使用 IO 流實作資料的傳輸
- 每個資料發送單元被統一封裝成資料包(ip,介面,資料等)的方式,發送方將資料發到網路上,資料包在網路上尋找它要去的目的地
需要使用的類:
- DatagramSocket:用于發送或接收資料包
- DatagramPacket:資料包
4.1 一次單向通信
客戶端:
public static void main(String[] args) throws IOException {
// 1.創建一個 Socket,用來發送和接收資料包
DatagramSocket socket = new DatagramSocket(9999); // 客戶端監聽的介面
// 2.使用 socket 發送一個資料包
String str = "親,在嗎";
byte[] buf = str.getBytes();
InetAddress ia = InetAddress.getLocalHost();
int port = 8888; // 服務器端接收資料的埠號
DatagramPacket packet = new DatagramPacket(buf, buf.length, ia, port);
// 發送資料包
socket.send(packet);
// 3.關閉 socket
socket.close();
}
服務器端:
public static void main(String[] args) throws IOException {
// 1.創建一個 Socket,用來發送和接收資料包
DatagramSocket socket = new DatagramSocket(8888); // 服務器端監聽的介面
// 2.使用 socket 接收一個資料包
byte[] buf = new byte[128];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet); // ip,port等資訊
System.out.println(new String(packet.getData(), 0, packet.getLength()));
System.out.println(packet.getAddress());
System.out.println(packet.getPort());
// 3.關閉 socket
socket.close();
}
4.2 多次雙向通信
客戶端:
public static void main(String[] args) throws IOException {
// 1.創建一個 Socket,用來發送和接收資料包
DatagramSocket socket = new DatagramSocket(9999); // 客戶端監聽的介面
Scanner sc = new Scanner(System.in);
while (true) {
String line = sc.nextLine();
// 2.使用 socket 發送一個資料包
byte[] buf = line.getBytes();
InetAddress ia = InetAddress.getLocalHost();
int port = 8888; // 服務器端接收資料的埠號
DatagramPacket packet = new DatagramPacket(buf, buf.length, ia, port);
// 發送資料包
socket.send(packet);
// 如果客戶端輸入 bye,結束對話
if ("bye".equals(line)) {
break;
}
// 接收服務器端回傳的訊息
byte[] bytes = new byte[128];
DatagramPacket packetReceive = new DatagramPacket(bytes, bytes.length);
socket.receive(packetReceive);
System.out.println(new String(packetReceive.getData(), 0, packetReceive.getLength()));
}
// 3.關閉 socket
socket.close();
}
服務器端:
public static void main(String[] args) throws IOException {
// 1.創建一個 Socket,用來發送和接收資料包
DatagramSocket socket = new DatagramSocket(8888); // 服務器端監聽的介面
Scanner sc = new Scanner(System.in);
while (true) {
// 2.使用 socket 接收一個資料包
byte[] buf = new byte[128];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet); // ip,port等資訊
String info = new String(packet.getData(), 0, packet.getLength());
System.out.println(info);
if ("bye".equals(info)) {
break;
}
// 使用 socket 給客戶端發送一個資料包
String str = sc.nextLine();
byte[] bytes = str.getBytes();
InetAddress address = packet.getAddress(); // 發送資料的客戶端地址
int port = packet.getPort(); // 埠號
DatagramPacket sendPacket = new DatagramPacket(bytes, bytes.length, address, port);
socket.send(sendPacket);
}
// 3.關閉 socket
socket.close();
}
5. 檔案上傳
使用 TCP編程 實作檔案上傳功能
- 思路:進行兩次檔案的復制
- 【客戶端】將檔案從【本地】復制到【網路】
- 【服務端】將檔案從【網路】復制到【本地】

客戶端:
public class UploadClient {
public static void main(String[] args) throws IOException {
// 創建一個 socket,指明服務器端ip 和監聽埠
Socket socket = new Socket(InetAddress.getLocalHost(), 8800);
// ======================== 上傳檔案到服務端的目的埠 ===========================
// 本機的源檔案
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("e:/readme.txt"));
// 將檔案寫到目的服務器埠的位置
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
byte[] buf = new byte[1024];
int len = bis.read(buf);
while (len != -1) {
bos.write(buf, 0, len);
len = bis.read(buf);
}
bos.close();
bis.close();
}
}
服務器端:
public class UploadServer {
public static void main(String[] args) throws IOException {
// 創建一個 ServerSocket
ServerSocket serverSocket = new ServerSocket(8800);
// 使用 ServerSocket 在指定埠監聽
Socket socket = serverSocket.accept();
// ===================== 從目的埠取檔案 ========================
// 從目的埠取內容
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
// 保存到服務器的本地
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("e:/readme2.txt"));
byte[] buf = new byte[1024];
int len = bis.read(buf);
while (len != -1) {
bos.write(buf, 0, len);
len = bis.read(buf);
}
bos.close();
bis.close();
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/301250.html
標籤:其他
