Java 實(shí)現(xiàn)簡(jiǎn)單Socket 通信的示例
Java socket 封裝了傳輸層的實(shí)現(xiàn)細(xì)節(jié),開發(fā)人員可以基于 socket 實(shí)現(xiàn)應(yīng)用層。本文介紹了 Java socket 簡(jiǎn)單用法。
1. 傳輸層協(xié)議
傳輸層包含了兩種協(xié)議,分別是 TCP (Transmission Control Protocol,傳輸控制協(xié)議) 和 UDP (User Datagram Protocol,用戶數(shù)據(jù)報(bào)協(xié)議)。
TCP 是一種面向連接,可靠的流協(xié)議。通信雙方在“發(fā)送-接收”數(shù)據(jù)之前需要先建立 TCP 連接,然后通過互相發(fā)送二進(jìn)制數(shù)據(jù)流來進(jìn)行通信。所謂連接,指的是各種設(shè)備、線路,或網(wǎng)絡(luò)中進(jìn)行通信的應(yīng)用程序?yàn)榱讼嗷鬟f消息而建立的專有、虛擬的通信線路。連接一旦建立,進(jìn)行通信的應(yīng)用程序只使用該虛擬的通信線路發(fā)送和接收數(shù)據(jù)。TCP 還需要處理端到端之間的流量控制。
UDP 是一種無連接的,不可靠的數(shù)據(jù)報(bào)協(xié)議。發(fā)送方不需要與接收方建立連接,通信雙方通過發(fā)送一個(gè)個(gè)獨(dú)立的數(shù)據(jù)報(bào)來進(jìn)行通訊。
TCP 通過序列號(hào)、確認(rèn)應(yīng)答、數(shù)據(jù)校驗(yàn)等機(jī)制確保了傳輸?shù)目煽啃?,適用于需要可靠數(shù)據(jù)傳輸?shù)膱?chǎng)景,應(yīng)用層協(xié)議 HTTP,F(xiàn)TP 基于 TCP。UDP 沒有復(fù)雜的控制機(jī)制,不糾錯(cuò),不重發(fā),不保證數(shù)據(jù)的準(zhǔn)確性,不確保數(shù)據(jù)到達(dá)目的地;不過 UDP 傳送等量數(shù)據(jù)花費(fèi)更小的流量,適用于對(duì)時(shí)延要求高但對(duì)準(zhǔn)確性要求不高的場(chǎng)景,如視頻、音頻通訊。
Java 中有 3 種套接字類,java.net.Socket 和 java.net.ServerSocket 基于 TCP,java.net.DatagramSocket 基于 UDP。
2. TCP 示例
TCP 是面向連接的,所以在進(jìn)行通訊之前發(fā)送端(客戶端)需要先連接到接收端(服務(wù)端)。客戶端通過 new Socket("localhost", 9090) 來創(chuàng)建一個(gè)連接到服務(wù)端的套接字,這個(gè)套接字連接到主機(jī) localhost 的 9090 端口。
ServerSocket 實(shí)現(xiàn)服務(wù)端套接字,通過 new ServerSocket(9090) 來創(chuàng)建一個(gè)監(jiān)聽端口為 9090 實(shí)例;ServerSocket.accept() 方法會(huì)阻塞等待客戶端的連接,一旦有連接過來,會(huì)返回一個(gè)服務(wù)端的 Socket 實(shí)例。連接建立完成,客戶端 Socket 實(shí)例和服務(wù)端 Socket 實(shí)例就可以面向輸入輸出流發(fā)送數(shù)據(jù)了。
2.1 示例效果
客戶端程序接收控制臺(tái)輸入的內(nèi)容,客戶端控制臺(tái)每輸入一行,就往服務(wù)端發(fā)送,服務(wù)端接收到消息之后,將消息打印到控制臺(tái)。
客戶端輸入 "Bye" 時(shí),客戶端斷開與服務(wù)端的連接,客戶端程序退出,服務(wù)端程序繼續(xù)等待連接。
客戶端控制臺(tái)輸入輸出:
$ java Server.java Bind Port 9090 New client connected. Received Message --> Are you OK!
服務(wù)端控制臺(tái)輸出:
$ java Client.java Are you OK! Send Msg --> Are you OK! Bye $
2.2 服務(wù)端程序代碼
import java.net.*; import java.io.*; class Server { public static void main(String[] args) { // ServerSocket 實(shí)現(xiàn)了 AutoCloseable 接口,所以支持 try-with-resource 語(yǔ)句 // 創(chuàng)建一個(gè) ServerSocket,監(jiān)聽 9090 端口 try(ServerSocket serv = new ServerSocket(9090)){ System.out.printf("Bind Port %d\n", serv.getLocalPort()); Socket socket = null; while(true){ // 接收連接,如果沒有連接,accept() 方法會(huì)阻塞 socket = serv.accept(); // 獲取輸入流,并使用 BufferedInputStream 和 InputStreamReader 裝飾,方便以字符流的形式處理,方便一行行讀取內(nèi)容 try(BufferedReader in = new BufferedReader( new InputStreamReader(socket.getInputStream()) )){ String msg = null; char[] cbuf = new char[1024]; int len = 0; while( (len = in.read(cbuf, 0, 1024)) != -1 ){ // 循環(huán)讀取輸入流中的內(nèi)容 msg = new String(cbuf, 0, len); if("Bye".equals(msg)) { // 如果檢測(cè)到 "Bye" ,則跳出循環(huán),不再讀取輸入流中內(nèi)容。 break; } System.out.printf("Received Message --> %s \n", msg); } }catch (IOException e){ e.printStackTrace(); } } }catch (IOException e){ e.printStackTrace(); } } } 2.3 客戶端程序代碼 import java.net.*; import java.io.*; import java.util.*; class Client{ public static void main(String[] args){ try(Socket socket = new Socket("localhost", 9090)){ BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); Scanner scanner = new Scanner(System.in); scanner.useDelimiter("\r\n"); String msg = null; while( !(msg = scanner.next()).equals("Bye") ){ System.out.printf("Send Msg --> %s \n", msg); out.write(msg); out.flush(); // 立即發(fā)送,否則需要積累到一定大小才一次性發(fā)送 } }catch (IOException e){ e.printStackTrace(); } } }
3. UDP 示例
UDP 不需要連接,客戶端與服務(wù)端通過發(fā)送數(shù)據(jù)報(bào)來完成通信。Java 中使用 java.net.DatagramSocket 來表示 UDP 客戶端或服務(wù)端的套接字,使用 java.net.DatagramPacket 來表示 UDP 的數(shù)據(jù)報(bào)??蛻舳撕头?wù)端可以直接向?qū)Ψ桨l(fā)送數(shù)據(jù)報(bào),不需要進(jìn)行連接。
下面代碼基于 UDP 實(shí)現(xiàn)了與上面程序同樣的功能。不過消息可能會(huì)出錯(cuò),某些消息可能也不能到達(dá)服務(wù)端。
3.1 服務(wù)端程序代碼
import java.net.*; import java.io.*; class Server { public static void main(String[] args){ // 創(chuàng)建一個(gè) DatagramPacket 實(shí)例,用來接收客戶端發(fā)送過來的 UDP 數(shù)據(jù)報(bào),這個(gè)實(shí)例可以重復(fù)利用。 byte[] buf = new byte[8192]; // 緩存區(qū) int len = buf.length; // 要利用的緩存區(qū)的大小 DatagramPacket pac = new DatagramPacket(buf, len); // 創(chuàng)建服務(wù)端的套接字,需要指定綁定的端口號(hào) try(DatagramSocket serv = new DatagramSocket(9191)){ while(true){ serv.receive(pac); // 接收數(shù)據(jù)報(bào)。如果沒有數(shù)據(jù)報(bào)發(fā)送過來,會(huì)阻塞 System.out.println("Message --> " + new String(pac.getData(), 0, pac.getLength())); } }catch (IOException e){ e.printStackTrace(); } } }
3.2 客戶端程序代碼
import java.io.*; import java.net.*; import java.util.*; class Client { public static void main(String[] args){ // 創(chuàng)建一個(gè)客戶端的 UDP 套接字,不需要指定任何信息 try(DatagramSocket client = new DatagramSocket()){ // 創(chuàng)建一個(gè)數(shù)據(jù)報(bào)實(shí)例,數(shù)據(jù)和長(zhǎng)度在發(fā)送之前都會(huì)重新設(shè)置,所以這里直接置為 0 即可。 // 由于是發(fā)送端,所以需要設(shè)置服務(wù)端的地址和端口 DatagramPacket pac = new DatagramPacket(new byte[0], 0, InetAddress.getByName("localhost"), 9191); // 掃描控制臺(tái)輸入 Scanner scanner = new Scanner(System.in); scanner.useDelimiter("\r\n"); String msg = null; while( !(msg = scanner.next()).equals("Bye") ){ // 設(shè)置要發(fā)送的數(shù)據(jù) pac.setData(msg.getBytes()); // 發(fā)送數(shù)據(jù)報(bào) client.send(pac); System.out.println("Sent Message --> " + msg); } }catch (IOException e){ e.printStackTrace(); } } }
需要注意的是,UDP 是面向無連接的,但 DatagramSocket 的 API 中提供了帶有 connect 字樣的方法,這里的 connect 并非 TCP 中連接的意思。而是指定了當(dāng)前的 UDP 套接字只能夠向指定的主機(jī)和端口發(fā)送數(shù)據(jù)報(bào)。
以上就是Java 實(shí)現(xiàn)簡(jiǎn)單Socket 通信的示例的詳細(xì)內(nèi)容,更多關(guān)于Java 實(shí)現(xiàn)Socket 通信的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java的System.getProperty()方法獲取大全
這篇文章主要介紹了Java的System.getProperty()方法獲取大全,羅列了System.getProperty()方法獲取各類信息的用法,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2014-12-12spring?boot中spring框架的版本升級(jí)圖文教程
Spring Boot是一款基于Spring框架的快速開發(fā)框架,它提供了一系列的開箱即用的功能和組件,這篇文章主要給大家介紹了關(guān)于spring?boot中spring框架的版本升級(jí)的相關(guān)資料,需要的朋友可以參考下2023-10-10詳解Mybatis是如何把數(shù)據(jù)庫(kù)數(shù)據(jù)封裝到對(duì)象中的
這篇文章主要介紹了Mybatis是如何把數(shù)據(jù)庫(kù)數(shù)據(jù)封裝到對(duì)象中的,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12教你使用eclipse?搭建Swt?環(huán)境的全過程
本文給大家分享使用eclipse?搭建Swt?環(huán)境的全過程,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-12-12spring Cloud微服務(wù)跨域?qū)崿F(xiàn)步驟
這篇文章主要介紹了spring Cloud微服務(wù)跨域?qū)崿F(xiàn)步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11Java 實(shí)戰(zhàn)項(xiàng)目錘煉之小區(qū)物業(yè)管理系統(tǒng)的實(shí)現(xiàn)流程
讀萬(wàn)卷書不如行萬(wàn)里路,只學(xué)書上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+SSM+jsp+mysql+maven實(shí)現(xiàn)一個(gè)小區(qū)物業(yè)管理系統(tǒng),大家可以在過程中查缺補(bǔ)漏,提升水平2021-11-11idea啟動(dòng)報(bào)錯(cuò):Command line is too long問題
在使用IDEA時(shí),若遇到"Commandlineistoolong"錯(cuò)誤,通常是因?yàn)槊钚虚L(zhǎng)度超限,這是因?yàn)镮DEA通過命令行或文件將classpath傳遞至JVM,操作系統(tǒng)對(duì)命令行長(zhǎng)度有限制,解決方法是切換至動(dòng)態(tài)類路徑,通過修改項(xiàng)目的workspace.xml文件2024-09-09Spring MVC整合Shiro權(quán)限控制的方法
這篇文章主要介紹了Spring MVC整合Shiro權(quán)限控制,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-05-05