網絡編程
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;
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;
public class SenderDemo1 {
public static void main(String[] args) throws IOException {
System.out.println("發送端啓動");
DatagramSocket socket = new DatagramSocket();
byte[] bytes = "今天中午吃蒸羊羔...".getBytes();
DatagramPacket packet = new DatagramPacket(bytes,
bytes.length,
InetAddress.getLocalHost(),
8848);
socket.send(packet);
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;
public class ReceiveDemo1 {
public static void main(String[] args) throws IOException {
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.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();
}
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;
public class TcpServer1 {
public static void main(String[] args) throws IOException {
System.out.println("服務器啓動");
System.out.println("-----------------------");
ServerSocket serverSocket = new ServerSocket(8848);
Socket socket = serverSocket.accept();
InputStream inputStream = socket.getInputStream();
byte[] buf = new byte[1024];
int length = inputStream.read(buf);
System.out.println(new String(buf, 0, length));
OutputStream outputStream = socket.getOutputStream();
String str = "歡迎來到德萊聯盟";
outputStream.write(str.getBytes());
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;
public class TcpClient1 {
public static void main(String[] args) throws UnknownHostException, IOException {
System.out.println("客戶端啓動");
System.out.println("------------------------");
Socket socket = new Socket("192.168.31.154", 8848);
OutputStream outputStream = socket.getOutputStream();
outputStream.write("你好服務器!!!".getBytes());
InputStream inputStream = socket.getInputStream();
byte[] buf = new byte[1024];
int length = inputStream.read(buf);
System.out.println(new String(buf, 0, length));
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;
public class TcpClient {
public static void main(String[] args) throws IOException {
BufferedInputStream bis = new BufferedInputStream(
new FileInputStream(new File("D:/aaa/1.mp4")));
Socket socket = new Socket(InetAddress.getLocalHost().getHostAddress(), 8848);
OutputStream outputStream = socket.getOutputStream();
int length = -1;
byte[] buf = new byte[1024 * 8];
while ((length = bis.read(buf)) != -1) {
outputStream.write(buf, 0, length);
}
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;
public class TcpServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8848);
Socket socket = serverSocket.accept();
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(
new File("D:/aaa/temp.mp4")));
InputStream inputStream = socket.getInputStream();
int length = -1;
byte[] buf = new byte[1024 * 8];
while ((length = inputStream.read(buf)) != -1) {
bos.write(buf, 0, length);
}
bos.close();
socket.close();
}
}
4.7.4 目前服務端代碼問題
1. 保存的文件名都是一致的,無法保存多個文件。
這裏可以考慮使用UUID作爲文件名
2. 服務端代碼肯定不能執行完一個上傳功能就結束
3. 服務端代碼不可能只有一個上傳文件功能
多線程