1.網絡程式設計概述
1.1 C/S和B/S
C/S
用戶端——伺服器軟體結構
服務提供商給予使用者服務需要準備的内容(如:本地軟體更新)
B/S
浏覽器——伺服器軟體結構
服務提供商隻要提供資料服務以及前端資料展示方式。
1.2 網絡通信協定
協定
protocol協定
網絡通信協定是要求雙方傳遞資料的計算機必須遵守的,按照對應的網絡傳輸協定,才可以進入資料的互動和傳遞。
常見的協定:
應用層 HTTP、HTTPS、FTP、SMTP、SNMP、DNS
傳輸層 TCP、UDP
網絡層 JCMP、IGMP、IP、ARP、PARP
資料鍊路層 Wi-Fi、GPS、以太網
實體層 以太網、數據機
1.3 UDP和TCP/IP差別
UDP使用者資料報協定
類似于廣播
面向無連接配接,資料傳遞不算特别安全
因為無連接配接,傳輸速度快
因為無連接配接,資料傳遞存在丢包問題
UDP沒有用戶端和伺服器端差別,都可以作為發送端和接收端
UDP協定使用場景:直播、遊戲
TCP/IP傳輸控制協定
面向連接配接,資料傳遞較為安全
因為有連接配接,是以傳遞速度較慢
但資料傳遞有保障
TCP/IP協定是有明确的用戶端和伺服器端概念
TCP/IP協定使用場景:用戶端登陸,資料下載下傳,檔案傳輸
1.4 網絡程式設計的三要素
(1)協定
兩個在有網絡的情況下的計算機資料傳遞,都需要對應的協定來完成
(2)IP位址
Internet Protocol Address
目前計算機在網絡中的一個位址編号,類似于手機号碼
IPv4是一個32位的二進制數,通常展示效果是a.b.c.d 如:192.168.1.1。abcd各代表0~255的數字,目前已使用完。
IPv6是足夠使用。
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位址類對象
代碼示範
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資料傳輸方式
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發送端流程
流程
建立UDP伺服器對應的發送端Socket
準備對應資料包,需要帶有指定資料
發送資料包 send
關閉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("發送端啟動");
// 建立對應的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接收端流程
流程
打開UDP服務,并且監聽指定端口
建立新的空資料包
通過Socket接受資料
關閉UDP服務接收端
代碼示範
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
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.4UDP資料傳遞丢失問題
網絡不夠好,穩定性不行,帶寬不夠
電腦性能不好
以上都會導緻資料傳遞丢失
3.5FeiQ
網絡傳輸都有自己的傳輸規格,如果軟體接收到的資料是自己的規格,那麼可以讀取資料,否則丢棄!!
如FeiQ:
version:time:sender:ip:flag:content
版本:時間:發送者名字:發送人IP:标記:内容
資料是一個String類型
而且使用的協定是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();
//getData方法,将字元串資料轉換為指定格式
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;
建立對應的ServerSocket開啟伺服器,等待用戶端連接配接
(2)用戶端Socket
java.net.Socket;
建立用戶端Socket,并且連接配接伺服器,同時将Socket發送給伺服器綁定注冊。
三次握手機制
4.2 用戶端Socket
給用戶端提供資料傳輸的符合TCP/IP要求的Socket對象
構造方法
Socket(String host, int port);
host:是伺服器IP位址
port:對應伺服器程式的端口号
通過指定的【伺服器IP位址】和【端口号】,擷取TCP連接配接對象
成員方法
InputStream getInputStream();
擷取Socket對象輸入位元組流,可以【從伺服器擷取】對應的資料
InputStream是一個資源,需要在程式退出時關閉
OutputStream getOutputStream();
擷取Socket對象輸出位元組流,可以【發送資料】到伺服器
須在退出時關閉
void close();
關閉用戶端Socket
void shutdownOutput();
禁止目前Socket發送資料
TCP/IP協定對應的Socket是基于IO流實作的。
4.3 服務端Socket(ServerSocket)
構造方法
ServeSocket(int port);
開啟ServeSocket伺服器,并且明确目前服務端口是誰
成員方法
Socket accept();
監聽并且連接配接,得到一個Socket對象,同時該方法是一個阻塞方法,會處于一個始終監聽的狀态
傳回的是Socket,也就是用戶端Socket對象,擷取到目前Socket對象,相當于擷取到用戶端連接配接,同時使用的Socket和用戶端一緻。
4.4 TCP協定示範
4.4.1伺服器流程
流程:
(1)建立ServerSocket伺服器,同時監聽指定端口
(2)通過accept方法擷取Socket連接配接,得到用戶端Socket對象
(3)通過Socket對象,擷取InputStream,讀取用戶端發送資料
(4)通過Socket對象,擷取OutputStream,發送資料給用戶端
(5)關閉服務
代碼示範:
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.4.2用戶端流程
流程:
(1)建立Socket服務,同時明确連接配接伺服器的IP位址和對應端口号
(2)通過Socket對象,擷取對應的OutputStream對象,發送資料給伺服器
(3)通過Socket對象,擷取對應的InputStream對象,接受伺服器發送的資料
(4)關閉該服務
代碼示範
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.5 檔案上傳操作
4.5.1分析過程
4.5.2 用戶端程式
流程:
(1)建立對應檔案的輸入位元組流操作,用緩沖
(2)啟動Socket
(3)擷取Socket輸出OutputStream對象,發送資料給伺服器
(4)邊讀邊發,而不是讀取完再統一發送
(5)檔檔案讀取結束時,發送完畢,關閉用戶端
代碼示範:
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.5.3服務端程式
流程:
(1)開啟服務端服務,建立ServerSocket對象
(2)明确儲存檔案的位置,建立對應檔案夾的輸出緩沖位元組流
(3)讀取資料,寫入檔案
(4)關閉伺服器
代碼示範:
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.5.4 目前服務端代碼問題
(1)儲存的檔案名都是一緻的,無法儲存多個檔案夾。
(2)服務端代碼肯定不能執行完一個上傳功能就結束
(3)服務端代碼不可能隻有一個上傳檔案功能
(4)多線程