# 網路編程 ## 一、概述 網路編程是指編寫運行在多個設備(電腦)的程式,這些設備都通過網路連接起來。 ### 電腦網路 把分佈在不同地理區域的電腦與專門的外部設備用通信線路互連成一個規模大,功能強的網路系統, 從而使眾多的電腦可以方便地互相傳遞信息,共用硬體,軟體,數據信息等資源。 ## ...
網路編程
一、概述
網路編程是指編寫運行在多個設備(電腦)的程式,這些設備都通過網路連接起來。
電腦網路
把分佈在不同地理區域的電腦與專門的外部設備用通信線路互連成一個規模大,功能強的網路系統, 從而使眾多的電腦可以方便地互相傳遞信息,共用硬體,軟體,數據信息等資源。
網路編程目的
-
直接或間接地通過網路協議與其他電腦實現數據交換,
-
進行通訊。
網路編程中主要問題
- 如何準確的定位網路上的一臺或多台主機,定位主機上的特定的應用?
- 找到主機後如何可靠高效地進行數據傳輸?
二、網路通信要素
- ip + 埠號
- 通信協議
IP
-
'網際協議'
-
是 TCP/IP 協議族中最重要的協議之一,也是最重要的互聯網協議之一。這裡的 IP 指 IPV4
-
IP 地址分為五類,主要是前三類
- A類:0~127
- B類:128~191
- C類:192~223
-
ip 地址由 4個十進位數組成,以點隔開
例如:
192.168.10.102
埠號
-
識別電腦上的進程。因為一臺電腦上有很多的軟體,不同的軟體都有不同的進程號。即識別電腦上的軟體
-
範圍:0~65535
-
常用埠號:
-
下述為服務端埠號,範圍:0~1023
-
HTTP DNS HTTPS FTP 80 53 443 21
-
-
客戶端埠號:49152~65535
埠號與IP地址的組合,得出一個網路套接字:Socket,所以說一些網路編程也被稱為Socket編程
通信協議
說協議前需要瞭解網路體繫結構。
網路體繫結構
以前是OSI參考模型,但由於模型過於理想化,未能在網際網路上進行廣泛推廣。現在國際上的標準是 TCP/IP 參考模型。
網路體繫結構:
而網路通信主要在 傳輸層。所以需要瞭解的通信協議也就是:TCP 與 UDP
TCP
- 每一條 TCP 連接都會有兩個埠
- TCP是面向連接的。因為在進行 TCP 通信前需要進行 ”三次握手“ 與。通信結束後需要進行 ”四次揮手“
- TCP協議進行通信的兩個應用進程:客戶端,服務端。
- 在連接中可進行大數據量的傳輸 傳輸完畢,需要釋放已建立的連接,效率低 舉例:打電話
UDP
- 每一條 UDP 連接都會有兩個埠
- 面向無連接的。進行 UDP通信時,源端不需要確認你是否線上,它只管發送。
- 將數據,源,目的封裝成數據包,不需要建立連接 每個數據報的大小限制在64K內 發送方不管對方是否準備好,接收方收到也不確認,故是不可靠的
- 例如:發簡訊。
三、TCP網路編程
1、通過上述可知,網路編程也可為 socket 編程;而 socket 是套接字。即將 埠號拼接到 ip 地址構成的。
2、套接字使用TCP提供了兩台電腦之間的通信機制。 客戶端程式創建一個套接字,並嘗試連接伺服器的套接字。當連接建立時,伺服器會創建一個 Socket 對象。客戶端和伺服器現在可以通過對 Socket 對象的寫入和讀取來進行通信。java.net.Socket 類代表一個套接字,並且 java.net.ServerSocket 類為伺服器程式提供了一種來監聽客戶端,並與他們建立連接的機制。
3、通過如下步驟即可在兩台電腦之間使用套接字建立 TCP 連接:
- 伺服器實例化一個 ServerSocket 對象,表示通過伺服器上的埠通信。
- 伺服器調用 ServerSocket 類的 accept() 方法,該方法將一直等待,直到客戶端連接到伺服器上給定的埠。
- 伺服器正在等待時,一個客戶端實例化一個 Socket 對象,指定伺服器名稱和埠號來請求連接。
- Socket 類的構造函數試圖將客戶端連接到指定的伺服器和埠號。如果通信被建立,則在客戶端創建一個 Socket 對象能夠與伺服器進行通信。
- 在伺服器端,accept() 方法返回伺服器上一個新的 socket 引用,該 socket 連接到客戶端的 socket。
當連接建立後,即可通過 I/O 流進行通信。
- 每一個socket都有一個輸出流和一個輸入流
- 客戶端的輸出流連接到伺服器端的輸入流,而客戶端的輸入流連接到伺服器端的輸出流。
在 java 中提供了一個類用於獲取一個埠,並且偵聽客戶端請求。如下
ServerSocket 類
伺服器應用程式通過使用 java.net.ServerSocket 類以獲取一個埠,並且偵聽客戶端請求。
構造方法如下:
public ServerSocket() //創建非綁定伺服器套接字
public ServerSocket(int port) //創建綁定到特定埠的伺服器套接字。
public ServerSocket(int port, int backlog) //利用指定的 backlog 創建伺服器套接字並將其綁定到指定的本地埠號。
public ServerSocket(int port, int backlog, InetAddress bindAddr) //使用指定的埠、偵聽 backlog 和要綁定到的本地 IP 地址創建伺服器。
常用方法:
public int getLocalPort() //返回此套接字在其上偵聽的埠。
public Socket accept() //偵聽並接受到此套接字的連接。
public void setSoTimeout(int timeout) //通過指定超時值啟用/禁用 SO_TIMEOUT,以毫秒為單位
public void bind(SocketAddress host, int backlog) //將 ServerSocket 綁定到特定地址(IP 地址和埠號)。
Socket 類
java.net.Socket 類代表客戶端和伺服器都用來互相溝通的套接字。客戶端要獲取一個 Socket 對象通過實例化 ,
而 伺服器獲得一個 Socket 對象則通過 accept() 方法的返回值。
構造方法:
public Socket(String host, int port) //創建一個流套接字並將其連接到指定主機上的指定埠號。
public Socket(InetAddress host, int port) //創建一個流套接字並將其連接到指定 IP 地址的指定埠號
public Socket(String host, int port, InetAddress localAddress, int localPort) //創建一個套接字並將其連接到指定遠程主機上的指定遠程埠。
public Socket(InetAddress host, int port, InetAddress localAddress, int localPort) //創建一個套接字並將其連接到指定遠程地址上的指定遠程埠
public Socket() //通過系統預設類型的 SocketImpl 創建未連接套接字
常用方法:
public void connect(SocketAddress host, int timeout) //將此套接字連接到伺服器,並指定一個超時值。
public InetAddress getInetAddress() //返回套接字連接的地址。
public int getPort() //返回此套接字連接到的遠程埠。
public int getLocalPort() //返回此套接字綁定到的本地埠。
public SocketAddress getRemoteSocketAddress() //返回此套接字連接的端點的地址,如果未連接則返回 null。
public InputStream getInputStream() //返回此套接字的輸入流。
public OutputStream getOutputStream() //返回此套接字的輸出流。
public void close() //關閉此套接字。
註意:客戶端和伺服器端都有一個 Socket 對象,所以無論客戶端還是服務端都能夠調用這些方法。
InetAddress 類
這個類表示互聯網協議(IP)地址。類中是沒有構造方法的,所以不能 new 。而可以通過如下方法來進行獲取 ip 地址
static InetAddress getByAddress(byte[] addr) //在給定原始 IP 地址的情況下,返回 InetAddress 對象。
static InetAddress getByAddress(String host, byte[] addr) //根據提供的主機名和 IP 地址創建 InetAddress。
static InetAddress getByName(String host) //在給定主機名的情況下確定主機的 IP 地址。
String getHostAddress() //返回 IP 地址字元串(以文本表現形式)。
String getHostName() //獲取此 IP 地址的主機名。
static InetAddress getLocalHost() //返回本地主機。
String toString() //將此 IP 地址轉換為 String。
案例一:TCP 通信
服務端:
public class TcpServer {
public static void main(String[] args) {
ByteArrayOutputStream bos = null;
InputStream is = null;
Socket socket = null;
ServerSocket serverSocket = null;
try {
//1、設置服務端的地址與埠
serverSocket = new ServerSocket(9923);
//2、監聽該埠的請求
socket = serverSocket.accept();
//3、讀取信息,通過管道流
//獲取輸入流
is = socket.getInputStream();
//獲取位元組緩衝數組流,用於輸出
bos = new ByteArrayOutputStream();
//設置緩衝數組
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1){
bos.write(buffer, 0, len);
}
System.out.println(bos.toString());
} catch (IOException e) {
e.printStackTrace();
}finally {
//4、資源關閉
if (null != bos) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != is) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != socket) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != serverSocket) {
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
客戶端:
public class TcpClient {
public static void main(String[] args) {
OutputStream outputStream = null;
Socket socket = null;
try {
//1、設置ip地址
InetAddress inetAddress = InetAddress.getByName("127.0.0.1");
//2、設置埠
int port = 9923;
//3、創建交流的套子介面
socket = new Socket(inetAddress, port);
//4、進行交流
outputStream = socket.getOutputStream();
outputStream.write("你好,java網路編程".getBytes());
} catch (Exception e) {
e.printStackTrace();
}finally {
//5、進行資源關閉
if (null != outputStream) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != socket) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
案例二:TCP 文件
服務端:
public class TcpFileServer {
public static void main(String[] args) throws Exception {
//1、設置埠
ServerSocket serverSocket = new ServerSocket(9923);
//2、監聽埠
Socket socket = serverSocket.accept();
//3、獲取客戶端發送的數據
InputStream inputStream = socket.getInputStream();
//將數據寫入該路徑下的文件
//FileOutputStream fileOutputStream = new FileOutputStream(new File("reseve.txt"));
FileOutputStream fileOutputStream = new FileOutputStream(new File("/opt/software/reseve.txt"));//虛擬機地址
byte[] buffer = new byte[1024];
int len;
while ((len = inputStream.read(buffer)) != -1){
fileOutputStream.write(buffer, 0, len);
}
//4、通知客戶端傳輸完畢
OutputStream outputStream = socket.getOutputStream();
outputStream.write("已接收完畢".getBytes());
//5、關閉資源
outputStream.close();
fileOutputStream.close();
inputStream.close();
socket.close();
serverSocket.close();
}
}
客戶端:
public class TcpFileClient {
public static void main(String[] args) throws Exception {
//1、獲取埠與地址進行交互
Socket socket = new Socket("192.168.10.102", 9923);
//Socket socket = new Socket("127.0.0.1", 9923);
//2、獲取輸出流,進行輸出文件
OutputStream outputStream = socket.getOutputStream();
//3、讀取文件
FileInputStream fileInputStream = new FileInputStream(new File("20230702_18474242.png"));
//4、進行上傳
byte[] buffer = new byte[1024];
int len;
while ((len = fileInputStream.read(buffer)) != -1){
outputStream.write(buffer, 0, len);
}
//5、進行斷開連接判斷
//通知伺服器,已發送完畢
socket.shutdownOutput();;//已發送完
//獲取輸入流,獲取服務端發送的數據
InputStream inputStream = socket.getInputStream();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] bytes = new byte[1024];
int len2;
while ((len2 = inputStream.read(bytes)) != -1){
bos.write(bytes, 0, len2);
}
System.out.println(bos.toString());
//6、關閉資源
bos.close();
inputStream.close();
fileInputStream.close();
outputStream.close();
socket.close();
}
}
註意:tcp 網路編程時,需要先運行服務端,在運行客戶端
四、UDP網路編程
-
在 UDP 網路編程中,數據是以包發送的,所以需要將數據,目的主機,目的埠封裝在數據包中進行發送。
-
DatagramSocket 和 DatagramPacket 兩個類實現了基於UDP協議的網路程式。
-
UDP 數據報通過數據報套接字 DatagramSocket 發送和接收,系統不保證UDP數據報一定能夠安全送到目的地,也不確定什麼時候可以抵達。
-
DatagramPacket 對象封裝了 UDP 數據報,在數據報中包含了發送端的IP地址和埠號以及接收端的IP地址和埠號。
-
UDP 協議中每個數據報都給出了完整的地址信息,因此無需建立發送方和接收方的連接。如同發快 遞包裹一樣。
案例一:UDP 通信
發送端:
public class UdpSend {
public static void main(String[] args) throws Exception {
//1、創建通信鏈接通道
DatagramSocket datagramSocket = new DatagramSocket();
//2、創建包
String msg = "你好";
InetAddress inetAddress = InetAddress.getByName("127.0.0.1");
int post = 9923;
DatagramPacket datagramPacket = new DatagramPacket(msg.getBytes(), 0, msg.getBytes().length, inetAddress, post);
//3、發送包
datagramSocket.send(datagramPacket);
//4、關閉資源
datagramSocket.close();
}
}
接收端:
public class UdpReceive {
public static void main(String[] args) throws IOException {
//1、創建通信鏈接通道
DatagramSocket datagramSocket = new DatagramSocket(9923);
//2、創建接收數據的包
byte[] buffer = new byte[1024];
DatagramPacket datagramPacket = new DatagramPacket(buffer, buffer.length);
//3、接收
datagramSocket.receive(datagramPacket);
System.out.println(new String(datagramPacket.getData()));//獲取數據
System.out.println(datagramPacket.getPort());//獲取發射器埠
System.out.println(datagramPacket.getSocketAddress());//獲取包中的地址 加 發送器的埠
System.out.println(datagramPacket.getAddress());//獲取包中的地址
//4、關閉資源
datagramSocket.close();
}
}
案例二:UDP 線上聊天
發送端:
public class UdpSend {
public static void main(String[] args) throws Exception {
DatagramSocket socket = new DatagramSocket();
//準備數據
BufferedReader dataPacket = new BufferedReader(new InputStreamReader(System.in));
while (true){
String data = dataPacket.readLine();
byte[] dataBytes = data.getBytes();
DatagramPacket packet = new DatagramPacket(dataBytes, 0, dataBytes.length, new InetSocketAddress("192.168.10.102", 9923));
//發送數據
socket.send(packet);
if (data.equalsIgnoreCase("bye")){
break;
}
}
//關閉資源
socket.close();
}
}
接收端:
public class UdpReceive {
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket(9923);
//接收多次
while (true){
//準備容器接收數據
byte[] container = new byte[1024];
DatagramPacket packet = new DatagramPacket(container, 0, container.length);
//阻塞接收數據
socket.receive(packet);
//斷開連接
byte[] data = packet.getData();
String receiveData = new String(data);
System.out.println(receiveData);
if (receiveData.equalsIgnoreCase("bye")){
break;
}
}
//關閉資源
socket.close();
}
}
案例三:案例二改進為多線程
發送端也是接收端。通過多線程來實現。
發送端:
public class TalkServiceSend implements Runnable{
private DatagramSocket socket = null;
private BufferedReader dataPacket = null;
/**
* 接收者的ip
*/
private String receiveIp;
/**
* 接收者埠
*/
private int receivePort;
public TalkServiceSend(String receiveIp, int receivePort) {
this.receiveIp = receiveIp;
this.receivePort = receivePort;
try {
socket = new DatagramSocket();
dataPacket = new BufferedReader(new InputStreamReader(System.in));
} catch (SocketException e) {
e.printStackTrace();
}
}
@Override
public void run() {
while (true){
try {
String data = dataPacket.readLine();
byte[] dataBytes = data.getBytes();
DatagramPacket packet = new DatagramPacket(dataBytes, 0, dataBytes.length, new InetSocketAddress(this.receiveIp, this.receivePort));
//發送數據
socket.send(packet);
if (data.equalsIgnoreCase("bye")){
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
//關閉資源
socket.close();
}
}
接收端:
public class TalkServiceReceive implements Runnable{
DatagramSocket socket = null;
/**
* 埠
*/
private int port;
/**
* 發送者
*/
private String sender;
public TalkServiceReceive(int port, String sender) {
this.port = port;
this.sender = sender;
try {
socket = new DatagramSocket(this.port);
} catch (SocketException e) {
e.printStackTrace();
}
}
@Override
public void run() {
//接收多次
while (true){
try {
//準備容器接收數據
byte[] container = new byte[1024];
DatagramPacket packet = new DatagramPacket(container, 0, container.length);
//阻塞接收數據
socket.receive(packet);
//斷開連接
byte[] data = packet.getData();
String receiveData = new String(data);
System.out.println(sender + " : " + receiveData);
if (receiveData.equalsIgnoreCase("bye")){
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
//關閉資源
socket.close();
}
}
測試:
public class TalkStudent {
public static void main(String[] args) {
//new Thread(new TalkServiceSend("127.0.0.1", 9923)).start();
new Thread(new TalkServiceSend("10.82.148.136", 9923)).start();//虛擬機地址
new Thread(new TalkServiceReceive(9924, "老師")).start();
}
}
public class TalkTeacher {
public static void main(String[] args) {
new Thread(new TalkServiceSend("192.168.10.102", 9924)).start();
new Thread(new TalkServiceReceive(9923, "學生")).start();
}
}
五、URL編程
- URL (Uniform Resource Locator): 統一資源定位符,它表示 internet 上某一資源的地址。
- 它是一種具體的URI,即URL可以用來標識一個資源,而且還指明瞭如何locate:定位這個資源。
- 通過URL 我們可以訪問Internet上的各種網路資源
- URL 的 基本結構由 5部分組成: 傳輸協議://主機名:埠號/文件名 #片段名?參數列表
- 例如:http://localhost:8080/helloworld/index.jsp#a?username=kuangshen&password=123
舉例:
public class UrlTest {
public static void main(String[] args) throws MalformedURLException {
URL url = new URL("https://www.jd.com/?cu=true&utm_source=www.baidu.com&utm_medium=tuiguang&utm_campaign=t_1003608409_");
System.out.println(url.getProtocol()); //獲取URL的協議名
System.out.println(url.getHost()); //獲取URL的主機名
System.out.println(url.getPort()); //獲取URL的埠號
System.out.println(url.getPath()); //獲取URL的文件路徑
System.out.println(url.getFile()); //獲取URL的文件名
System.out.println(url.getQuery()); //獲取URL的查詢名
}
}
案例一:下載文件
public class UrlTest {
public static void main(String[] args) throws Exception {
//1、定位到伺服器上的資源位置
URL url = new URL("https://lmg.jj20.com/up/allimg/4k/s/02/2109250006343S5-0-lp.jpg");
//2、創建連接
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
//對於程式而言,寫出去----》output/writer;讀取---》input/reader
//3、獲取輸入流
InputStream stream = connection.getInputStream();
//4、寫入文件
FileOutputStream outputStream = new FileOutputStream("山水.jpg");
byte[] bytes = new byte[1024];
int len;
while ((len = stream.read(bytes)) != -1){
outputStream.write(bytes, 0, len);
}
//5、關閉資源
outputStream.close();
stream.close();
connection.disconnect();//斷開連接
}
}