網絡編程相關

網絡編程

1. 網絡編程概述

1.1 C/S和B/S
C/S 
	客戶端 服務器軟件結構
	服務提供商給予用戶服務需要準備的內容
		1. 各大平臺的客戶端
			Android iOS PC Windows Linux macOS
			QQ 微信 淘寶 JD 劍與遠征
		2. 服務器提供服務
	軟件更新:
		LOL服務器版本更新,同時本地軟件也要進行更新操作。這個操作非常耗時。
		熱更新
		
B/S
	瀏覽器 服務器軟件結構
	服務提供商只要提供數據服務就OK,以及前端數據展示方式
		1. 瀏覽器提供商非常非常多
			谷歌,火狐,歐朋,Safari,Edge
		2. 服務器提供服務
	軟件更新:
		服務器更新數據,瀏覽器刷新就ok了
1.2 網絡通信協議
協議:
	protocol協議
	網絡通信協議是要求雙方傳遞數據的計算機必須遵守的,按照對應的網絡傳輸協議,纔可以進入數據的交互和傳遞。

目前網絡段數據傳輸比較常見的協議:
	UDP TCP/IP

在這裏插入圖片描述

1.3 UDP和TCP/IP區別
UDP
	1. 面向無連接,數據傳遞不算特別安全
	2. 因爲面向無連接,傳輸速度快
	3. 因爲面向無連接,數據傳遞存在丟包問題
	4. UDP沒有客戶端和服務器區別,都可以作爲發送端和接收端
	UDP協議使用場景
		直播,網絡遊戲

TCP/IP
	1. 面向連接,數據傳遞較爲安全
	2. 因爲面向連接,所有傳遞速度較慢
	3. 面向連接,數據傳遞有保障
	4. TCP/IP協議是有明確的服務器和客戶端概念
	TCP/IP協議使用場景
		客戶端登陸,數據下載,文件傳輸
1.4 網絡編程的三要素
1. 協議
	兩個在於網絡情況下的計算機數據傳遞,都需要對應的協議來完成。

2. IP地址
	Internet Protocol Address
	當前計算機在網絡中的一個地址編號,類似於手機號號碼
IP地址有IPv4協議和IPv6協議
IPv4是一個32位的二進制數,通常展示效果是a.b.c.d 例如 192.168.1.1
	a.b.c.d 各代表0 ~ 255的數字,目前已經消耗殆盡 42億個
IPv6
	IPv6是能夠保證地球上的每一粒沙子都有一個IP地址。
	128位地址長度,16字節一組
	8組 0x0 ~ 0xFFFF

3. 端口號
	端口號是當前應用程序在計算機中的一個編號。可以讓計算機明確知道,當前的數據是給予那一個程序使用,或者數據從哪一個程序出現的。
	端口號是一個short類型 0 ~ 65535
	0~1024不能用於自定義端口號使用,特定的系統端口號

2. IP類

SUN公司提供給開發使用的IP地址類
	InetAddress
常用方法:
	InetAddress getLocalhost();
		獲取本機IP地址類對象
	InetAddress getByName(String str);
		根據指定的主機名獲取對應的IP地址對象
	InetAddress[] getAllByName(String str);
		獲取指定主機名,或者域名對應的所有IP地址類對象
package com.qfedu.a_ip;

import java.net.InetAddress;
import java.net.UnknownHostException;

/*
 * IP類演示
 */
public class Demo1 {
	public static void main(String[] args) throws UnknownHostException {
		InetAddress localHost = InetAddress.getLocalHost();
		System.out.println(localHost);
		
		InetAddress byName = InetAddress.getByName("DESKTOP-M89SDP7");
		System.out.println(byName);
		
		InetAddress byName2 = InetAddress.getByName("www.4399.com");
		System.out.println(byName2);
		
		System.out.println("----------------------------------");
		
		InetAddress[] allByName = InetAddress.getAllByName("www.baidu.com");
		for (InetAddress inetAddress : allByName) {
			System.out.println(inetAddress);
		}
		
		System.out.println("----------------------------------");
		InetAddress[] allByName1 = InetAddress.getAllByName("www.taobao.com");
		for (InetAddress inetAddress : allByName1) {
			System.out.println(inetAddress);
		}
		
		System.out.println("----------------------------------");
		InetAddress[] allByName2 = InetAddress.getAllByName("www.jd.com");
		for (InetAddress inetAddress : allByName2) {
			System.out.println(inetAddress);
		}
		
	}
}

3. UDP協議數據傳輸

3.1 UDP數據傳輸方式
User Datagram Protocol 
	數據傳遞採用數據包方式傳遞,所有的數據要進行打包操作,並且沒有對應的客戶端服務器概念,有且只有發送段和接收端
	
Socket 套接字
	數據需要進行傳遞操作,在數據傳遞的兩臺計算機當中必須有對應的Socket。這裏採用UDP協議,那麼必須有一個UDP協議的Socket
	DatagramSocket();
		創建一個發送端UDP協議Socket對象
	DatagramSocket(int port);
		創建一個接收端UDP協議的Socket對象,這裏需要【監聽】指定端口
		
發送端數據包的打包方法:
	DatagramPacket DatagramPacket(byte[] buf, int length, InetAddress address, int port);
	buf: 需要傳遞數據的字節數組
	length:是當前字節數組中數據容量字節數
	address:接收端IP地址對象
	port: 接收端對應的端口號

接收端數據包接收方式
	這裏需要準備一個空的數據包
	DatagramPacket DatagramPacket(byte[] buf, int length);
	buf: 字節緩衝數組,通常是1024整數倍
	length: 當前字節緩衝數組的容量
3.2 發送端
流程:
	1. 創建UDP服務器對應的發送端Socket
	2. 準備對應數據包,需要帶有指定數據
	3. 發送數據 send
	4. 關閉UDP發送端
package com.qfedu.b_udp;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/*
流程:
	1. 創建UDP服務器對應的發送端Socket
	2. 準備對應數據包,需要帶有指定數據
	3. 發送數據 send
	4. 關閉UDP發送端
 */
public class SenderDemo1 {
	public static void main(String[] args) throws IOException {
		System.out.println("發送端啓動");
		// 創建對應的Socket
		DatagramSocket socket = new DatagramSocket();
		
		// 準備數據包
		byte[] bytes = "今天中午吃蒸羊羔...".getBytes();
		DatagramPacket packet = new DatagramPacket(bytes,  // 字節數組數據
				bytes.length,  // 字節數組數據長度
				InetAddress.getLocalHost(),  // 指定接收端IP地址
				8848); // 8848對應端口號
		
		// 發送數據包
		socket.send(packet);
		
		// 關閉UDP發送端
		socket.close();
	}
}	
3.3 接收端
流程:
	1. 打開UDP服務,並且監聽指定端口
	2. 創建新的空數據包
	3. 通過Socket接收數據 
	4. 關閉UDP服務接收端
package com.qfedu.b_udp;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

/*
流程:
	1. 打開UDP服務,並且監聽指定端口
	2. 創建新的空數據包
	3. 通過Socket接收數據 receive
	4. 關閉UDP服務接收端
 */
public class ReceiveDemo1 {
	public static void main(String[] args) throws IOException {
		// 創建Socket監聽端口
		DatagramSocket socket = new DatagramSocket(8848);
		
		// 準備空數據包
		byte[] buf = new byte[1024];
		DatagramPacket packet = new DatagramPacket(buf, buf.length);
		
		// 接收數據
		socket.receive(packet);
		
		// 確定接收到的字節長度
		int length = packet.getLength();
		
		System.out.println(new String(buf, 0, length));
		
		// 關閉socket
		socket.close();
		
	}
}
3.4 UDP數據傳遞丟失問題
1. 網絡不夠好,穩定性不行,帶寬不夠
2. 電腦性能不好
3.5 FeiQ
網絡傳輸都有自己的傳輸規格,如果軟件接受到的數據是自己的規格,那麼可以讀取數據
如果不是,丟棄!!!
FeiQ
    version:time:sender:ip:flag:content
    版本:時間:發送者名字:發送人IP:標記:內容
    數據是一個String類型

    而且使用的協議是UDP協議
package com.qfedu.b_udp;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class FeiQ {
	public static void main(String[] args) throws IOException {
		DatagramSocket socket = new DatagramSocket();
		
		String data = getData("Welcome to Java World!!!");
		DatagramPacket datagramPacket = new DatagramPacket(data.getBytes(), data.getBytes().length
				, InetAddress.getByName("192.168.31.255"), 2425);
		
		socket.send(datagramPacket);
		
		socket.close();
		
	}
	
	/**
	 * 傳入數據,轉換成FeiQ可以識別的數據
	 * version:time:sender:ip:flag:content
	 * 
	 * @param message 字符串類型內容
	 * @return 符合FeiQ格式要求的字符串
	 */
	public static String getData(String message) {
		StringBuilder stb = new StringBuilder();
		
		stb.append("1.0:");
		stb.append(System.currentTimeMillis() + ":");
		stb.append("Anonymous:");
		stb.append("10.1.1.1:");
		stb.append("32:");
		stb.append(message);
		
		return stb.toString();
	} 
}

4. TCP

4.1 TCP概述
	TCP相對於UDP比較穩定的傳輸協議,這裏存在三次握手,保證連接狀態,同時有明確的客戶端和服務端之分
	TCP服務中需要服務器端先啓動,需要監聽指定端口,等待客戶端連接。
	客戶端主動連接服務器,和服務器連接之後,纔可以進行數據交互,服務器不能主動連接客戶端的。

TCP操作而言,Java中提供了兩個Socket
	1. 服務端Socket 
		java.net.ServerSocket;
		創建對應的ServerScoket開啓服務器,等待客戶端連接
	2. 客戶端Socket
		java.net.Socket
		創建客戶端Scoket,並且連接服務器,同時將Socket發送給服務器綁定註冊。

在這裏插入圖片描述

4.2 Socket 客戶端Socket
給客戶端提供數據傳輸的符合TCP/IP要求的Socket對象

構造方法 Constructor
	Socket(String host, int port);
		host是服務器IP地址,port對應服務器程序的端口號
		通過指定的服務器IP地址和端口號,獲取TCP連接對象

成員方法 Method	
	InputStream getInputStream();
		獲取Socket對象輸入字節流,可以從服務器獲取對應的數據
		InputStream是一個資源,需要在程序退出是關閉
		Read
	
	OutputStream getOutputStream();
		獲取Sokcet對象輸出字節流,可以發送數據到服務器
		OutputStream是一個資源,需要在程序退出是關閉
		Write
	
	void close();
		關閉客戶端Socket
	
	void shutdownOutput();
		禁止當前Socket發送數據

TCP/IP協議對應的Socket是給予IO流實現的。
4.3 ServerSocket服務端Socket
在服務端開啓Socket服務器
構造方法 Constructor:
	ServerSocket(int port);
		開啓ServerSocket服務器,並且明確當前服務端口是誰

成員方法 Method:
	Socket accept();
		監聽並且連接,得到一個Socket對象,同時該方法是一個阻塞方法,會處於一個始終
		的監聽狀態
		返回的是Socket,也就是客戶端Socket對象,獲取到當前Socket對象,相對於獲取到
		客戶端連接,同時使用的Socket和客戶端一致。
4.6 TCP協議代碼演示

在這裏插入圖片描述

4.6.1 服務器代碼
流程:
	1. 創建ServerSocket服務器,同時監聽指定端口
	2. 通過accept方法獲取Socket連接,得到客戶端Socket對象
	3. 通過Socket對象,獲取InputStream,讀取客戶端發送數據
	4. 通過Socket對象,獲取OutputStream,發送數據給客戶端
	5. 關閉服務
package com.qfedu.c_tcp;

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

/*
流程:
	1. 創建ServerSocket服務器,同時監聽指定端口
	2. 通過accept方法獲取Socket連接,得到客戶端Socket對象
	3. 通過Socket對象,獲取InputStream,讀取客戶端發送數據
	4. 通過Socket對象,獲取OutputStream,發送數據給客戶端
	5. 關閉服務 
 */
public class TcpServer1 {
	public static void main(String[] args) throws IOException {
		System.out.println("服務器啓動");
		System.out.println("-----------------------");
		// 1. 創建ServerSocket服務器,同時監聽指定端口
		ServerSocket serverSocket = new ServerSocket(8848);
	
		// 2. 通過accept方法獲取Socket連接,得到客戶端Socket對象
		Socket socket = serverSocket.accept();
		
		// 3. 通過Socket對象,獲取InputStream,讀取客戶端發送數據
		InputStream inputStream = socket.getInputStream();
		
		// IO流操作
		byte[] buf = new byte[1024];
		int length = inputStream.read(buf);
		System.out.println(new String(buf, 0, length));
        
		// 4. 通過Socket對象,獲取OutputStream,發送數據給客戶端
		OutputStream outputStream = socket.getOutputStream();
		String str = "歡迎來到德萊聯盟";
		
		outputStream.write(str.getBytes());
		
		// 5. 關閉Socket服務 同時關閉當前Socket使用的輸入字節流和輸出字節流	
		// Closing this socket will also close the socket's InputStream and OutputStream. 
		socket.close();	
	}
}
4.6.2 客戶端代碼
流程:
	1. 創建Socket服務,同時明確連接服務器的IP地址和對應端口號
	2. 通過Socket對象,獲取對應的OutputStream對象,發送數據給服務器
	3. 通過Socket對象,獲取對應的InputStream對象,接收服務器發送數據
	4. 關閉服務
package com.qfedu.c_tcp;

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

/*
流程:
	1. 創建Socket服務,同時明確連接服務器的IP地址和對應端口號
	2. 通過Socket對象,獲取對應的OutputStream對象,發送數據給服務器
	3. 通過Socket對象,獲取對應的InputStream對象,接收服務器發送數據
	4. 關閉服務
 */
public class TcpClient1 {
	public static void main(String[] args) throws UnknownHostException, IOException {
		System.out.println("客戶端啓動");
		System.out.println("------------------------");
		// 1. 創建Socket服務,同時明確連接服務器的IP地址和對應端口號
		Socket socket = new Socket("192.168.31.154", 8848);
		
		// 2. 通過Socket對象,獲取對應的OutputStream對象,發送數據給服務器
		OutputStream outputStream = socket.getOutputStream();
		
		outputStream.write("你好服務器!!!".getBytes());
		
		// 3. 通過Socket對象,獲取對應的InputStream對象,接收服務器發送數據
		InputStream inputStream = socket.getInputStream();
		
		byte[] buf = new byte[1024];
		int length = inputStream.read(buf);
		System.out.println(new String(buf, 0, length));
		
		// 4. 關閉服務
		socket.close();
	}
}

4.7 文件上傳操作

4.7.1 分析過程

在這裏插入圖片描述

4.7.2 客戶端程序
流程:
	1. 創建對應文件的輸入字節流操作,這裏可以使用緩衝
	2. 啓動Socket,
	3. 獲取Socket輸出OutputStream對象,發送數據給服務器
	4. 邊讀邊發
	5. 當文件讀取結束,發送完畢,關閉客戶端
package com.qfedu.d_fileupload;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;

/*
流程:
	1. 創建對應文件的輸入字節流操作,這裏可以使用緩衝
	2. 啓動Socket,
	3. 獲取Socket輸出OutputStream對象,發送數據給服務器
	4. 邊讀邊發
	5. 當文件讀取結束,發送完畢,關閉客戶端
 */
public class TcpClient {
	public static void main(String[] args) throws IOException {
		// 1. 創建對應文件的輸入字節流操作,這裏可以使用緩衝
		BufferedInputStream bis = new BufferedInputStream(
				new FileInputStream(new File("D:/aaa/1.mp4")));
		
		// 2. 啓動Socket
		Socket socket = new Socket(InetAddress.getLocalHost().getHostAddress(), 8848);
		
		// 3. 獲取Socket輸出OutputStream對象,發送數據給服務器
		OutputStream outputStream = socket.getOutputStream();
		
		int length = -1;
		byte[] buf = new byte[1024 * 8];
		
		
		// 4. 讀取數據,發送數據
		while ((length = bis.read(buf)) != -1) {
			outputStream.write(buf, 0, length);
		}
		
		// 5. 關閉資源
		socket.close();
		bis.close();
	}
}
4.7.3 服務端程序
流程:
	1. 開啓服務端服務,創建ServerSocket對象
	2. 明確保存文件的位置,創建對應文件夾的輸出緩衝字節流
	3. 讀取數據,寫入文件
	4. 關閉服務器
package com.qfedu.d_fileupload;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

/*
流程:
	1. 開啓服務端服務,創建ServerSocket對象
	2. 明確保存文件的位置,創建對應文件夾的輸出緩衝字節流
	3. 讀取數據,寫入文件
	4. 關閉服務器
 */
public class TcpServer {
	public static void main(String[] args) throws IOException {
		// 1. 開啓服務端服務,創建ServerSocket對象
		ServerSocket serverSocket = new ServerSocket(8848);
		
		Socket socket = serverSocket.accept();
		
		// 2. 明確保存文件的位置,創建對應文件夾的輸出緩衝字節流
		BufferedOutputStream bos = new BufferedOutputStream(
				new FileOutputStream(
						new File("D:/aaa/temp.mp4")));
		
		// 3. 獲取Socket對應的輸入流
		InputStream inputStream = socket.getInputStream();
		
		// 4. 邊讀邊寫
		int length = -1;
		byte[] buf = new byte[1024 * 8];
		
		while ((length = inputStream.read(buf)) != -1) {
			bos.write(buf, 0, length);
		}
		
		// 5. 關閉資源
		bos.close();
		socket.close();
	}
}
4.7.4 目前服務端代碼問題
1. 保存的文件名都是一致的,無法保存多個文件。
	這裏可以考慮使用UUID作爲文件名
2. 服務端代碼肯定不能執行完一個上傳功能就結束

3. 服務端代碼不可能只有一個上傳文件功能
	多線程
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章