目錄
那什么是Socket?
什么是ServerSocket?
代碼展示:
代碼解析:
補充:
輸入流(InputStream):
輸出流(OutputStream):
`BufferedReader` 是如何提高讀取效率的?
`BufferedWritter` 是如何提高讀取效率的?
在Java中實現客戶端與服務器端之間的連接通常可以通過Socket和ServerSocket類來實現。
那什么是Socket?
- Socket(套接字)是在網絡編程中使用的一種抽象概念,用于建立不同計算機之間的通信連接。Socket允許不同計算機上的應用程序通過網絡進行數據交換。
- 在Java編程中,Socket類是對套接字的抽象表示,提供了用于網絡通信的接口。通過Socket類,可以實現客戶端和服務器之間的通信,包括數據的發送和接收。
- 在一個通信過程中,客戶端和服務器端各自創建一個Socket對象,并通過這個Socket對象來進行通信。客戶端通過Socket連接服務器端的地址和端口號,然后可以通過Socket對象發送數據到服務器端;服務器端接收到客戶端的請求后,也會創建一個新的Socket對象與客戶端建立連接,從而進行數據交換。
- Socket對象在使用完畢后需要進行關閉操作,以釋放資源和終止通信連接。在Java編程中,通常會在try-with-resources語句中使用Socket對象,以確保在通信結束時Socket對象能夠被正確關閉。
- 總的來說,Socket類在網絡編程中扮演著重要的角色,它提供了一種簡單而有效的方式來實現計算機之間的通信和數據交換。
?
什么是ServerSocket?
- ServerSocket是Java編程語言中的一個類,用于在服務器端創建一個服務器套接字,監聽客戶端的連接請求。通過ServerSocket,服務器端可以接收來自客戶端的連接,并與客戶端建立通信連接。
- ServerSocket類提供了創建服務器套接字、監聽端口、接受客戶端連接請求等功能。通過ServerSocket的accept()方法,服務器端可以等待客戶端的連接請求,并一旦有客戶端請求連接,accept()方法將返回一個Socket對象,服務器端可以通過這個Socket對象與客戶端進行通信。
- 在一個典型的網絡應用程序中,服務器端通常會使用ServerSocket來初始化并監聽一個特定的端口,等待客戶端連接,然后處理客戶端請求并進行相應的回復。ServerSocket類被廣泛應用于Java網絡編程中,用于實現服務器端的監聽與響應。
- 需要注意的是,ServerSocket通常用在服務器端,用于監聽客戶端連接請求;而連接到服務器的客戶端將會使用普通的Socket對象進行通信。通過ServerSocket和Socket相互配合,可以實現服務器端與多個客戶端之間的連接通信。
- 總的來說,ServerSocket是Java中用于創建服務器套接字、接收客戶端連接請求的重要類,是實現基于TCP協議的服務器端網絡通信的關鍵組件。
代碼展示:
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
//【服務端】
public class AIzzServer {public static HashMap<String,String> map=new HashMap<>();static {map.put("你好", "你好呀,孫子");map.put("hi", "hello,孫子");map.put("hello", "hi,孫子");map.put("吃了嗎", "沒呢,孫子");map.put("很高興認識你", "我也是哦");}public static void main(String[] args) {try(ServerSocket serverSocket=new ServerSocket(8848)){while(true) {Socket clientSocket=serverSocket.accept();String clientIp=clientSocket.getInetAddress().getHostAddress();//輸入流:讀取客戶端發送的”問題“//輸出流:發送問題的答案給客戶端try(BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));BufferedWriter writer=new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()))){//讀取來自客戶端的”問題“String question=reader.readLine();if(question==null||question.length()==0){continue;}System.out.printf("來自客戶端[%s]的問題:%s\n",clientIp,question);String answer=map.get(question);answer=answer==null?"對不起,我不知道你在說什么!":answer;//將問題的答案輸出至”客戶端“writer.write(answer);}}} catch (IOException e) {throw new RuntimeException(e);}}
}
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;//【客戶端】
public class AIzzClient {public static void main(String[] args) {try(Scanner input=new Scanner(System.in)){//讀取控制臺輸入的問題String question=input.nextLine();//創建Socket,輸出流,輸入流try(Socket clientSocket=new Socket(InetAddress.getLocalHost(),8848);BufferedReader reader=new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));BufferedWriter writer=new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));){//向服務端發送”問題“(輸出至服務端)writer.write(question);writer.flush();//暫時關閉輸出流clientSocket.shutdownOutput();//接收服務端返回的“答案”String ans=reader.readLine();System.out.println("來自服務端的回答:"+ans);} catch (UnknownHostException e) {throw new RuntimeException(e);} catch (IOException e) {throw new RuntimeException(e);}}}
}
代碼解析:
????????上述代碼展示了一個基于 TCP 協議的簡單人工對打程序,包括服務端 `AIzzServer` 和客戶端 `AIzzClient` 兩個部分。
服務端 `AIzzServer` 主要負責:
- 1. 創建一個監聽端口為 `8848` 的 `ServerSocket` ,等待客戶端連接。
- 2. 當有客戶端連接成功時,獲取客戶端的 IP 地址。
- 3. 通過輸入流讀取客戶端發送的問題,并在預先定義的 `HashMap` 中查找對應的答案。如果問題不存在于 `HashMap` 中,則返回默認的回答“對不起,我不知道你在說什么!”。
- 4. 將找到的答案通過輸出流發送回客戶端。
客戶端 `AIzzClient` 主要負責:
- 1. 從控制臺讀取用戶輸入的問題。
- 2. 創建與本地主機 `8848` 端口的連接。
- 3. 通過輸出流向服務端發送問題。
- 4. 發送完成后關閉輸出流。? ?
????????通過輸入流接收服務端返回的答案,并打印輸出。 兩者通過 TCP 協議進行通信,客戶端發送問題,服務端接收并處理后返回答案,形成了一個簡單的交互流程。例如,當用戶在客戶端輸入“你好”,客戶端將這個問題發送給服務端,服務端從 `HashMap` 中找到對應的“你好呀,孫子”并返回給客戶端,客戶端接收并顯示出來。
補充:
輸入流(InputStream):
????????輸入流用于從數據源讀取數據到程序中。數據源可以是文件、網絡連接、內存緩沖區等。
它提供了一系列方法,允許程序按順序讀取數據的一部分或全部。常見的輸入流操作包括讀取一個字節、讀取一組字節、讀取一個字符、讀取一行文本等。
????????例如,FileInputStream
?可以從文件中讀取字節數據,BufferedReader
?可以更高效地從字符輸入流中讀取文本行。
輸出流(OutputStream):
????????輸出流則用于將程序中的數據寫入到數據目的地。目的地同樣可以是文件、網絡連接、內存緩沖區等。
輸出流提供了方法來將數據按順序寫入,例如寫入一個字節、寫入一組字節、寫入一個字符等。
????????比如,FileOutputStream
?用于向文件中寫入字節數據,PrintWriter
?常用于向輸出流中寫入格式化的文本。
總的來說,輸入流和輸出流是 Java 中用于實現數據在程序與外部數據源或目的地之間傳輸的重要機制。通過合理使用不同類型的輸入流和輸出流,可以方便地處理各種數據的讀取和寫入操作。
`BufferedReader` 是如何提高讀取效率的?
????????`BufferedReader` 通過內部的緩沖區來提高讀取效率。 當使用普通的輸入流(如 `FileReader` )讀取數據時,每次讀取操作可能都會導致與底層數據源(例如文件)的直接交互,這可能會帶來較高的系統開銷,特別是在頻繁讀取小量數據的情況下。
????????而 `BufferedReader` 在其內部維護了一個緩沖區(通常是一個字節數組)。當調用讀取方法時,它首先嘗試從緩沖區中獲取數據。如果緩沖區中有足夠的數據,就直接返回,避免了頻繁地與底層數據源進行交互。 當緩沖區中的數據不足時,`BufferedReader` 會一次性從底層輸入流中讀取較大塊的數據填充緩沖區,而不是每次只讀取一個字節或幾個字節。
???????? 例如,如果緩沖區大小設置為 8192 字節,那么 `BufferedReader` 可能會一次性從底層輸入流讀取 8192 字節的數據放入緩沖區,后續的讀取操作只要從緩沖區獲取即可,大大減少了與底層輸入流交互的次數,從而顯著提高了讀取效率。 這種緩沖機制特別適用于按行讀取文本文件的情況,因為按行讀取時,可能需要多次讀取少量的數據,`BufferedReader` 的緩沖作用能有效地優化這種操作。
`BufferedWritter` 是如何提高讀取效率的?
????????`BufferedWriter` 主要通過內部的緩沖區來提高寫入效率。 當使用普通的字符輸出流(如 `FileWriter`)進行寫入操作時,每次寫入一個字符或一小段數據,都可能會引發與底層輸出目的地(例如文件)的實際交互,這會帶來較多的系統開銷。
????????而 `BufferedWriter` 在其內部維護了一個緩沖區(通常是一個字符數組)。當調用寫入方法(如 `write`)時,數據并不是直接寫入到目的地,而是先被存儲到緩沖區中。 緩沖區有一定的大小,當緩沖區被填滿或者手動調用 `flush` 方法時,`BufferedWriter` 會一次性地將緩沖區中的數據寫入到底層輸出流中。這樣就減少了與底層輸出目的地交互的次數,從而顯著提高了寫入效率。
????????例如,在向文件寫入大量數據時,如果不使用 `BufferedWriter`,可能需要頻繁地進行磁盤 I/O 操作,而使用 `BufferedWriter` 后,只需在緩沖區滿或主動刷新時才進行實際的磁盤寫入,大大降低了磁盤 I/O 的次數。 一般情況下,使用默認大小的緩沖區即可滿足大多數需求。但如果明確知道寫入數據的特征,也可以在創建 `BufferedWriter` 對象時指定緩沖區的大小,
? ??