軟體項目實訓及課程設計指導——如何正確地創建和銷毀軟體應用系統中網絡通訊中的Socket對象實例
1、基於TCP/IP協議的Socket通信相關的基礎知識
(1)TCP/IP(Transmission Control Protocol傳輸控制協議/Internet Protocol網間協議)
TCP/IP是目前Internet網絡中的主要協議,它定義了計算機和外設進行通信所使用的規則;TCP/IP網絡參考模型包括五個層次:應用層、傳輸層、網絡層、鏈路層、物理層。而ISO/OSI網絡參考模型則包括七個層次:應用層、表示層、會話層、傳輸層、網絡層、鏈路層、物理層。
(2)網絡通訊中數據傳輸的模式
面向連接(TCP)——比如生活中的打電話
無連接(UDP,User Datagram Protocol)——比如發郵件、簡訊等
(3)埠號
在網絡通訊中應用埠號達到區分服務的類別(該服務也就是伺服器端相關程序所提供的功能),從而允許伺服器端程序在同一時間內能夠進行多個不同的網絡會話,因為不同的網絡會話都有對應的服務埠號,彼此相互區分。
如:WWW服務的埠號為80,telnet服務的埠號為21,ftp服務的埠號為23,smtp服務的埠號為25,pop3服務的埠號為110等。
在程序中應用埠號時需要注意埠號的範圍,埠號其實是一個16位無符號整數,數值範圍是0-65535;低於256的埠號保留給標準的應用程式使用,比如pop3的埠號就是110。
2、Java語言中提供基於TCP/IP協議的Socket通信的技術支持
(1)了解什麼是Socket(套接字)
網絡中雙向通訊程序中的某一端稱為一個Socket,一個程序將一段信息寫入Socket中,該Socket將這段信息發送給另外一個Socket中,使這段信息能傳送到其他程序中。
(2)Java提供客戶端套接字Socket類和伺服器端套接字ServerSocket類
在JDK系統API庫java.net包中提供有客戶端套接字Socket類和伺服器端套接字ServerSocket類,軟體應用系統的開發人員可以應用在網絡通信的客戶端程序中構建出Socket類的對象實例,也就是當客戶端程序需要與伺服器端程序進行通訊時,客戶端程序需要在客戶機程序中創建一個Socket對象,Socket類有多種不同形式的構造方法。
在JDK API中與Socket通信有關的各個接口和功能類都定義在java.net包中,而在對應的伺服器端程序中則需要創建出ServerSocket類的對象實例。
在Java程序中構建Socket類的對象實例需要提供伺服器端主機的IP位址以及伺服器端相關程序所提供的對外服務的埠號,從而使得不同的客戶端程序應用IP位址可以區分不同的伺服器主機,同時再通過埠號進一步區分在同一伺服器主機中可能提供不同的功能服務。
(3)基於Socket通訊的主要過程
通訊的主要過程包括Socket的建立、監聽、連接、發送數據和接收數據等環節。
3、Socket通訊實現中客戶/伺服器端通訊的主要方式及工作原理
(1)客戶/伺服器端通訊的主要方式
在網絡通訊中的客戶端和伺服器端之間的通訊方式主要有如下的兩種方式:
「一對一」,也就是一個伺服器對應一個客戶機,此種通訊方式由於通訊的效率低下,目前已經不再應用;
「一對多」,也就是一個伺服器對應多個連接的客戶機。為此,在伺服器相關的程序中一般都要應用多線程的技術才能使得伺服器程序可以同時響應不同的客戶端程序的請求——也就是需要採用一個伺服器端程序進程但構建出多個不同的線程。
(2)客戶/伺服器端通訊的工作原理如下示圖所示
4、正確地應用Socket類的構造方法創建出Socket類的對象實例
如下示圖為JDK API幫助文檔中所提供的Socket類的各種構造方法的定義,但在Socket類中兩個常用的構造方法分別是 Socket(InetAddress addr, int port) 和 Socket(String host, int port),它們都能夠創建出一個基於Socket的連接伺服器端流的套接字對象。
在程序中可以通過構造方法Socket(InetAddress addr, int port)內的第1個參數InetAddress類對象實例addr獲得伺服器端主機的IP位址,而在程序中通過構造方法Socket(String host, int port)中的第1個字符串類型參數host可以獲得代表伺服器端主機的名稱字符串。
這兩個構造方法都聲明有拋出UnknownHostException異常類對象。下面為構造Socket類對象實例的代碼片段示例:
try {
Socket oneSocketObject =new Socket(「伺服器IP位址」,」埠號」);
}
catch(UnknownHostException e){
}
catch (Ioexception e){
}
5、如何獲得伺服器端主機及客戶端主機的IP位址
由於Socket類對象實例可以出現在網絡通訊中的客戶端程序和伺服器端程序中,因此在客戶端和伺服器端不同的程序中通過調用Socket類對象實例中的同一個方法但獲得的結果卻是不同的,讀者在程序中應用時需要加以區分。
(1)在客戶端程序代碼中通過調用Socket類對象實例中的getInetAddress()方法可以獲得遠程伺服器端主機的IP位址,而通過調用Socket類對象實例中的getPort()可以獲得遠程伺服器端主機的埠號。
(2)在伺服器端程序代碼中通過調用Socket類對象實例中的getInetAddress()方法可以獲得遠程客戶端主機的IP位址。
(3)在伺服器端程序代碼中通過調用Socket類中的getLocalAddress()可以獲得伺服器端本機的IP位址,而通過調用Socket類中的getLocalPort()可以獲得伺服器端本機的埠號。
(4)在客戶端程序代碼中通過調用Socket類中的getLocalAddress()可以獲得客戶端本機的IP位址,而通過調用Socket類中的getLocalPort()可以獲得客戶端本機的埠號(此埠號其實就是連接到伺服器的埠號)。
6、獲得伺服器的主機名和對應的IP位址的程序代碼示例
(1)依據伺服器的主機名獲得伺服器的IP位址
InetAddress類存儲遠程系統的IP位址,如下代碼片段示例是根據所提供的伺服器端主機名稱字符串"WWW-XUMIF9WPKIP "(可以為其它主機名稱字符串或者包括本機"localhost"字符串等)獲得對應的IP位址。
try{
InetAddress serverInetAddress =
InetAddress.getByName("WWW-XUMIF9WPKIP ");
System.out.println("伺服器主機IP位址為:"+
serverInetAddress.toString());
}
catch (UnknownHostException e){
}
對其中的serverInetAddress.toString()的調用代碼也可以改用如下的語句System.out.println("伺服器主機IP位址為:"+ serverInetAddress.getHostAddress());顯示出伺服器主機IP位址。程序代碼執行的結果參見如下示圖所示。
(2)依據伺服器主機的IP位址獲得伺服器的主機名
在InetAddress類中提供有static 靜態類型的public static InetAddress getByAddress(byte[] addr) throws UnknownHostException方法,該方法根據給定伺服器主機IP 地址(由4位元組的數組參數表示)返回 InetAddress 類型的對象實例。
如下代碼示例是假定伺服器主機的IP位址為192.168.0.1,然後再依據該IP位址獲得對應的主機名稱字符串的代碼片段示例。
try{
byte[] serverHostIPByteArray = new byte[] { (byte) 192, (byte) 168, 0, 1 };
InetAddress serverInetAddress =
InetAddress.getByAddress(serverHostIPByteArray);
System.out.println("伺服器主機名稱為:"+
serverInetAddress.getHostName ());
}
catch (UnknownHostException e){
}
7、如何正確地關閉Socket類的對象實例以及時釋放所佔的系統資源
當構造出Socket類的對象實例時,Java虛擬機JVM系統同樣也需要為它分配一定的系統資源。因此,在軟體應用系統程序中如果不再需要Socket類的對象實例時,應該及時地將它關閉掉以釋放所佔用的系統資源。
關閉Socket類的對象實例分為主動關閉(Active closure)和被動關閉(Passive closure)兩種情況。前者是指由本地客戶端主機主動發起的關閉行為;而後者則是指本地主機檢測到遠程伺服器主機發起的關閉行為之後,相應地也作出對應關閉的回應,從而關閉整個通訊中的連接。
下面為關閉Socket類對象實例的代碼片段示例,並且要將這些程序代碼放在finally 關鍵字所標識的語句塊中以保證在程序中即使出現異常也能夠正常地被執行:
finally {
try{
/** 下面代碼中的oneSocketInputObject對象代表Socket輸入流對象實例 */
oneSocketInputObject.close();
/** 下面代碼中的oneSocketOutputObject對象代表Socket輸出流對象實例 */
oneSocketOutputObject.close();
/** 下面代碼中的oneSocketObject對象代表Socket流對象實例 */
oneSocketObject.close();
}
catch(IOException e){
}
}
由於在通訊過程中需要發送和接收數據,因此在客戶端還需要構造出Socket輸入流對象實例(如示例代碼中的oneSocketInputObject)和Socket輸出流對象實例(如示例代碼中的oneSocketOutputObject),在關閉Socket類對象實例之前首先應該分別關閉與Socket相關的所有輸入/輸出流對象實例,以釋放所有的資源。
而且還要注意關閉代碼的順序——與Socket相關的所有輸入/輸出IO流對象實例應該要先關閉,然後再關閉Socket類對象實例本身。
儘管Java語言中提供有垃圾自動回收的機制,Socket通訊過程中所佔用的各種網絡資源最終是會被Java虛擬機JVM系統釋放的。但是為了能夠有效地利用系統的資源,建議讀者還是按照合理的順序在程序代碼中主動釋放這些資源,以避免出現內存洩漏現象。
在程序中如何正確地創建和銷毀軟體應用系統中文件IO流對象實例
課程設計指導——如何應用Java反射技術靈活地創建程序類對象實例
如何應用GOF設計模式中的構建者模式創建複合對象實例
如何應用GOF設計模式中的創建型模式實現鬆耦合地創建對象實例
如何合理地創建對象實例以降低程序類之間關係的耦合度