java複習第9天---9.3---網絡編程---綜合案例-模擬文件上傳和模擬BS服務器

java複習第9天---9.2---網絡編程---綜合案例-模擬文件上傳和模擬BS服務器


目錄




內容

1、模擬文件上傳

1.1、實現原理

  文件上傳流程:從客戶端硬盤—>通過本地輸入流—>客戶端內存---->通過網絡傳輸—>服務端內存---->通過本地輸出流—>服務器硬盤

  • 步驟:

    1. 客戶端通過本地字節輸入流,把文件讀入內存
    2. 客戶端通過獲取的網絡輸出流,把文件內容寫入網絡輸出流
    3. 服務器通過獲取的網絡輸入流,讀取客戶端傳輸的文件內容
    4. 服務器通過本地字節碼文件輸出流,把文件數據存儲在本地硬盤中
    5. 服務器使用網絡輸出流,給客戶端回覆“上傳成功"
    6. 客戶端使用網絡輸入流讀取服務器回顯的數據
    7. 關閉流,釋放資源
  • 本質:就是從數據源–>目的地的文件複製

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、代碼阻塞

  • 分析:

    1. read方法爲阻塞方法
    2. 客戶端讀取本地文件結束,但是並不會把結束標識發送給服務器,導致服務器讀取部分無限死循環,導致程序阻塞
  • 解決:

    • 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:[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-ET1BmAOI-1590848623118)(./images/java複習第9天---9.3---網絡編程---綜合案例-B.png)]

後記

本項目爲參考某馬視頻開發,相關視頻及配套資料可自行度娘或者聯繫本人。上面爲自己編寫的開發文檔,持續更新。歡迎交流,本人QQ:806797785

前端項目源代碼地址:https://gitee.com/gaogzhen/vue-leyou
後端JAVA源代碼地址:https://gitee.com/gaogzhen/JAVA
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章