程式設計師必備介面測試偵錯工具:
推薦學習:《》
(1)Java最初是作為網路程式語言出現的,它對網路的高度支援,使得使用者端和伺服器端流暢的溝通成為現實。
(2)在網路程式設計中,使用最多的就是Socket,每一個實用的網路程式都少不了它的參與。
(3)在計算機網路程式設計技術中,兩個程序或者說兩臺計算機可以通過一個網路通訊連線實現資料的交換,這種通訊鏈路的端點就被稱為「通訊端」(英文名稱也就是Socket)。
(4)Socket是網路驅動層提供給應用程式的一個介面或者說一種機制。
(5)使用物流送快遞的例子來說明Socket:
-->發件人將有收貨人地址資訊的貨物送到快遞站,發件人不用關心物流是如何進行的,貨物被送到收貨人所在地區的快遞站點,進行配送,收貨人等待收貨就可以了。
-->這個過程很形象地說明了資訊在網路中傳遞的過程。其中,貨物就是資料資訊,2個快遞站點就是2個端點Socket。
(6)資訊如何在網路中定址傳遞,應用程式並不用關心,只負責準備傳送資料和接收資料即可。
(1)對於程式設計人員來說,無須瞭解Socket底層機制是如何傳送資料的,而是直接將資料提交給Socket,Socket會根據應用程式提供的相關資訊,通過一系列計算,繫結IP及資訊資料,將資料交給驅動程式向網路上傳送。
(2)Socket的底層機制非常複雜,Java平臺提供了一些簡單但是強大的類,可以簡單有效地使用Socket開發通訊程式而無須瞭解底層機制。
(1)java.net包提供了若干支援基於通訊端的使用者端/伺服器通訊的類。
(2)java.net包中常用的類有Socket、ServerSocket、DatagramPacket、DatagramSocket、InetAddress、URL、URLConnection和URLEncoder等。
(3)為了監聽使用者端的連線請求,可以使用ServerSocket類。
(4)Socket類實現用於網路上程序間通訊的通訊端。
(5)DatagramSocket類使用UDP協定實現使用者端和伺服器通訊端。
(6)DatagramPacket類使用DatagramSocket類的物件封裝設定和收到的資料包。
(7)InetAddress類表示Internet地址。
(8)在建立資料包報文和Socket物件時,可以使用InetAddress類
(1)java.net包的兩個類Socket和ServerSocket,分別用來實現雙向安全連線的使用者端和伺服器端,它們是基於TCP協定進行工作的,工作過程如同打電話的過程,只有雙方都接通了,才能開始通話。
(2)進行網路通訊時,Socket需要藉助資料流來完成資料的傳遞工作。
(3)一個應用程式要通過網路向另一個應用程式傳送資料,只要簡單地建立Socket,然後將資料寫入到與該Socket關聯的輸出流即可。對應的,接收方的應用程式建立Socket,從相關聯的輸入流讀取資料即可。
(4)注意:2個端點在基於TCP協定的Socket程式設計中,經常一個作為使用者端,一個作為伺服器端,也就是遵循client-server模型。
● Socket類
Socket物件在使用者端和伺服器之間建立連線。可用Socket類的構造方法建立通訊端,並將此通訊端連線至指定的主機和埠。
(1)構造方法
-->第一種構造方法以主機名和埠號作為引數來建立一個Socket物件。建立物件時可能丟擲UnknownHostException或IOException異常,必須捕獲它們。
Socket s = new Socket(hostName,port);
-->第二種構造方法以InetAddress物件和埠號作為引數來建立一個Socket物件。構造方法可能丟擲IOException或UnknownHostException異常,必須捕獲並處理它們。
Socket s = new Socket(address,port);
(2)常用方法
● ServerSocket類
ServerSocket物件等待使用者端建立連線,連線建立以後進行通訊。
(1)構造方法
-->第一種構造方法接受埠號作為引數建立ServerSocket物件,建立此物件時可能丟擲IOException異常,必須捕獲和處理它。
ServerSocket ss = new ServerSocket(port);
-->第二種構造方法接受埠號和最大佇列長度作為引數,佇列長度表示系統在拒絕連線前可以擁有的最大使用者端連線數。
ServerSocket ss = new ServerSocket(port,maxqu);
(2)常用方法
-->Socket類中列出的方法也適用於ServerSocket類。
-->ServerSocket類具有accept()方法,此方法用於等待使用者端發起通訊,這樣Socket物件就可用於進一步的資料傳輸。
● 實現單使用者登入
-->Socket網路程式設計一般分成如下4個步驟進行:
(1)建立連線。
(2)開啟Socket關聯的輸入/輸出流。
(3)從資料流中寫入資訊和讀取資訊。
(4)關閉所有的資料流和Socket。
-->使用兩個類模擬實現使用者登入的功能,實現使用者端向伺服器端傳送使用者登入資訊,伺服器端顯示這些資訊。
使用者端實現步驟:
1)建立連線,連線指向伺服器及埠。
2)開啟Socket關聯的輸入/輸出流。
3)向輸出流中寫入資訊。
4)從輸入流中讀取響應資訊。
5)關閉所有的資料流和Socket。
伺服器端實現步驟:
1)建立連線,監聽埠。
2)使用accept()方法等待使用者端發起通訊
3)開啟Socket關聯的輸入/輸出流。
4)從輸入流中讀取請求資訊。
5)向輸出流中寫入資訊。
6)關閉所有的資料流和Socket。
-->使用者端和伺服器端的互動,採用一問一答的模式,先啟動伺服器進入監聽狀態,等待使用者端的連線請求,連線成功以後,使用者端先「發言」,伺服器給予「迴應」。
範例01:實現傳遞物件資訊。
♥ user類
package cn.bdqn.demo02; import java.io.Serializable; public class User implements Serializable { private static final long serialVersionUID = 1L; /** 使用者名稱 */ private String loginName; /** 使用者密碼 */ private String pwd; public User() { super(); } public User(String loginName, String pwd) { super(); this.loginName = loginName; this.pwd = pwd; } public String getLoginName() { return loginName; } public void setLoginName(String loginName) { this.loginName = loginName; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } }登入後複製♥ LoginServer類
package cn.bdqn.demo02; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class LoginServer { public static void main(String[] args) { ServerSocket serverSocket = null; Socket socket = null; InputStream is = null; ObjectInputStream ois = null; OutputStream os = null; try { // 建立一個伺服器Socket(ServerSocket),指定埠8800並開始監聽 serverSocket = new ServerSocket(8800); // 使用accept()方法等待使用者端發起通訊 socket = serverSocket.accept(); // 開啟輸入流 is = socket.getInputStream(); // 反序列化 ois = new ObjectInputStream(is); // 獲取使用者端資訊,即從輸入流讀取資訊 User user = (User) ois.readObject(); if (user != null) { System.out.println("我是伺服器,客戶登入資訊為:" + user.getLoginName() + "," + user.getPwd()); } // 給使用者端一個響應,即向輸出流中寫入資訊 String reply = "歡迎你,登入成功"; os = socket.getOutputStream(); os.write(reply.getBytes()); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { // 關閉資源 try { os.close(); ois.close(); is.close(); socket.close(); serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } }登入後複製LoginClient類
package cn.bdqn.demo02; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.net.Socket; import java.net.UnknownHostException; public class LoginClient { /* * 範例02:升級演示範例01,實現傳遞物件資訊。 */ public static void main(String[] args) { Socket socket = null; OutputStream os = null; ObjectOutputStream oos = null; InputStream is = null; BufferedReader br = null; try { // 建立使用者端Socket連線,指定伺服器的位置為本機以及埠為8800 socket = new Socket("localhost", 8800); // 開啟輸出流 os = socket.getOutputStream(); // 物件序列化 oos = new ObjectOutputStream(os); // 傳送使用者端資訊,即向輸出流中寫入資訊 User user = new User("Tom", "123456"); oos.writeObject(user); socket.shutdownOutput(); // 接收伺服器端的響應,即從輸入流中讀取資訊 is = socket.getInputStream(); br = new BufferedReader(new InputStreamReader(is)); String reply; while ((reply = br.readLine()) != null) { System.out.println("我是使用者端,伺服器的響應為:" + reply); } } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { br.close(); is.close(); oos.close(); os.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } } }登入後複製
範例02:升級演示範例01,實現傳遞多個物件資訊。
user類
package cn.bdqn.demo03; import java.io.Serializable; public class User implements Serializable { private static final long serialVersionUID = 1L; /** 使用者名稱 */ private String loginName; /** 使用者密碼 */ private String pwd; public User() { super(); } public User(String loginName, String pwd) { super(); this.loginName = loginName; this.pwd = pwd; } public String getLoginName() { return loginName; } public void setLoginName(String loginName) { this.loginName = loginName; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } }登入後複製LoginServer類
package cn.bdqn.demo03; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class LoginServer { public static void main(String[] args) { ServerSocket serverSocket = null; Socket socket = null; InputStream is = null; ObjectInputStream ois = null; OutputStream os = null; try { // 建立一個伺服器Socket(ServerSocket),指定埠8800並開始監聽 serverSocket = new ServerSocket(8800); // 使用accept()方法等待使用者端發起通訊 socket = serverSocket.accept(); // 開啟輸入流 is = socket.getInputStream(); // 反序列化 ois = new ObjectInputStream(is); // 獲取使用者端資訊,即從輸入流讀取資訊 User[] users = (User[]) ois.readObject(); for (int i = 0; i < users.length; i++) { System.out.println("我是伺服器,客戶登入資訊為:" + users[i].getLoginName() + "," + users[i].getPwd()); } // 給使用者端一個響應,即向輸出流中寫入資訊 String reply = "歡迎你,登入成功"; os = socket.getOutputStream(); os.write(reply.getBytes()); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { // 關閉資源 try { os.close(); ois.close(); is.close(); socket.close(); serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } }登入後複製LoginClient類
package cn.bdqn.demo03; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.net.Socket; import java.net.UnknownHostException; public class LoginClient { /* * 範例02:升級演示範例01,實現傳遞物件資訊。 */ public static void main(String[] args) { Socket socket = null; OutputStream os = null; ObjectOutputStream oos = null; InputStream is = null; BufferedReader br = null; try { // 建立使用者端Socket連線,指定伺服器的位置為本機以及埠為8800 socket = new Socket("localhost", 8800); // 開啟輸出流 os = socket.getOutputStream(); // 物件序列化 oos = new ObjectOutputStream(os); // 傳送使用者端資訊,即向輸出流中寫入資訊 User user1 = new User("Tom", "123456"); User user2 = new User("bob", "123456"); User user3 = new User("lisa", "123456"); User[] users = {user1,user2,user3}; oos.writeObject(users); socket.shutdownOutput(); // 接收伺服器端的響應,即從輸入流中讀取資訊 is = socket.getInputStream(); br = new BufferedReader(new InputStreamReader(is)); String reply; while ((reply = br.readLine()) != null) { System.out.println("我是使用者端,伺服器的響應為:" + reply); } } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { br.close(); is.close(); oos.close(); os.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } } }登入後複製
● 實現多使用者端使用者登入
-->一問一答的模式在現實中顯然不是人們想要的。一個伺服器不可能只針對一個使用者端服務,一般是面向很多的使用者端同時提供服務的,但是單執行緒實現必然是這樣的結果。
-->解決這個問題的辦法是採用多執行緒的方式,可以在伺服器端建立一個專門負責監聽的應用主服務程式、一個專門負責響應的執行緒程式。這樣可以利用多執行緒處理多個請求。
->使用者端實現步驟:
1)建立連線,連線指向伺服器及埠。
2)開啟Socket關聯的輸入/輸出流。
3)向輸出流中寫入資訊。
4)從輸入流中讀取響應資訊。
5)關閉所有的資料流和Socket。
-->伺服器端實現步驟:
1)建立伺服器執行緒類,run()方法中實現對一個請求的響應處理。
2)修改伺服器端程式碼,讓伺服器端Socket一直處於監聽狀態。
3)伺服器端每監聽到一個請求,建立一個執行緒物件並啟動。
範例03:升級演示範例02,實現多使用者端的響應處理。
user類
package cn.bdqn.demo04; import java.io.Serializable; public class User implements Serializable { private static final long serialVersionUID = 1L; /** 使用者名稱 */ private String loginName; /** 使用者密碼 */ private String pwd; public User() { super(); } public User(String loginName, String pwd) { super(); this.loginName = loginName; this.pwd = pwd; } public String getLoginName() { return loginName; } public void setLoginName(String loginName) { this.loginName = loginName; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } }登入後複製LoginThread
package cn.bdqn.demo04; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.OutputStream; import java.net.Socket; public class LoginThread extends Thread { /* * 範例03:升級範例02,實現多使用者端的響應處理。 * * 分析如下: * (1)建立伺服器端執行緒類,run()方法中實現對一個請求的響應處理。 * (2)修改伺服器端程式碼,讓伺服器端Socket一直處於監聽狀態。 * (3)伺服器端每監聽到一個請求,建立一個執行緒物件並啟動 */ Socket socket = null; //每啟動一個執行緒,連線對應的Socket public LoginThread(Socket socket) { this.socket = socket; } //啟動執行緒,即響應客戶請求 public void run() { InputStream is = null; ObjectInputStream ois = null; OutputStream os = null; try { //開啟輸入流 is = socket.getInputStream(); //反序列化 ois = new ObjectInputStream(is); //獲取使用者端資訊,即從輸入流讀取資訊 User user = (User)ois.readObject(); if(user!=null){ System.out.println("我是伺服器,客戶登入資訊為:"+user.getLoginName()+","+user.getPwd()); } //給使用者端一個響應,即向輸出流中寫入資訊 os = socket.getOutputStream(); String reply = "歡迎你,登入成功"; os.write(reply.getBytes()); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); }finally{ try { os.close(); ois.close(); is.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } } }登入後複製LoginServer類
package cn.bdqn.demo04; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; public class LoginServer { public static void main(String[] args) { ServerSocket serverSocket = null; try { // 建立一個伺服器Socket(ServerSocket)指定埠並開始監聽 serverSocket = new ServerSocket(8800); // 監聽一直進行中 while (true) { // 使用accept()方法等待客戶發起通訊 Socket socket = serverSocket.accept(); LoginThread loginThread = new LoginThread(socket); loginThread.start(); } } catch (IOException e) { e.printStackTrace(); } } }登入後複製♥ LoginClient1類
package cn.bdqn.demo04; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.net.Socket; import java.net.UnknownHostException; public class LoginClient01 { /* * 使用者端通過輸出流向伺服器端傳送請求資訊 * 伺服器偵聽使用者端的請求得到一個Socket物件,將這個Socket物件傳遞給執行緒類 * 執行緒類通過輸入流獲取使用者端的請求並通過輸出流向使用者端傳送響應資訊 * 使用者端通過輸入流讀取伺服器傳送的響應資訊 * */ /* * 範例03:升級演示範例02,實現多使用者端的響應處理 */ public static void main(String[] args) { Socket socket = null; OutputStream os = null; ObjectOutputStream oos = null; InputStream is = null; BufferedReader br = null; try { // 建立使用者端Socket連線,指定伺服器的位置為本機以及埠為8800 socket = new Socket("localhost", 8800); // 開啟輸出流 os = socket.getOutputStream(); // 物件序列化 oos = new ObjectOutputStream(os); // 傳送使用者端資訊,即向輸出流中寫入資訊 User user = new User("Tom", "123456"); oos.writeObject(user); socket.shutdownOutput(); // 接收伺服器端的響應,即從輸入流中讀取資訊 is = socket.getInputStream(); br = new BufferedReader(new InputStreamReader(is)); String reply; while ((reply = br.readLine()) != null) { System.out.println("我是使用者端,伺服器的響應為:" + reply); } } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { br.close(); is.close(); oos.close(); os.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } } }登入後複製♥ LoginClient2類和LoginClient3類
同LoginClient1類一樣,建立不同的User物件即可
-->java.net包中的InetAddress類用於封裝IP地址和DNS。要建立InetAddress類的範例,可以使用工廠方法,因為此類沒有構造方法。
-->InetAddress類中的工廠方法
-->如果找不到主機,兩種方法都將丟擲UnknownHostNameException異常。
TCP | UDP | |
是否連線 | 面向連線 | 面向非連線 |
傳輸可靠性 | 可靠 | 不可靠 |
速度 | 慢 | 快 |
(1)基於TCP的網路通訊是安全的,是雙向的,如同打電話,需要先有伺服器端,建立雙向連線後,才開始資料通訊。
(2)基於UDP的網路通訊只需要指明對方地址,然後將資料送出去,並不會事先連線。這樣的網路通訊是不安全的,所以只應用在如聊天系統、諮詢系統等場合下。
(3)資料包是表示通訊的一種報文型別,使用資料包進行通訊時無須事先建立連線,它是基於UDP協定進行的。
(4)Java中有兩個可使用資料包實現通訊的類,即DatagramPacket和DatagramSocket。
(5)DatagramPacket類起到容器的作用,DatagramSocket類用於傳送或接收DatagramPacket。
(6)DatagramPacket類不提供傳送或接收資料的方法,而DatagramSocket類提供send()方法和receive()方法,用於通過通訊端傳送和接收資料包。
● DatagramPacket類
(1)構造方法
-->使用者端要向外傳送資料,必須首先建立一個DatagramPacket物件,再使用DatagramSocket物件傳送。
(2)常用方法
● DatagramSocket類
(1)構造方法
-->DatagramSocket類不維護連線狀態,不產生輸入/輸出資料流,它的唯一作用就是接收和傳送DatagramPacket物件封裝好的資料包。
(2)常用方法
-->利用UDP通訊的兩個端點是平等的,也就是說通訊的兩個程式關係是對等的,沒有主次之分,甚至它們的程式碼都可以完全是一樣的,這一點要與基於TCP協定的Socket程式設計區分開來。
-->基於UDP協定的Socket網路程式設計一般按照以下4個步驟進行:
(1)利用DatagramPacket物件封裝封包。
(2)利用DatagramSocket物件傳送封包。
(3)利用DatagramSocket物件接收封包。
(4)利用DatagramPacket物件處理封包。
-->模擬客戶諮詢功能,實現傳送方傳送諮詢問題,接收方接收並顯示傳送來的諮詢問題。
傳送方實現步驟:
1)獲取本地主機的InetAddress物件。
2)建立DatagramPacket物件,封裝要傳送的資訊。
3)利用DatagramSocket物件將DatagramPacket物件資料傳送出去。
接收方實現步驟:
1)建立DatagramPacket物件,準備接收封裝的資料。
2)建立DatagramSocket物件,接收資料儲存於DatagramPacket物件中。
3)利用DatagramPacket物件處理資料。
範例04:傳送方傳送諮詢問題,接收方迴應諮詢。
♥ Receive類
package cn.bdqn.demo05; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketAddress; import java.net.SocketException; public class Receive { public static void main(String[] args) { /* * 範例06:傳送方傳送諮詢問題,接收方迴應諮詢。 * * 接收方實現步驟如下: * (1)建立DatagramPacket物件,準備接收封裝的資料。 * (2)建立DatagramSocket物件,接收資料儲存於DatagramPacket物件中。 * (3)利用DatagramPacket物件處理資料。 */ DatagramSocket ds = null; DatagramPacket dp = null; DatagramPacket dpto = null; // 建立DatagramPacket物件,用來準備接收資料 byte[] buf = new byte[1024]; dp = new DatagramPacket(buf, 1024); try { // 建立DatagramSocket物件,接收資料 ds = new DatagramSocket(8800); ds.receive(dp); // 顯示接收到的資訊 String mess = new String(dp.getData(), 0, dp.getLength()); System.out.println(dp.getAddress().getHostAddress() + "說:" + mess); String reply = "你好,我在,請諮詢!"; // 顯示與本地對話方塊 System.out.println("我 說:" + reply); // 建立DatagramPacket物件,封裝資料 SocketAddress sa = dp.getSocketAddress(); dpto = new DatagramPacket(reply.getBytes(), reply.getBytes().length, sa); ds.send(dpto); } catch (SocketException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { ds.close(); } } }登入後複製♥ Send類
package cn.bdqn.demo05; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException; import java.net.UnknownHostException; public class Send { /* * 範例06:升級範例05,傳送方傳送諮詢問題,接收方迴應諮詢。 * * 傳送方實現步驟如下: * (1)獲取本地主機的InetAddress物件。 * (2)建立DatagramPacket物件,封裝要傳送的資訊。 * (3)利用DatagramSocket物件將DatagramPacket物件資料傳送出去。 */ public static void main(String[] args) { DatagramSocket ds = null; InetAddress ia = null; String mess = "你好,我想諮詢一個問題。"; System.out.println("我說:" + mess); try { // 獲取本地主機地址 ia = InetAddress.getByName("localhost"); // 建立DatagramPacket物件,封裝資料 DatagramPacket dp = new DatagramPacket(mess.getBytes(), mess.getBytes().length, ia, 8800); // 建立DatagramSocket物件,向伺服器傳送資料 ds = new DatagramSocket(); ds.send(dp); byte[] buf = new byte[1024]; DatagramPacket dpre = new DatagramPacket(buf, buf.length); ds.receive(dpre); // 顯示接收到的資訊 String reply = new String(dpre.getData(), 0, dpre.getLength()); System.out.println(dpre.getAddress().getHostAddress() + "說:" + reply); } catch (UnknownHostException e) { e.printStackTrace(); } catch (SocketException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { ds.close(); } } }登入後複製
推薦學習:《》
以上就是JAVA進階學習之Socket程式設計的詳細內容,更多請關注TW511.COM其它相關文章!