Java利用Socket實現(xiàn)網(wǎng)絡通信功能
一. Socket編程
1. 簡介
Socket編程是基于TCP/IP協(xié)議的網(wǎng)絡編程技術,它給我們提供了一種可以用于網(wǎng)絡通信的機制,讓我們能在不同的計算機之間進行數(shù)據(jù)交換。我們可以利用Socket編程實現(xiàn)客戶端/服務器程序的開發(fā),如聊天室、FTP客戶端等項目。
2. 通信流程
對于Socket編程,我們要重點搞清楚客戶端與服務器端之間的通信流程。
在Socket編程中有兩種類型的Socket:服務器Socket和客戶端Socket。服務器Socket可以在服務器上創(chuàng)建用于監(jiān)聽客戶端請求的端口,客戶端Socket則可以在客戶端上創(chuàng)建用于連接服務器的Socket??蛻舳薙ocket向服務器Socket發(fā)送請求,服務器Socket可以接收該請求,并創(chuàng)建一個新的Socket用于與客戶端通信。通過這種方式,客戶端和服務器端之間就可以進行數(shù)據(jù)交換。
在Socket編程中,客戶端和服務器之間會通過TCP/IP協(xié)議進行通信。客戶端Socket首先回連接到服務器Socket的IP地址和端口號,這樣就會建立起一個TCP連接。一旦連接建立,客戶端和服務器之間就可以進行數(shù)據(jù)傳輸。數(shù)據(jù)會被分割成數(shù)據(jù)包,并通過TCP/IP協(xié)議在客戶端和服務器之間傳輸。
然后服務器Socket會在服務器端創(chuàng)建一個用于監(jiān)聽客戶端請求的端口,客戶端Socket則在客戶端上創(chuàng)建用于連接服務器的Socket??蛻舳薙ocket向服務器Socket發(fā)送連接請求,服務器Socket接收該請求并創(chuàng)建一個新的Socket用于與客戶端通信。通過這種方式,客戶端和服務器端之間就可以進行數(shù)據(jù)交換。
這樣,客戶端和服務器端之間就通過TCP/IP協(xié)議實現(xiàn)了Socket通信。
3. 核心API
我們可以使用java.net包中的API來實現(xiàn)Socket編程,這些常用的API包括:
- ServerSocket類:用于創(chuàng)建服務器端Socket,監(jiān)聽客戶端發(fā)來的請求。
- Socket類:用于創(chuàng)建客戶端Socket,可以連接服務器Socket。
- InputStream和OutputStream類:用于在Socket之間傳輸數(shù)據(jù)的輸入輸出流。
4. 基本案例-單向通信
接下來先給大家編寫一個可以實現(xiàn)單向通信的基本案例,包括一個服務器端和一個客戶端。這個案例中,客戶端給服務器端發(fā)送一條消息,然后服務器把客戶端發(fā)來的消息打印出來,代碼如下:
4.1 服務器端
下面是服務器端的代碼案例。在本案例中,主要是利用ServerSocket對象定義了一個服務器,并通過accept()方法來獲取與該服務器綁定的Socket客戶端對象。這里要注意端口號“1234”是我們自己隨意定義的,只要不與其他程序的端口號重合即可。然后我們可以在Socket客戶端對象上通過getInputStream()方法來獲取一個InputStream輸入流,進而讀取客戶端發(fā)來的消息。最后大家要記得把各種IO流資源釋放。
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.ServerSocket; import java.net.Socket; /** * @author 一一哥Sun * @company 千鋒教育 */ public class MySocketServer { public static void main(String[] args) throws IOException { //設置服務器的端口號 int portNumber = 1234; // 創(chuàng)建服務器套接字并綁定端口號 ServerSocket serverSocket = new ServerSocket(portNumber); //獲取與ServerSocket關聯(lián)的Socket客戶端對象 Socket acceptSocket = serverSocket.accept(); // 服務器接收客戶端發(fā)來的消息 InputStream serverInputStream = acceptSocket.getInputStream(); BufferedReader in = new BufferedReader(new InputStreamReader(serverInputStream)); String response = in.readLine(); if (response != null) { System.out.println("服務器接收客戶端發(fā)來的消息===>: " + response); } // 關閉套接字和流 serverSocket.close(); acceptSocket.close(); serverInputStream.close(); } }
大家要注意,在今天的案例中,沒有使用循環(huán)來一直進行消息的收發(fā),如果我們想實現(xiàn)不間斷的消息收發(fā),可以把相關代碼寫在循環(huán)體中。所以在今天的案例中,消息收發(fā)一次之后,項目就會停掉。
4.2 客戶端
接著又定義了一個客戶端程序。在該程序中,定義了一個Socket客戶端對象,該對象通過IP地址和端口號來關聯(lián)服務端對象。因為小編的項目,客戶端和服務端是在同一臺機器上,所以這里的IP地址我們可以使用localhost,當然也可以用服務端的實際IP地址。然后利用字符流和字節(jié)輸出流給服務端發(fā)送信息,最后釋放IO流資源。
import java.io.IOException; import java.io.PrintWriter; import java.net.Socket; import java.util.Scanner; /** * @author 一一哥Sun * @company 千鋒教育 */ public class MySocketClient { public static void main(String[] args) throws IOException { try { // 創(chuàng)建Socket對象并連接到服務器,關聯(lián)服務器的ip地址與端口號 Socket socket = new Socket("localhost", 1234); // 創(chuàng)建輸入輸出流,通過套接字發(fā)送和接收數(shù)據(jù) Scanner scanner = new Scanner(System.in); String nextLine = scanner.nextLine(); // 輸出流,客戶端向服務器發(fā)送消息 PrintWriter out = new PrintWriter(socket.getOutputStream(), true); out.println(nextLine); // 關閉套接字和流 out.close(); scanner.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } }
在這個案例中,主要是利用ServerSocket、Socket和IO流這三種API,就實現(xiàn)了客戶端給服務端發(fā)送消息的功能。但是這個案例中,我們只能是客戶端給服務端發(fā)送消息,服務端卻不能給客戶端回復消息,所以接下來要把這個案例改進一下,實現(xiàn)客戶端與服務端之間互相傳遞消息。
5. 改進案例-雙向通信
接下來我們就把上面的案例改進一下,實現(xiàn)客戶端與服務器端之間的雙向通信,即客戶端給服務器端發(fā)送消息,服務器端收到消息之后,再給客戶端返回消息。
5.1 服務器端
在下面的代碼中,小編把服務器端的代碼改進了一下,在if語句中增加了利用OutputStream向客戶端返回信息的代碼,如下所示:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; /** * @author 一一哥Sun * @company 千鋒教育 */ public class MyServer { public static void main(String[] args) throws IOException { int portNumber = 1234; // 創(chuàng)建服務器套接字并綁定端口號 ServerSocket serverSocket = new ServerSocket(portNumber); // 注意:服務器只能接受一次客戶端!用該socket對象既可以接收客戶端發(fā)來的消息,也可以給客戶端回復消息 Socket acceptSocket = serverSocket.accept(); // 服務器接收客戶端發(fā)來的消息 InputStream serverInputStream = acceptSocket.getInputStream(); BufferedReader in = new BufferedReader(new InputStreamReader(serverInputStream)); String response = in.readLine(); if (response != null) { System.out.println("服務器接收客戶端發(fā)來的消息===>: " + response); // 服務器向客戶端發(fā)送響應信息 OutputStream serverOutputStream = acceptSocket.getOutputStream(); String serverResponse = "我是服務器,你的消息已收到!"; serverOutputStream.write(serverResponse.getBytes()); serverOutputStream.flush(); serverOutputStream.close(); } // 關閉套接字和流 serverSocket.close(); acceptSocket.close(); serverInputStream.close(); //serverOutputStream.close(); } }
5.2 客戶端
在下面的代碼中,把客戶端的代碼也改進了一下,主要是增加了利用BufferedReader和InputStream,讀取服務器返回信息的代碼,如下所示:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.util.Scanner; /** * @author 一一哥Sun * @company 千鋒教育 */ public class MyClient { public static void main(String[] args) throws IOException { try { //創(chuàng)建Socket對象并連接到服務器 Socket socket = new Socket("localhost", 1234); //創(chuàng)建輸入輸出流,通過套接字發(fā)送和接收數(shù)據(jù) Scanner scanner=new Scanner(System.in); String nextLine = scanner.nextLine(); //輸出流,客戶端向服務器發(fā)送消息 PrintWriter out = new PrintWriter(socket.getOutputStream(), true); out.println(nextLine); //輸入流,客戶端從服務器接收消息 BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); String response = in.readLine(); System.out.println("Server服務器返回的響應信息===>: " + response); //關閉套接字和流 out.close(); in.close(); scanner.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } }
經(jīng)過以上代碼的改造,我們就實現(xiàn)了服務器與客戶端之間的雙向通信,大家可以把小編的代碼案例運行起來看看效果。
6. 注意事項
大家要注意,在進行Socket通信開發(fā)時,稍有不慎可能就會出現(xiàn)各種問題,所以我們要注意以下事項:
- 在每個Socket連接中,我們都應該嚴格按照協(xié)議規(guī)定的格式進行數(shù)據(jù)的發(fā)送和接收,否則就可能會導致數(shù)據(jù)傳輸失敗或被誤解。
- 在Socket編程中,應該使用多線程或異步機制等技術來避免阻塞。如果阻塞時間過長,可能會導致客戶端或服務器崩潰。在上面的案例中,是把收發(fā)消息的代碼之間寫在了主線程中,其實我們可以把這種耗時的操作放在Thread或線程池中進行實現(xiàn)。
- 服務器端應該可以同時處理多個客戶端的請求,否則有可能會導致客戶端的請求被拒絕或服務器崩潰。
- 我們要保證Socket編程的保安全性,例如防止黑客攻擊、拒絕服務攻擊等惡意行為。
- 我們還應該有適當?shù)腻e誤處理,例如必須處理網(wǎng)絡連接失敗、數(shù)據(jù)傳輸?shù)氖〉犬惓G闆r。
除了以上幾點注意事項,還有其他一些細節(jié)需要注意。在Socket編程中,必須對網(wǎng)絡通信有深入的理解和掌握,才能確保程序的正確性和安全性。
二. 結語
今天的文章,主要是給大家介紹了Socket編程的實現(xiàn)過程。其實Socket通信主要就是分為服務器端和客戶端,有著比較清晰的實現(xiàn)邏輯,學習起來并不難,大家重點理解和掌握Socket通信的實現(xiàn)流暢即可。
相關文章
SpringBoot中MyBatis-Plus 查詢時排除某些字段的操作方法
這篇文章主要介紹了SpringBoot中MyBatis-Plus 查詢時排除某些字段的操作方法,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-08-08Elasticsearch寫入瓶頸導致skywalking大盤空白
這篇文章主要為大家介紹了Elasticsearch寫入瓶頸導致skywalking大盤空白的解決方案,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步2022-02-02SpringBoot實現(xiàn)elasticsearch 查詢操作(RestHighLevelClient 
這篇文章主要給大家介紹了SpringBoot如何實現(xiàn)elasticsearch 查詢操作,文中有詳細的代碼示例和操作流程,具有一定的參考價值,需要的朋友可以參考下2023-07-07Java 實現(xiàn)限流器處理Rest接口請求詳解流程
在工作中是否會碰到這樣的場景,高并發(fā)的請求但是無法全部執(zhí)行,需要一定的限流。如果你是使用的微服務框架,比如SpringCloud,可以使用Gateway增加限流策略來解決。本篇文章是在沒有框架的情況實現(xiàn)限流器2021-11-11SpringBoot啟動時自動執(zhí)行代碼的幾種實現(xiàn)方式
這篇文章主要給大家介紹了關于SpringBoot啟動時自動執(zhí)行代碼的幾種實現(xiàn)方式,文中通過實例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2022-02-02Spring?AI?+?混元帶你實現(xiàn)企業(yè)級穩(wěn)定可部署的AI業(yè)務智能體
我們深入探討了Spring?AI在智能體構建中的實際應用,特別是在企業(yè)環(huán)境中的價值與效能,通過逐步實現(xiàn)一個本地部署的智能體解決方案,我們不僅展示了Spring?AI的靈活性與易用性,還強調了它在推動AI技術與業(yè)務深度融合方面的潛力,感興趣的朋友一起看看吧2024-11-11聊聊springboot2.2.3升級到2.4.0單元測試的區(qū)別
這篇文章主要介紹了springboot 2.2.3 升級到 2.4.0單元測試的區(qū)別,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10