本篇文章為大家帶來了關於java的相關知識,其中主要介紹了關於socket程式設計的相關內容,Socket是網路驅動層提供給應用程式的一個介面或是說一種機制,下面一起來看一下,希望對大家有幫助。
推薦學習:《java影片教學》
(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)常用方法 方法也適用於ServerSocket類別。 -->ServerSocket類別具有accept()方法,此方法用於等待客戶端發起通訊,以便Socket物件可用於進一步的資料傳輸。## - ->Socket網路程式設計一般分成如下4個步驟:
(1)建立連線。 (2)開啟Socket關聯的輸入/輸出流。 (3)從資料流中寫入資訊和讀取資訊。 (4)關閉所有的資料流和Socket。 -->使用兩個類別模擬實現使用者登入的功能,實現客戶端向伺服器端傳送使用者登入訊息,伺服器端顯示這些資訊。 客戶端實作步驟:1)建立連接,連接指向伺服器與連接埠。
2)以Socket相關的輸入/輸出流開啟。
3)寫入訊息中寫入訊息。
4)從輸入流讀取回應訊息。
5)關閉所有的資料流及Socket。
伺服器端實作步驟:
1)建立連接,以監聽連接埠。
2)使用accept()方法等待客戶啟動通訊
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,實現多重客戶端的回應處理。
## -->java.net封包中的InetAddress類型用於封裝中的InetAddress. 。要建立InetAddress類別的實例,可以使用工廠方法,因為這類沒有建構方法。 -->InetAddress類別中的工廠方法 -->如果找不到主機,兩種方法都會拋出UnknownHostNameException例外。 三、基於UDP協定的Socket程式設計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(); } } } }登入後複製♥ LoginClient22類別和LoginClient3類別
同LoginClient1類別一樣,建立不同的User物件即可
#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)常用方法
2.使用Socket程式設計 客戶諮詢
- ->利用UDP通信的兩個端點是平等的,也就是說通信的兩個程序關係是對等的,沒有主次之分,甚至它們的代碼都可以完全是一樣的,這一點要與基於TCP協定的Socket程式區分開來。
-->以UDP協定為基礎的Socket網路程式設計一般依照下列4個步驟:(1)使用DatagramPacket物件封裝封包。
(2)利用DatagramSocket物件傳送資料包。
(3)利用DatagramSocket物件接收封包。
(4)利用DatagramPacket物件處理資料包。
-->模擬顧客諮詢功能,實現傳送者傳送諮詢問題,接收者接收並顯示傳送來的諮詢問題。
### 發送方實作步驟:###### ### 1)以取得本機主機的InetAddress物件。 ############ 2)以建立DatagramPacket對象,以封裝要傳送的訊息。 ############ 3)地使用DatagramSocket物件將DatagramPacket物件資料送入。 ######### 接收者實作步驟:###### ### 1)以建立DatagramPacketet來接收封裝的資料。 ######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视频教程》
以上是JAVA進階學習之Socket編程的詳細內容。更多資訊請關注PHP中文網其他相關文章!