哈哈,準備期末考試去了,項目停了一段時間。現在又忘的差不多了。所以專門寫一篇博客總結前期項目的知識點。
Client軟件包
代碼加總結:
這段代碼實現了一個簡單的客戶端程序,用于與服務器建立連接、發送登錄信息并接收服務器的響應。它展示了如何使用 Java 的 Socket 和對象流來進行網絡通信和對象傳輸,以及如何處理相關的異常情況。
package Client;import common.Message;
import common.MessageType;import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;public class CheckUser {private Socket socket;public boolean login(Message message) {//登錄try {//創建一個新的Socket連接到指定的IP地址和端口號socket = new Socket("127.0.0.1", 10086);//獲取Socket輸出流,并包裝成ObjectOutputStream以便可以發送對象ObjectOutputStream oos=new ObjectOutputStream(socket.getOutputStream());//發送登錄消息到服務器oos.writeObject(message);//獲取Socket的輸入流,并包裝成ObjectInputStream以便可以接收對象ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());//反序列化強轉為String類型,提示信息//從服務器接收對象,并嘗試將其強制轉換為Message類型//這里假設服務器返回的對象是Message類型,并且包含了登錄的結果信息Message message1 = (Message) ois.readObject();//檢查返回的Message對象的MassageType屬性是否為登錄成功//注意:這里應該使用equals進行比較枚舉值if(message1.getMessageType().equals(MessageType.loginsuccefull)){return true;}else return false;} catch (IOException e) {// 如果在Socket通信過程中發生IO異常,捕獲該異常并拋出一個運行時異常// 這樣做通常是為了簡化異常處理,但也可能導致調用者難以處理特定類型的IO異常throw new RuntimeException(e);// 注釋:捕獲了與Socket通信相關的IO異常,并重新包裝為運行時異常拋出} catch (ClassNotFoundException e) {// 如果在嘗試反序列化對象時找不到類的定義,則捕獲此異常// 這通常意味著服務器發送的對象類型在客戶端的類路徑中不存在throw new RuntimeException(e);// 注釋:捕獲了反序列化對象時找不到類的定義的情況,并重新包裝為運行時異常拋出}}public boolean SignIn(Message message) {//注冊try {//創建一個新的Socket連接到指定的IP地址和端口號socket = new Socket("127.0.0.1", 10086);//獲取Socket輸出流,并包裝成ObjectOutputStream以便可以發送對象ObjectOutputStream oos=new ObjectOutputStream(socket.getOutputStream());//發送注冊消息到服務器oos.writeObject(message);//獲取Socket的輸入流,并包裝成ObjectInputStream以便可以接收對象ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());//反序列化強轉為String類型,提示信息//從服務器接收對象,并嘗試將其強制轉換為Message類型//這里假設服務器返回的對象是Message類型,并且包含了注冊的結果信息Message message1 = (Message) ois.readObject();//檢查返回的Message對象的MassageType屬性是否為登錄成功//注意:這里應該使用equals進行比較枚舉值if(message1.getMessageType().equals(MessageType.RegistrationSuccessfully)){return true;}else return false;} catch (IOException e) {// 如果在Socket通信過程中發生IO異常,捕獲該異常并拋出一個運行時異常// 這樣做通常是為了簡化異常處理,但也可能導致調用者難以處理特定類型的IO異常throw new RuntimeException(e);// 注釋:捕獲了與Socket通信相關的IO異常,并重新包裝為運行時異常拋出} catch (ClassNotFoundException e) {// 如果在嘗試反序列化對象時找不到類的定義,則捕獲此異常// 這通常意味著服務器發送的對象類型在客戶端的類路徑中不存在throw new RuntimeException(e);// 注釋:捕獲了反序列化對象時找不到類的定義的情況,并重新包裝為運行時異常拋出}}
}
詳細疏解知識點:
看懂代碼的朋友們就可以不用看這段總結了,這段總結會非常基礎。
1.private 是什么意思?
private是訪問修飾符的一種。
JAVA的權限修飾符_java的訪問修飾符權限-CSDN博客
?2.Socket是什么意思?
- 定義:Socket是對網絡中不同主機上的應用進程之間進行雙向通信的端點的抽象。一個Socket就是網絡上進程通信的一端,提供了應用層進程利用網絡協議交換數據的機制。
- 功能:Socket作為網絡通信的編程接口,允許應用程序將I/O插入到網絡中,并與網絡中的其他應用程序進行通信。它是實現網絡通信的基礎,無論是瀏覽網頁、發送電子郵件,還是流式傳輸視頻,這些操作背后都有Socket在工作。
- 工作原理:一個應用程序通過創建一個Socket,并指定相應的網絡協議(如TCP/IP)、IP地址和端口號等參數,然后通過這個Socket進行通信。通信過程包括建立連接、發送數據、接收數據以及關閉連接等步驟。
3.通信協議與網絡協議
-
通信協議:是指雙方實體完成通信或服務所必須遵循的規則和約定。它主要用于定義發送方和接收方如何發送和接收數據,以及如何處理發生的錯誤。通信協議還負責定義傳輸的數據的格式,以及數據的結構和語義。這些協議常用于點對點通信,特別是在傳統的電話通信領域被廣泛應用。常見的通信協議包括HTTP、SMTP、FTP、SSH、Telnet、IMAP和POP3等。
通信協議分層思想是將網絡通信過程劃分為多個獨立的層次,每個層次負責不同的功能,并通過接口與相鄰層次進行交互。這種分層結構使得網絡通信過程更加模塊化,每一層只關注自己的任務,從而簡化了網絡設計和實現的復雜性。
分層模型示例
1. OSI七層參考模型
OSI(Open Systems Interconnection,開放式系統互聯)模型是國際標準化組織(ISO)在20世紀80年代制定的一種通信協議分層模型,它將網絡通信劃分為七個層次,從上到下依次為:
- 應用層:直接為用戶的應用程序提供服務,如文件傳輸、電子郵件等。
- 表示層:負責數據的編碼、轉換和壓縮,確保數據在不同系統間正確傳輸。
- 會話層:負責建立、管理和終止會話,保證數據在會話中的正確傳輸。
- 傳輸層:提供端到端的可靠數據傳輸服務,如TCP協議。
- 網絡層:負責數據的路由選擇和轉發,如IP協議。
- 數據鏈路層:負責在物理鏈路上無差錯地傳輸數據幀。
- 物理層:定義物理傳輸介質和信號格式,如雙絞線、光纖等。
2. TCP/IP四層模型
TCP/IP(Transmission Control Protocol/Internet Protocol,傳輸控制協議/網際協議)模型是互聯網中廣泛使用的通信協議分層模型,它將網絡通信劃分為四個層次:
- 應用層:與OSI模型的應用層相似,負責用戶應用程序之間的數據通信。
- 傳輸層:提供端到端的數據傳輸服務,包括TCP和UDP兩種協議。
- 網絡層:負責數據的路由選擇和轉發,主要協議為IP。
- 網絡接口層(有時也稱為鏈路層):負責在物理網絡上傳輸數據幀,包含各種硬件協議如以太網等。
- 網絡協議:是指互聯網上各個計算機之間進行數據傳輸和交換所必須共同遵循的規范。它包含了大量的細節和約定,如IP地址、TCP/UDP協議、ARP協議等。網絡協議的設計目標是幫助計算機實現可靠、高效和安全的網絡通信。網絡協議是網絡上所有設備之間通信規則的集合,它規定了通信時信息必須采用的格式和這些格式的意義。
4.TCP/UDP協議
java.net
包中提供了兩種常見的網絡協議的支持:TCP和UDP
TCP(Transmission Control Protocol)和UDP(User Datagram Protocol)是計算機網絡中兩種主要的傳輸層協議,它們各自具有獨特的特點和適用場景。以下是TCP和UDP之間的詳細比較:
連接性
TCP:面向連接的協議。在數據傳輸之前,TCP需要通過三次握手過程建立連接,確保雙方通信的同步性。UDP:無連接的協議。UDP發送方直接將數據包發送給接收方,無需建立連接。
可靠性
TCP:提供可靠的數據傳輸。使用確認和重傳機制來確保數據的完整性,如果數據包在傳輸過程中丟失或損壞,TCP會重新發送。UDP:不提供可靠的數據傳輸。數據包可能丟失、重復或亂序到達,接收方不會發送確認信號,發送方也不會重傳丟失的數據包。
順序性
TCP:保證數據按序到達。每個TCP數據包都有一個序列號,接收方會按照序列號重新排序數據包。UDP:不保證數據順序。數據包可能以任何順序到達接收方。
流量控制和擁塞控制
TCP:提供流量控制和擁塞控制機制。通過滑動窗口協議和擁塞控制算法來調整數據發送速率,以適應網絡的擁塞情況。UDP:不提供流量控制和擁塞控制。發送方以恒定的速率發送數據包,不考慮網絡的擁塞情況。
傳輸速度
TCP:相對較慢。由于連接管理、錯誤檢測和恢復等機制,TCP的傳輸速度受到一定影響。UDP:相對較快。由于無需建立連接和進行確認操作,UDP的傳輸速度通常比TCP更快。
協議開銷
TCP:協議開銷較大。TCP數據包包含更多的頭部信息,用于連接管理、確認和重傳等機制。UDP:協議開銷較小。UDP數據包頭部信息較少,只包含必要的源端口號、目的端口號、長度和校驗和等信息。
?適用場景
TCP:適用于對數據傳輸可靠性要求較高的場景,如文件傳輸、電子郵件、Web瀏覽、遠程登錄等。UDP:適用于對實時性要求較高、可以容忍一定數據丟失的場景,如實時音視頻傳輸、在線視頻會議、實時游戲等。
5.TCP協議使用
服務端:
import java.net.*;
import java.io.*; public class TCPServer { public static void main(String[] args) { try { // 創建服務器端的Socket對象,綁定監聽端口 ServerSocket serverSocket = new ServerSocket(8888); System.out.println("服務器啟動,等待連接..."); // 等待客戶端連接,連接后返回一個Socket對象 Socket socket = serverSocket.accept(); System.out.println("客戶端已連接!"); // 創建輸入流,讀取客戶端發送的數據 InputStream inputStream = socket.getInputStream(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String info = bufferedReader.readLine(); // 讀取一行數據 System.out.println("收到客戶端消息:" + info); // 創建輸出流,向客戶端發送數據 OutputStream outputStream = socket.getOutputStream(); PrintWriter printWriter = new PrintWriter(outputStream, true); printWriter.println("服務器響應:收到消息!"); // 關閉資源 printWriter.close(); bufferedReader.close(); socket.close(); serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } }
}
客戶端:
import java.net.*;
import java.io.*; public class TCPClient { public static void main(String[] args) { try { // 創建客戶端的Socket對象,指定服務器地址和端口號 Socket socket = new Socket("localhost", 8888); // 創建輸出流,向服務器發送數據 OutputStream outputStream = socket.getOutputStream(); PrintWriter printWriter = new PrintWriter(outputStream, true); printWriter.println("你好,服務器!"); // 創建輸入流,讀取服務器發送的數據 InputStream inputStream = socket.getInputStream(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String response = bufferedReader.readLine(); // 讀取一行數據 System.out.println("收到服務器響應:" + response); // 關閉資源 printWriter.close(); bufferedReader.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } }
}
1. 創建連接
- 客戶端:使用
Socket
類的構造方法創建一個Socket
對象,該對象代表與服務器的一個連接。構造方法通常需要服務器的IP地址和端口號作為參數。例如:Socket socket = new Socket("serverIP", port);
- 服務器端:使用
ServerSocket
類的構造方法創建一個ServerSocket
對象,并綁定到一個指定的端口上。然后,調用accept()
方法等待客戶端的連接請求。例如:ServerSocket serverSocket = new ServerSocket(port); Socket socket = serverSocket.accept();
2. 數據傳輸
- 發送數據:在客戶端和服務器端,都可以通過
Socket
對象獲取輸出流(OutputStream
),然后將其包裝成更方便的流類型(如PrintWriter
),用于發送數據。例如:OutputStream outputStream = socket.getOutputStream();PrintWriter printWriter = new PrintWriter(outputStream, true); printWriter.println("Hello, Server!");
- 接收數據:同樣,在客戶端和服務器端,都可以通過
Socket
對象獲取輸入流(InputStream
),然后將其包裝成更方便的流類型(如BufferedReader
),用于接收數據。例如:InputStream inputStream = socket.getInputStream();BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));String line = bufferedReader.readLine();
3. 關閉連接
- 完成數據傳輸后,需要關閉連接以釋放資源。這可以通過調用
Socket
對象的close()
方法來實現。例如:socket.close();
?如果服務器端還綁定了ServerSocket
對象,則還需要關閉該對象。例如:serverSocket.close();
4. 異常處理
- 在進行網絡編程時,經常會遇到各種網絡異常(如
IOException
)。因此,通常需要將網絡操作放在try-catch
塊中,以便捕獲并處理這些異常。
注意事項
- 在進行TCP網絡通信時,需要確保客戶端和服務器端的端口號一致。
- TCP是面向連接的協議,因此在發送和接收數據之前,必須先建立連接。
- TCP協議保證了數據的可靠性和順序性,因此在網絡狀況不佳的情況下,可能會出現數據傳輸延遲或重傳的情況。
- 在實際開發中,還需要考慮多線程或多進程并發處理多個客戶端連接的情況,以及異常處理和資源釋放等問題。
6.序列化和反序列化
序列化:是將數據結構或對象狀態轉換為可以存儲或傳輸的格式的過程。這個格式通常是平臺無關的,以便在不同的上下文中重新創建對象的精確副本。序列化通常用于以下目的:
- 持久化:將對象的狀態保存到磁盤上,以便在程序停止運行后能夠重新加載。
- 網絡傳輸:將對象轉換為可以在網絡上發送的格式。
在序列化過程中,對象的公共和私有字段(以及某些類型的元數據)都會被轉換為字節流,以便存儲或傳輸。
反序列化:是序列化的逆過程,它將序列化的數據(通常是字節流)轉換回對象或數據結構。這個過程通常是在需要重新使用對象或數據結構時進行的,例如在從磁盤加載對象或在網絡上接收對象后。
序列化和反序列化的重要性
- 在不同編程語言或平臺之間共享數據。
- 將對象狀態保存到磁盤,以便在程序重啟后恢復。
- 通過網絡發送和接收對象。
然而,需要注意的是,在序列化對象時要特別小心,因為某些信息(如方法、對象的內部狀態等)可能無法被序列化或反序列化,這可能會導致數據丟失或程序行為異常。
7.ObjectOutputStream
ObjectOutputStream是Java中用于序列化對象的類,它屬于java.io包。
-
定義:ObjectOutputStream類將Java對象的原始數據類型和圖形寫入OutputStream,實現對象的持久化存儲。
-
功能:它允許程序員將Java對象轉換為字節序列,以便可以將這些字節寫入到輸出流中,例如文件或網絡連接。
-
public ObjectOutputStream(OutputStream out)
:創建一個寫入指定OutputStream的ObjectOutputStream類對象。 -
void writeObject(Object obj)
:將指定的對象寫入到ObjectOutputStream中。
序列化條件
要序列化的對象必須實現
java.io.Serializable
接口。如果沒有實現這個接口,將會拋出NotSerializableException
異常。對象的所有屬性(除了被
transient
修飾的)都必須是可序列化的。如果有一個屬性需要參與序列化,但該屬性是瞬態的,那么該屬性必須用transient
關鍵字進行標記。
使用步驟
創建ObjectOutputStream對象:在構造方法中傳遞一個字節輸出流對象(如FileOutputStream)。
寫入對象:使用
writeObject
方法將對象寫入到文件中或通過網絡發送。釋放資源:在完成序列化后,記得關閉ObjectOutputStream和底層的輸出流。
注意事項
- 序列化過程中不會序列化靜態變量和標記為
transient
的變量。- 如果一個對象的類在序列化后發生了變化(例如,添加或刪除了字段),那么在反序列化時可能會拋出
InvalidClassException
。為了避免這種情況,可以為類指定一個serialVersionUID
。- 序列化一個對象時,對象的整個圖(即對象引用的所有其他對象)也會被序列化,除非這些對象被標記為
transient
或它們的類沒有實現Serializable
接口。
8.ObjectInputStream
ObjectInputStream是Java中用于反序列化對象的類,它屬于java.io包。ObjectInputStream用于從輸入流中讀取已經序列化的對象,并將這些對象恢復為原來的狀態。
public ObjectInputStream(InputStream in)
:創建一個從指定InputStream讀取的ObjectInputStream對象。
Object readObject()
:從ObjectInputStream中讀取一個對象。此方法將阻塞,直到對象可用。如果流在讀取對象時到達文件末尾,則返回EOFException。如果流在讀取對象時被關閉,則拋出IOException。
使用條件
- 被反序列化的對象必須實現了
java.io.Serializable
接口。- 如果對象的類在序列化后發生了變化(例如,屬性被添加、刪除或修改),可能會導致反序列化失敗或拋出異常。為了避免這種情況,建議為類指定一個
serialVersionUID
。
使用步驟
- 創建ObjectInputStream對象:在構造方法中傳遞一個字節輸入流對象(如FileInputStream)。
- 讀取對象:使用
readObject
方法從輸入流中讀取對象。注意,由于此方法返回的是Object類型,你可能需要進行類型轉換以獲取具體的類型。- 釋放資源:在完成反序列化后,記得關閉ObjectInputStream和底層的輸入流。
注意事項
- 在使用readObject方法時,需要處理可能拋出的ClassNotFoundException異常,因為反序列化過程中需要加載對象的類定義。
- 如果對象的類在序列化后進行了不兼容的更改(如刪除了一個必要的字段),那么反序列化可能會失敗。在這種情況下,應該更新serialVersionUID以表示類的更改,并確保在反序列化時能夠正確處理這些更改。
- 由于readObject方法可能會阻塞,因此應該在一個單獨的線程中執行它,以避免阻塞主線程。
?
?
common軟件包
代碼加總結:
package common;import java.io.Serializable;public class Message implements Serializable {private static final long serialVersionUID = 1L;private String MessageType;public String getMessageType() {return MessageType;}public void setMessageType(String messageType) {MessageType = messageType;}
}
package common;import java.io.Serializable;public interface MessageType {String buy = "1";String login = "2";String loginsuccefull = "3";String longfail = "4";String SignIn = "5";//注冊String RegistrationSuccessfully = "6";//注冊成功String RegistrationFailed = "7";//注冊失敗
}
詳細疏解知識點:
1.封裝(get/set方法)
封裝是面向對象編程(OOP)中的一個核心概念,它涉及到將數據(屬性)和行為(方法)捆綁在一起形成一個緊密的單元,即“類”。
- 封裝,即隱藏對象的屬性和實現細節,僅對外公開接口,控制在程序中屬性的讀和修改的訪問級別。
- 將抽象得到的數據和行為(或功能)相結合,形成一個有機的整體,也就是將數據與操作數據的源代碼進行有機的結合,形成“類”,其中數據和函數都是類的成員。
封裝的作用
- 保護數據的完整性:通過封裝,可以限制對類內部數據的直接訪問,只允許通過類提供的公共方法進行訪問和修改。這可以防止外部代碼在不適當的時候更改類的內部狀態,從而預防意外或惡意的篡改。
- 減少代碼冗余:封裝通過提供通用方法來減少代碼冗余。設計良好的類可以被多處代碼重用,因此不必在每個需要使用這些行為的地方編寫相同的代碼。這顯著降低了重復的邏輯,并且當需要變更時,只需在一個地方修改。
- 提高軟件的可維護性:封裝提高了軟件的可維護性,因為修改內部實現細節不會影響到使用它的外部代碼。即使在內部邏輯發生較大變化的情況下,只要公共接口保持不變,代碼的其余部分仍然可以正常運作。
- 隱藏實現細節:封裝實現了信息隱藏,這意味著類的用戶無需了解其內部的復雜性,只要知道如何通過公開的方法與之交互即可。這不僅保護了數據,還減少了外部代碼對內部修改的依賴性,從而提高了代碼的靈活性和可維護性。
- 增強安全性:封裝增加了系統的整體安全性。通過隱藏類的內部狀態,限制了外部對這些狀態的直接操作,從而避免了未經授權的訪問和潛在的安全風險。
封裝的實現
- 在編程中,封裝通常通過定義類、使用訪問修飾符(如
public
、private
、protected
)來限制對屬性和方法的訪問級別,以及提供公共的get
和set
方法來實現。get
方法用于讀取類的私有屬性的值,而set
方法用于修改這些屬性的值。
未完待續……