1.網絡編程入門
1.1 網絡編程概述【理解】
-
計算機網絡
是指將地理位置不同的具有獨立功能的多臺計算機及其外部設備,通過通信線路連接起來,在網絡操作系統,網絡管理軟件及網絡通信協議的管理和協調下,實現資源共享和信息傳遞的計算機系統
-
網絡編程
在網絡通信協議下,實現網絡互連的不同計算機上運行的程序間可以進行數據交換
1.2 網絡編程三要素【理解】
-
IP地址
要想讓網絡中的計算機能夠互相通信,必須爲每臺計算機指定一個標識號,通過這個標識號來指定要接收數據的計算機和識別發送的計算機,而IP地址就是這個標識號。也就是設備的標識
-
端口
網絡的通信,本質上是兩個應用程序的通信。每臺計算機都有很多的應用程序,那麼在網絡通信時,如何區分這些應用程序呢?如果說IP地址可以唯一標識網絡中的設備,那麼端口號就可以唯一標識設備中的應用程序了。也就是應用程序的標識
-
協議
通過計算機網絡可以使多臺計算機實現連接,位於同一個網絡中的計算機在進行連接和通信時需要遵守一定的規則,這就好比在道路中行駛的汽車一定要遵守交通規則一樣。在計算機網絡中,這些連接和通信的規則被稱爲網絡通信協議,它對數據的傳輸格式、傳輸速率、傳輸步驟等做了統一規定,通信雙方必須同時遵守才能完成數據交換。常見的協議有UDP協議和TCP協議
1.3 IP地址【理解】
IP地址:是網絡中設備的唯一標識
-
IP地址分爲兩大類
-
IPv4:是給每個連接在網絡上的主機分配一個32bit地址。按照TCP/IP規定,IP地址用二進制來表示,每個IP地址長32bit,也就是4個字節。例如一個採用二進制形式的IP地址是“11000000 10101000 00000001 01000010”,這麼長的地址,處理起來也太費勁了。爲了方便使用,IP地址經常被寫成十進制的形式,中間使用符號“.”分隔不同的字節。於是,上面的IP地址可以表示爲“192.168.1.66”。IP地址的這種表示法叫做“點分十進制表示法”,這顯然比1和0容易記憶得多
-
IPv6:由於互聯網的蓬勃發展,IP地址的需求量愈來愈大,但是網絡地址資源有限,使得IP的分配越發緊張。爲了擴大地址空間,通過IPv6重新定義地址空間,採用128位地址長度,每16個字節一組,分成8組十六進制數,這樣就解決了網絡地址資源數量不夠的問題
-
-
DOS常用命令:
-
ipconfig:查看本機IP地址
-
ping IP地址:檢查網絡是否連通
-
-
特殊IP地址:
- 127.0.0.1:是回送地址,可以代表本機地址,一般用來測試使用
1.4InetAddress【應用】
InetAddress:此類表示Internet協議(IP)地址
-
相關方法
方法名 說明 static InetAddress getByName(String host) 確定主機名稱的IP地址。主機名稱可以是機器名稱,也可以是IP地址 String getHostName() 獲取此IP地址的主機名 String getHostAddress() 返回文本顯示中的IP地址字符串 -
代碼演示
public class InetAddressDemo { public static void main(String[] args) throws UnknownHostException { //InetAddress address = InetAddress.getByName("itheima"); InetAddress address = InetAddress.getByName("192.168.1.66"); //public String getHostName():獲取此IP地址的主機名 String name = address.getHostName(); //public String getHostAddress():返回文本顯示中的IP地址字符串 String ip = address.getHostAddress(); System.out.println("主機名:" + name); System.out.println("IP地址:" + ip); } }
1.5端口和協議【理解】
-
端口
- 設備上應用程序的唯一標識
-
端口號
- 用兩個字節表示的整數,它的取值範圍是065535。其中,01023之間的端口號用於一些知名的網絡服務和應用,普通的應用程序需要使用1024以上的端口號。如果端口號被另外一個服務或應用所佔用,會導致當前程序啓動失敗
-
協議
- 計算機網絡中,連接和通信的規則被稱爲網絡通信協議
-
UDP協議
- 用戶數據報協議(User Datagram Protocol)
- UDP是無連接通信協議,即在數據傳輸時,數據的發送端和接收端不建立邏輯連接。簡單來說,當一臺計算機向另外一臺計算機發送數據時,發送端不會確認接收端是否存在,就會發出數據,同樣接收端在收到數據時,也不會向發送端反饋是否收到數據。
- 由於使用UDP協議消耗資源小,通信效率高,所以通常都會用於音頻、視頻和普通數據的傳輸
- 例如視頻會議通常採用UDP協議,因爲這種情況即使偶爾丟失一兩個數據包,也不會對接收結果產生太大影響。但是在使用UDP協議傳送數據時,由於UDP的面向無連接性,不能保證數據的完整性,因此在傳輸重要數據時不建議使用UDP協議
-
TCP協議
-
傳輸控制協議 (Transmission Control Protocol)
-
TCP協議是面向連接的通信協議,即傳輸數據之前,在發送端和接收端建立邏輯連接,然後再傳輸數據,它提供了兩臺計算機之間可靠無差錯的數據傳輸。在TCP連接中必須要明確客戶端與服務器端,由客戶端向服務端發出連接請求,每次連接的創建都需要經過“三次握手”
-
三次握手:TCP協議中,在發送數據的準備階段,客戶端與服務器之間的三次交互,以保證連接的可靠
第一次握手,客戶端向服務器端發出連接請求,等待服務器確認
第二次握手,服務器端向客戶端回送一個響應,通知客戶端收到了連接請求
第三次握手,客戶端再次向服務器端發送確認信息,確認連接
-
完成三次握手,連接建立後,客戶端和服務器就可以開始進行數據傳輸了。由於這種面向連接的特性,TCP協議可以保證傳輸數據的安全,所以應用十分廣泛。例如上傳文件、下載文件、瀏覽網頁等
-
2.UDP通信程序
2.1 UDP發送數據【應用】
-
Java中的UDP通信
- UDP協議是一種不可靠的網絡協議,它在通信的兩端各建立一個Socket對象,但是這兩個Socket只是發送,接收數據的對象,因此對於基於UDP協議的通信雙方而言,沒有所謂的客戶端和服務器的概念
- Java提供了DatagramSocket類作爲基於UDP協議的Socket
-
構造方法
方法名 說明 DatagramSocket() 創建數據報套接字並將其綁定到本機地址上的任何可用端口 DatagramPacket(byte[] buf,int len,InetAddress add,int port) 創建數據包,發送長度爲len的數據包到指定主機的指定端口 -
相關方法
方法名 說明 void send(DatagramPacket p) 發送數據報包 void close() 關閉數據報套接字 void receive(DatagramPacket p) 從此套接字接受數據報包 -
發送數據的步驟
- 創建發送端的Socket對象(DatagramSocket)
- 創建數據,並把數據打包
- 調用DatagramSocket對象的方法發送數據
- 關閉發送端
-
代碼演示
public class SendDemo { public static void main(String[] args) throws IOException { //創建發送端的Socket對象(DatagramSocket) // DatagramSocket() 構造數據報套接字並將其綁定到本地主機上的任何可用端口 DatagramSocket ds = new DatagramSocket(); //創建數據,並把數據打包 //DatagramPacket(byte[] buf, int length, InetAddress address, int port) //構造一個數據包,發送長度爲 length的數據包到指定主機上的指定端口號。 byte[] bys = "hello,udp,我來了".getBytes(); DatagramPacket dp = new DatagramPacket(bys,bys.length,InetAddress.getByName("192.168.1.66"),10086); //調用DatagramSocket對象的方法發送數據 //void send(DatagramPacket p) 從此套接字發送數據報包 ds.send(dp); //關閉發送端 //void close() 關閉此數據報套接字 ds.close(); } }
2.2UDP接收數據【應用】
-
接收數據的步驟
- 創建接收端的Socket對象(DatagramSocket)
- 創建一個數據包,用於接收數據
- 調用DatagramSocket對象的方法接收數據
- 解析數據包,並把數據在控制檯顯示
- 關閉接收端
-
構造方法
方法名 說明 DatagramPacket(byte[] buf, int len) 創建一個DatagramPacket用於接收長度爲len的數據包 -
相關方法
方法名 說明 byte[] getData() 返回數據緩衝區 int getLength() 返回要發送的數據的長度或接收的數據的長度 -
示例代碼
public class ReceiveDemo { public static void main(String[] args) throws IOException { //創建接收端的Socket對象(DatagramSocket) DatagramSocket ds = new DatagramSocket(12345); while (true) { //創建一個數據包,用於接收數據 byte[] bys = new byte[1024]; DatagramPacket dp = new DatagramPacket(bys, bys.length); //調用DatagramSocket對象的方法接收數據 ds.receive(dp); //解析數據包,並把數據在控制檯顯示 System.out.println("數據是:" + new String(dp.getData(), 0, dp.getLength())); } } }
2.3UDP通信程序練習【應用】
-
案例需求
UDP發送數據:數據來自於鍵盤錄入,直到輸入的數據是886,發送數據結束
UDP接收數據:因爲接收端不知道發送端什麼時候停止發送,故採用死循環接收
-
代碼實現
/* UDP發送數據: 數據來自於鍵盤錄入,直到輸入的數據是886,發送數據結束 */ public class SendDemo { public static void main(String[] args) throws IOException { //創建發送端的Socket對象(DatagramSocket) DatagramSocket ds = new DatagramSocket(); //自己封裝鍵盤錄入數據 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String line; while ((line = br.readLine()) != null) { //輸入的數據是886,發送數據結束 if ("886".equals(line)) { break; } //創建數據,並把數據打包 byte[] bys = line.getBytes(); DatagramPacket dp = new DatagramPacket(bys, bys.length, InetAddress.getByName("192.168.1.66"), 12345); //調用DatagramSocket對象的方法發送數據 ds.send(dp); } //關閉發送端 ds.close(); } } /* UDP接收數據: 因爲接收端不知道發送端什麼時候停止發送,故採用死循環接收 */ public class ReceiveDemo { public static void main(String[] args) throws IOException { //創建接收端的Socket對象(DatagramSocket) DatagramSocket ds = new DatagramSocket(12345); while (true) { //創建一個數據包,用於接收數據 byte[] bys = new byte[1024]; DatagramPacket dp = new DatagramPacket(bys, bys.length); //調用DatagramSocket對象的方法接收數據 ds.receive(dp); //解析數據包,並把數據在控制檯顯示 System.out.println("數據是:" + new String(dp.getData(), 0, dp.getLength())); } //關閉接收端 // ds.close(); } }
3.TCP通信程序
3.1TCP發送數據【應用】
-
Java中的TCP通信
- Java對基於TCP協議的的網絡提供了良好的封裝,使用Socket對象來代表兩端的通信端口,並通過Socket產生IO流來進行網絡通信。
- Java爲客戶端提供了Socket類,爲服務器端提供了ServerSocket類
-
構造方法
方法名 說明 Socket(InetAddress address,int port) 創建流套接字並將其連接到指定IP指定端口號 Socket(String host, int port) 創建流套接字並將其連接到指定主機上的指定端口號 -
相關方法
方法名 說明 InputStream getInputStream() 返回此套接字的輸入流 OutputStream getOutputStream() 返回此套接字的輸出流 -
示例代碼
public class ClientDemo { public static void main(String[] args) throws IOException { //創建客戶端的Socket對象(Socket) //Socket(String host, int port) 創建流套接字並將其連接到指定主機上的指定端口號 Socket s = new Socket("192.168.1.66",10000); //獲取輸出流,寫數據 //OutputStream getOutputStream() 返回此套接字的輸出流 OutputStream os = s.getOutputStream(); os.write("hello,tcp,我來了".getBytes()); //釋放資源 s.close(); } }
3.2TCP接收數據【應用】
-
構造方法
方法名 說明 ServletSocket(int port) 創建綁定到指定端口的服務器套接字 -
相關方法
方法名 說明 Socket accept() 監聽要連接到此的套接字並接受它 -
示例代碼
public class ServerDemo { public static void main(String[] args) throws IOException { //創建服務器端的Socket對象(ServerSocket) //ServerSocket(int port) 創建綁定到指定端口的服務器套接字 ServerSocket ss = new ServerSocket(10000); //Socket accept() 偵聽要連接到此套接字並接受它 Socket s = ss.accept(); //獲取輸入流,讀數據,並把數據顯示在控制檯 InputStream is = s.getInputStream(); byte[] bys = new byte[1024]; int len = is.read(bys); String data = new String(bys,0,len); System.out.println("數據是:" + data); //釋放資源 s.close(); ss.close(); } }
3.3TCP通信程序練習【應用】
-
案例需求
客戶端:發送數據,接受服務器反饋
服務器:收到消息後給出反饋
-
案例分析
- 客戶端創建對象,使用輸出流輸出數據
- 服務端創建對象,使用輸入流接受數據
- 服務端使用輸出流給出反饋數據
- 客戶端使用輸入流接受反饋數據
-
代碼實現
public class ServerDemo { public static void main(String[] args) throws IOException { //創建服務器端的Socket對象(ServerSocket) ServerSocket ss = new ServerSocket(10000); //監聽客戶端連接,返回一個Socket對象 Socket s = ss.accept(); //獲取輸入流,讀數據,並把數據顯示在控制檯 InputStream is = s.getInputStream(); byte[] bys = new byte[1024]; int len = is.read(bys); String data = new String(bys, 0, len); System.out.println("服務器:" + data); //給出反饋 OutputStream os = s.getOutputStream(); os.write("數據已經收到".getBytes()); //釋放資源 // s.close(); ss.close(); } } public class ClientDemo { public static void main(String[] args) throws IOException { //創建客戶端的Socket對象(Socket) Socket s = new Socket("192.168.1.66", 10000); //獲取輸出流,寫數據 OutputStream os = s.getOutputStream(); os.write("hello,tcp,我來了".getBytes()); //接收服務器反饋 InputStream is = s.getInputStream(); byte[] bys = new byte[1024]; int len = is.read(bys); String data = new String(bys, 0, len); System.out.println("客戶端:" + data); //釋放資源 // is.close(); // os.close(); s.close(); } }
3.4TCP通信程序練習【應用】
-
案例需求
客戶端:數據來自於鍵盤錄入, 直到輸入的數據是886,發送數據結束
服務端:接收到數據在控制檯輸出
-
案例分析
- 客戶端創建對象,使用鍵盤錄入循環接受數據,接受一行發送一行,直到鍵盤錄入886爲止
- 服務端創建對象,使用輸入流按行循環接受數據,直到接受到null爲止
-
代碼實現
public class ClientDemo { public static void main(String[] args) throws IOException { //創建客戶端Socket對象 Socket s = new Socket("192.168.1.66",10000); //數據來自於鍵盤錄入,直到輸入的數據是886,發送數據結束 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); //封裝輸出流對象 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); String line; while ((line=br.readLine())!=null) { if("886".equals(line)) { break; } //獲取輸出流對象 bw.write(line); bw.newLine(); bw.flush(); } //釋放資源 s.close(); } } public class ServerDemo { public static void main(String[] args) throws IOException { //創建服務器Socket對象 ServerSocket ss = new ServerSocket(10000); //監聽客戶端的連接,返回一個對應的Socket對象 Socket s = ss.accept(); //獲取輸入流 BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); String line; while ((line = br.readLine()) != null) { System.out.println(line); } //釋放資源 ss.close(); } }
3.5TCP通信程序練習【應用】
-
案例需求
客戶端:數據來自於鍵盤錄入,直到輸入的數據是886,發送數據結束
服務端:接受到的數據寫入文本文件中
-
案例分析
- 客戶端創建對象,使用鍵盤錄入循環接受數據,接受一行發送一行,直到鍵盤錄入886爲止
- 服務端創建對象,創建輸出流對象指向文件,每接受一行數據後使用輸出流輸出到文件中,直到接受到null爲止
-
代碼實現
ublic class ClientDemo { public static void main(String[] args) throws IOException { //創建客戶端Socket對象 Socket s = new Socket("192.168.1.66",10000); //數據來自於鍵盤錄入,直到輸入的數據是886,發送數據結束 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); //封裝輸出流對象 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); String line; while ((line=br.readLine())!=null) { if("886".equals(line)) { break; } bw.write(line); bw.newLine(); bw.flush(); } //釋放資源 s.close(); } } public class ServerDemo { public static void main(String[] args) throws IOException { //創建服務器Socket對象 ServerSocket ss = new ServerSocket(10000); //監聽客戶端連接,返回一個對應的Socket對象 Socket s = ss.accept(); //接收數據 BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); //把數據寫入文本文件 BufferedWriter bw = new BufferedWriter(new FileWriter("myNet\\s.txt")); String line; while ((line=br.readLine())!=null) { bw.write(line); bw.newLine(); bw.flush(); } //釋放資源 bw.close(); ss.close(); } }
3.6TCP通信程序練習【應用】
-
案例需求
客戶端:數據來自於文本文件
服務器:接收到的數據寫入文本文件
-
案例分析
- 創建客戶端,創建輸入流對象指向文件,從文件循環讀取數據,每讀取一行就使用輸出流給服務器輸出一行
- 創建服務端,創建輸出流對象指向文件,從客戶端接受數據,每接受一行就給文件中輸出一行
-
代碼實現
public class ClientDemo { public static void main(String[] args) throws IOException { //創建客戶端Socket對象 Socket s = new Socket("192.168.1.66",10000); //封裝文本文件的數據 BufferedReader br = new BufferedReader(new FileReader("myNet\\InetAddressDemo.java")); //封裝輸出流寫數據 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); String line; while ((line=br.readLine())!=null) { bw.write(line); bw.newLine(); bw.flush(); } //釋放資源 br.close(); s.close(); } } public class ServerDemo { public static void main(String[] args) throws IOException { //創建服務器Socket對象 ServerSocket ss = new ServerSocket(10000); //監聽客戶端連接,返回一個對應的Socket對象 Socket s = ss.accept(); //接收數據 BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); //把數據寫入文本文件 BufferedWriter bw = new BufferedWriter(new FileWriter("myNet\\Copy.java")); String line; while ((line=br.readLine())!=null) { bw.write(line); bw.newLine(); bw.flush(); } //釋放資源 bw.close(); ss.close(); } }
3.7TCP通信程序練習【應用】
-
案例需求
客戶端:數據來自於文本文件,接收服務器反饋
服務器:接收到的數據寫入文本文件,給出反饋
-
案例分析
- 創建客戶端對象,創建輸入流對象指向文件,每讀入一行數據就給服務器輸出一行數據,輸出結束後使用shutdownOutput()方法告知服務端傳輸結束
- 創建服務器對象,創建輸出流對象指向文件,每接受一行數據就使用輸出流輸出到文件中,傳輸結束後。使用輸出流給客戶端反饋信息
- 客戶端接受服務端的回饋信息
-
相關方法
方法名 說明 void shutdownInput() 將此套接字的輸入流放置在“流的末尾” void shutdownOutput() 禁止用此套接字的輸出流 -
代碼實現
public class ClientDemo { public static void main(String[] args) throws IOException { //創建客戶端Socket對象 Socket s = new Socket("192.168.1.66",10000); //封裝文本文件的數據 BufferedReader br = new BufferedReader(new FileReader("myNet\\InetAddressDemo.java")); //封裝輸出流寫數據 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); String line; while ((line=br.readLine())!=null) { bw.write(line); bw.newLine(); bw.flush(); } //public void shutdownOutput() s.shutdownOutput(); //接收反饋 BufferedReader brClient = new BufferedReader(new InputStreamReader(s.getInputStream())); String data = brClient.readLine(); //等待讀取數據 System.out.println("服務器的反饋:" + data); //釋放資源 br.close(); s.close(); } } public class ServerDemo { public static void main(String[] args) throws IOException { //創建服務器Socket對象 ServerSocket ss = new ServerSocket(10000); //監聽客戶端連接,返回一個對應的Socket對象 Socket s = ss.accept(); //接收數據 BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); //把數據寫入文本文件 BufferedWriter bw = new BufferedWriter(new FileWriter("myNet\\Copy.java")); String line; while ((line=br.readLine())!=null) { //等待讀取數據 bw.write(line); bw.newLine(); bw.flush(); } //給出反饋 BufferedWriter bwServer = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); bwServer.write("文件上傳成功"); bwServer.newLine(); bwServer.flush(); //釋放資源 bw.close(); ss.close(); } }
3.8TCP通信程序練習【應用】
-
案例需求
客戶端:數據來自於文本文件,接收服務器反饋
服務器:接收到的數據寫入文本文件,給出反饋,代碼用線程進行封裝,爲每一個客戶端開啓一個線程
-
案例分析
- 創建客戶端對象,創建輸入流對象指向文件,每讀入一行數據就給服務器輸出一行數據,輸出結束後使用shutdownOutput()方法告知服務端傳輸結束
- 創建多線程類,在run()方法中讀取客戶端發送的數據,爲了防止文件重名,使用計數器給文件名編號,接受結束後使用輸出流給客戶端發送反饋信息。
- 創建服務端對象,每監聽到一個客戶端則開啓一個新的線程接受數據。
- 客戶端接受服務端的回饋信息
-
代碼實現
public class ClientDemo { public static void main(String[] args) throws IOException { //創建客戶端Socket對象 Socket s = new Socket("192.168.1.66",10000); //封裝文本文件的數據 BufferedReader br = new BufferedReader(new FileReader("myNet\\InetAddressDemo.java")); //封裝輸出流寫數據 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); String line; while ((line=br.readLine())!=null) { bw.write(line); bw.newLine(); bw.flush(); } s.shutdownOutput(); //接收反饋 BufferedReader brClient = new BufferedReader(new InputStreamReader(s.getInputStream())); String data = brClient.readLine(); //等待讀取數據 System.out.println("服務器的反饋:" + data); //釋放資源 br.close(); s.close(); } } public class ServerThread implements Runnable { private Socket s; public ServerThread(Socket s) { this.s = s; } @Override public void run() { try { //接收數據寫到文本文件 BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); //解決名稱衝突問題 int count = 0; File file = new File("myNet\\Copy["+count+"].java"); while (file.exists()) { count++; file = new File("myNet\\Copy["+count+"].java"); } BufferedWriter bw = new BufferedWriter(new FileWriter(file)); String line; while ((line=br.readLine())!=null) { bw.write(line); bw.newLine(); bw.flush(); } //給出反饋 BufferedWriter bwServer = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); bwServer.write("文件上傳成功"); bwServer.newLine(); bwServer.flush(); //釋放資源 s.close(); } catch (IOException e) { e.printStackTrace(); } } } public class ServerDemo { public static void main(String[] args) throws IOException { //創建服務器Socket對象 ServerSocket ss = new ServerSocket(10000); while (true) { //監聽客戶端連接,返回一個對應的Socket對象 Socket s = ss.accept(); //爲每一個客戶端開啓一個線程 new Thread(new ServerThread(s)).start(); } } }