java複習第9天---9.2---網絡編程---綜合案例-模擬文件上傳和模擬BS服務器
目錄
內容
1、模擬文件上傳
1.1、實現原理
文件上傳流程:從客戶端硬盤—>通過本地輸入流—>客戶端內存---->通過網絡傳輸—>服務端內存---->通過本地輸出流—>服務器硬盤
-
步驟:
- 客戶端通過本地字節輸入流,把文件讀入內存
- 客戶端通過獲取的網絡輸出流,把文件內容寫入網絡輸出流
- 服務器通過獲取的網絡輸入流,讀取客戶端傳輸的文件內容
- 服務器通過本地字節碼文件輸出流,把文件數據存儲在本地硬盤中
- 服務器使用網絡輸出流,給客戶端回覆“上傳成功"
- 客戶端使用網絡輸入流讀取服務器回顯的數據
- 關閉流,釋放資源
-
本質:就是從數據源–>目的地的文件複製
1.2、客戶端代碼
-
代碼1.2-1:
package net.tcp; import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; public class UploadClient { public static void main(String[] args) throws IOException { // 1、創建客戶端Socket對象,綁定服務器IP和端口號 Socket client = new Socket("127.0.0.1", 8888); // 2、通過Socket對象的getOutputStream方法獲取網絡輸出流 OutputStream out = (OutputStream) client.getOutputStream(); // 3、通過網絡輸出流向服務器發送數據 // 3.1、從本地文件讀取數據 BufferedInputStream bis = new BufferedInputStream(new FileInputStream("f:\\test\\client\\a.txt")); int len = 0; byte[] b = new byte[1024]; // 4、通過Socket對象的getInputStream獲取網絡輸入流 InputStream is = client.getInputStream(); while((len = bis.read(b)) != -1) { out.write(b, 0, len); } // 解決代碼阻塞 client.shutdownOutput(); // 5、使用網絡輸入流讀取服務器返回的數據 len = is.read(b); // 6、處理返回的數據 System.out.println(new String(b, 0, len)); // 7、關閉Socket對象,釋放資源 client.close(); bis.close(); } } 測試結果: 上傳成功
1.3、服務端代碼
-
代碼1.3-1:
package net.tcp; import java.io.BufferedOutputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class UploadServer { public static void main(String[] args) throws IOException { // 1. 創建ServerSocket服務端套接字,並通過構造方法綁定到指定的端口 ServerSocket server = new ServerSocket(8888); // 2. 調用ServerSocket 的accept方法等待客戶端套接字的連接並獲取套接字 Socket socket = server.accept(); // 3. 調用上一步的得到的Socket對象的getInputStream獲取網絡輸入流 InputStream is = socket.getInputStream(); // 4. 調用網絡輸入流的read方法獲取客戶端傳入的數據 byte[] b = new byte[1024]; int len = 0; BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("f:\\test\\server\\b.txt")); while((len = is.read(b)) != -1) { bos.write(b, 0, len); } // 5. 處理客戶的傳入的數據,生成響應數據 String resp = "上傳完畢"; // 6. 調用Socket對象的getOutputStream獲取網絡輸出流 OutputStream os = socket.getOutputStream(); // 7. 調用網絡輸出流的write方法,把相應數據返回給客戶端 os.write(resp.getBytes()); // 8. 關閉Socket和ServerSocket對象,釋放資源 socket.close(); server.close(); bos.close(); } } 測試結果:自己測試
1.4、代碼阻塞
-
分析:
- read方法爲阻塞方法
- 客戶端讀取本地文件結束,但是並不會把結束標識發送給服務器,導致服務器讀取部分無限死循環,導致程序阻塞
-
解決:
- Socket的shutdownOutput關閉輸出流
- 客戶端傳輸完成後,調用,給服務器傳輸結束標識
1.5、代碼優化
當前服務器只能服務一個客戶端,我們想要服務多個客戶端怎麼辦呢,服務器使用多線程來解決。
-
方法
- 接收客戶端連接放入循環體,一直接收客戶端請求
- 每建立一個連接,就開啓一個線程,完成文件上傳
- 一直重複
-
代碼1.5-1:客戶端同上,下面爲服務器端代碼
package net.tcp; import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; public class UploadClient { public static void main(String[] args) throws IOException { // 1、創建客戶端Socket對象,綁定服務器IP和端口號 Socket client = new Socket("127.0.0.1", 8891); // 2、通過Socket對象的getOutputStream方法獲取網絡輸出流 OutputStream out = (OutputStream) client.getOutputStream(); // 3、通過網絡輸出流向服務器發送數據 // 3.1、從本地文件讀取數據 BufferedInputStream bis = new BufferedInputStream(new FileInputStream("I:\\test\\client\\a.txt")); int len = 0; byte[] b = new byte[1024]; // 4、通過Socket對象的getInputStream獲取網絡輸入流 InputStream is = client.getInputStream(); while((len = bis.read(b)) != -1) { out.write(b, 0, len); } client.shutdownOutput(); // 5、使用網絡輸入流讀取服務器返回的數據 len = is.read(b); // 6、處理返回的數據 System.out.println(new String(b, 0, len)); // 7、關閉Socket對象,釋放資源 client.close(); bis.close(); } }
-
多線程優化:多線程重複創建和銷燬,佔用過多的系統資源,此處用線程池優化
-
代碼1.5-2:客戶端代碼同上,下面五服務端代碼
package net.tcp; import java.io.BufferedOutputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.Random; public class MultiUploadServer { public static void main(String[] args) throws IOException { // 1. 創建ServerSocket服務端套接字,並通過構造方法綁定到指定的端口 ServerSocket server = new ServerSocket(8891); Random r = new Random(); while(true) { // 2. 調用ServerSocket 的accept方法等待客戶端套接字的連接並獲取套接字 Socket socket1 = server.accept(); new Thread(() -> { try { Socket socket = socket1; // 3. 調用上一步的得到的Socket對象的getInputStream獲取網絡輸入流 InputStream is = socket.getInputStream(); // 4. 調用網絡輸入流的read方法獲取客戶端傳入的數據 byte[] b = new byte[1024]; int len = 0; BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("I:\\test\\server\\b" + System.currentTimeMillis()+ r.nextInt(99999) + ".txt")); while((len = is.read(b)) != -1) { bos.write(b, 0, len); } // 5. 處理客戶的傳入的數據,生成響應數據 String resp = "上傳完畢"; // 6. 調用Socket對象的getOutputStream獲取網絡輸出流 OutputStream os = socket.getOutputStream(); // 7. 調用網絡輸出流的write方法,把相應數據返回給客戶端 os.write(resp.getBytes()); // 8. 關閉Socket和ServerSocket對象,釋放資源 socket.close(); bos.close(); }catch(IOException e) { e.printStackTrace(); } }).start(); } } }
2、模擬B/S服務器案例
2.1、B/S通信遵循HTTP協議
&emps;http協議分瀏覽器請求和服務器響應。請求部分由瀏覽器完成,我們暫時不關心,我們關心服務端響應格式
-
響應格式
HTTP/1.1 200 OK // 響應行 Content-Type:text/html // 響應頭 <h4>Hello</h4> // 響應體
-
簡單解析:詳細自行查閱,格式固定
- 響應行:協議/版本 響應碼數字 響應碼文字
- 響應頭:鍵值對形式 key: value1, value2 ,
- 響應體:爲返回內容,一般爲html 經過瀏覽器解析後爲常見的頁面
-
代碼2.1-1:
package net.tcp; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.Random; public class BServer { public static void main(String[] args) throws IOException { // 1. 創建ServerSocket服務端套接字,並通過構造方法綁定到指定的端口 ServerSocket server = new ServerSocket(8891); Random r = new Random(); while(true) { // 2. 調用ServerSocket 的accept方法等待客戶端套接字的連接並獲取套接字 Socket socket1 = server.accept(); new Thread(() -> { try { Socket socket = socket1; // 3. 調用上一步的得到的Socket對象的getInputStream獲取網絡輸入流 InputStream is = socket.getInputStream(); // 4. 調用網絡輸入流的read方法獲取客戶端傳入的數據 String len = null; BufferedReader br = new BufferedReader(new InputStreamReader(is)); len = br.readLine(); System.out.println(len); // 5. 處理客戶的傳入的數據,生成響應數據 String resp = "HTTP/1.1 200 OK\r\n"; resp += "Content-Type:text/html\r\n\r\n"; resp += "<h4>Hello</h4>"; // 6. 調用Socket對象的getOutputStream獲取網絡輸出流 OutputStream os = socket.getOutputStream(); // 7. 調用網絡輸出流的write方法,把相應數據返回給客戶端 os.write(resp.getBytes()); // 8. 關閉Socket和ServerSocket對象,釋放資源 socket.close(); }catch(IOException e) { e.printStackTrace(); } }).start(); } } }
-
測試結果:
- 瀏覽器圖示2.1-1:
後記 :
本項目爲參考某馬視頻開發,相關視頻及配套資料可自行度娘或者聯繫本人。上面爲自己編寫的開發文檔,持續更新。歡迎交流,本人QQ:806797785
前端項目源代碼地址:https://gitee.com/gaogzhen/vue-leyou
後端JAVA源代碼地址:https://gitee.com/gaogzhen/JAVA