日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > java >内容正文

java

Java网络编程二:Socket详解

發(fā)布時(shí)間:2023/12/9 java 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java网络编程二:Socket详解 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Socket又稱套接字,是連接運(yùn)行在網(wǎng)絡(luò)上兩個(gè)程序間的雙向通訊的端點(diǎn)。

一、使用Socket進(jìn)行網(wǎng)絡(luò)通信的過(guò)程

服務(wù)端:服務(wù)器程序?qū)⒁粋€(gè)套接字綁定到一個(gè)特定的端口,并通過(guò)此套接字等待和監(jiān)聽客戶端的連接請(qǐng)求。

客戶端:客戶端程序根據(jù)你服務(wù)器所在的主機(jī)名和端口號(hào)發(fā)出連接請(qǐng)求。

兩者之間的通信是通過(guò)Socket完成的,我們可以認(rèn)為Socket是兩個(gè)城市之間的交通工具,有了它,就可以在兩個(gè)城市之間穿梭了。

Socket通信示例

主機(jī)A的應(yīng)用程序和主機(jī)B的應(yīng)用程序通信,必須通過(guò)Socket建立連接,而建立Socket必須由底層的TCP/IP協(xié)議來(lái)建立TCP連接。建立TCP連接需要底層IP協(xié)議來(lái)尋址網(wǎng)絡(luò)中的主機(jī)。IP地址只能幫助我們找到目標(biāo)主機(jī),但是一個(gè)主機(jī)上面有多個(gè)應(yīng)用程序,如何才能找到我們需要的應(yīng)用程序,這個(gè)時(shí)候就可以通過(guò)端口號(hào)來(lái)指定了。 

二、簡(jiǎn)易服務(wù)端、客戶端模擬

服務(wù)器端:

1 public static void main(String[] args) throws IOException 2 { 3 //創(chuàng)建一個(gè)ServerSocket,用于監(jiān)聽客戶端Socket連接請(qǐng)求 4 ServerSocket ss = new ServerSocket(8888); 5 System.out.println("server start"); 6 //采用循環(huán)方式監(jiān)聽客戶端的請(qǐng)求 7 while(true) 8 { 9 //偵聽并接受到此套接字的連接。此方法在連接傳入之前一直阻塞。 10 Socket socket = ss.accept(); 11 OutputStream os = socket.getOutputStream(); 12 PrintStream ps = new PrintStream(os); 13 ps.print("您好,您收到了來(lái)自服務(wù)端的中秋祝福"); 14 ps.close(); 15 os.close(); 16 socket.close(); 17 } 18 }

執(zhí)行結(jié)果:

server start

客戶端:

1 public static void main(String[] args) throws IOException, Exception 2 { 3 Socket socket = new Socket("localhost",8888); 4 InputStream is = socket.getInputStream(); 5 BufferedReader br = new BufferedReader(new InputStreamReader(is)); 6 String str = br.readLine(); 7 System.out.println(str); 8 br.close(); 9 is.close(); 10 socket.close(); 11 }

執(zhí)行結(jié)果:

您好,您收到了來(lái)自服務(wù)端的中秋祝福

1、上面展示的是一個(gè)簡(jiǎn)易的服務(wù)端和客戶端通信的建立過(guò)程。

2、我們通過(guò)交互圖來(lái)詳細(xì)介紹這個(gè)過(guò)程:

? ??

3、首先在server端,指定端口號(hào)創(chuàng)建serverSocket對(duì)象,通過(guò)serverSocket的accpet方法獲取套接字,這個(gè)方法的特點(diǎn)是:偵聽并接受到此套接字的連接,此方法在連接傳入之前一直阻塞。這也就意味著,如果沒有客戶端連接請(qǐng)求過(guò)來(lái),服務(wù)端會(huì)一致阻塞在這里。

4、后面的代碼就是通過(guò)套接字socket可以得到輸入輸出流,到此為止,就是I/O的內(nèi)容了。

5、在客戶端這邊,通過(guò)指定的服務(wù)器主機(jī)名和服務(wù)器監(jiān)聽的端口號(hào),得到套接字Socket,這個(gè)時(shí)候就表示服務(wù)端和客戶端的連接已經(jīng)建立了,然后通過(guò)輸入輸出流來(lái)進(jìn)行通信了。

三、半關(guān)閉的socket

  在上面的Demo中,我們是以行作為通信的最小數(shù)據(jù)單位,服務(wù)器端也是逐行進(jìn)行處理的。但是我們?cè)诖蠖鄶?shù)場(chǎng)景下,通信的數(shù)據(jù)單位是多行的,這時(shí)候Socket的輸出流如何表達(dá)輸出的數(shù)據(jù)已經(jīng)結(jié)束?

  在IO學(xué)習(xí)過(guò)程中提到過(guò),如何要表示輸出已經(jīng)結(jié)束,則通過(guò)關(guān)閉輸出流來(lái)實(shí)現(xiàn),但是在socket中是行不通的,因?yàn)殛P(guān)閉socket,會(huì)導(dǎo)致無(wú)法再?gòu)脑搒ocket中讀取數(shù)據(jù)了。為了解決這種問(wèn)題,java提供了兩個(gè)半關(guān)閉的方法:

1、shutdownInput():關(guān)閉該Socket的輸入流,程序還可以通過(guò)該Socket的輸出流輸出數(shù)據(jù)。

2、shutdownOutput():關(guān)閉該Socket的輸出流,程序還可以通過(guò)該Socket的輸入流讀取數(shù)據(jù)。

如果我們對(duì)同一個(gè)Socket實(shí)例先后調(diào)用shutdownInput和shutdownOutput方法,該Socket實(shí)例依然沒有被關(guān)閉,只是該Socket既不能輸出數(shù)據(jù),也不能讀取數(shù)據(jù)。

服務(wù)器端:

1 ServerSocket ss = new ServerSocket(5555); 2 Socket socket = ss.accept(); 3 PrintStream ps = new PrintStream(socket.getOutputStream()); 4 ps.println("服務(wù)器端:開源中國(guó)杭州論壇"); 5 ps.println("服務(wù)器端:杭州G20峰會(huì)"); 6 //關(guān)閉輸出流,表明輸出已經(jīng)結(jié)束 7 socket.shutdownOutput(); 8 //判斷該socket是否關(guān)閉 9 System.out.println(socket.isClosed()); 10 Scanner scan = new Scanner((socket.getInputStream())); 11 while(scan.hasNextLine()) 12 { 13 System.out.println(scan.nextLine()); 14 } 15 scan.close(); 16 socket.close(); 17 ss.close(); 18 19

?客戶端:

1 Socket s = new Socket("localhost", 5555); 2 InputStream is = s.getInputStream(); 3 byte[] buffer = new byte[1024]; 4 int flag = 0; 5 while(-1 != (flag = is.read(buffer,0,buffer.length))) 6 { 7 String str = new String(buffer,0,flag); 8 System.out.print(str); 9 } 10 PrintStream ps = new PrintStream(s.getOutputStream()); 11 ps.println("客戶端:歡迎參加開源中國(guó)論壇"); 12 ps.println("客戶端:歡迎參加G20峰會(huì)"); 13 is.close(); 14 ps.close(); 15 s.close(); 16

執(zhí)行結(jié)果:

???

??

  在服務(wù)器端程序中可以看到,在輸出兩段字符串之后,調(diào)用了shutdownOutput方法,表示輸出已經(jīng)結(jié)束。隨即又去判斷了socket是否關(guān)閉,執(zhí)行的結(jié)果為false,表示socket并未關(guān)閉。

  但是在調(diào)用了這兩個(gè)半關(guān)閉的方法關(guān)閉了輸出輸入流之后,該socket無(wú)法再次打開該輸出流或者輸入流。因此這種場(chǎng)景不適合保持持久通信狀態(tài)的交互使用,只適合一站式的通信協(xié)議.例如http協(xié)議:客戶端連接到服務(wù)器之后,開始發(fā)送數(shù)據(jù),發(fā)送完成之后無(wú)須再次發(fā)送數(shù)據(jù),只需要讀取服務(wù)器響應(yīng)數(shù)據(jù)即可,讀取數(shù)據(jù)完畢之后,該socket連接也被關(guān)閉了。

四、基于UDP協(xié)議的網(wǎng)絡(luò)編程

  前面介紹的socket編程都是基于TCP協(xié)議的,現(xiàn)在來(lái)看下基于UDP協(xié)議的編程,TCP和UDP的區(qū)別在上一章已經(jīng)有過(guò)介紹。

  UDP協(xié)議的主要作用就是完成網(wǎng)絡(luò)數(shù)據(jù)流和數(shù)據(jù)報(bào)之間的轉(zhuǎn)換-----在信息的發(fā)送端,UDP協(xié)議將網(wǎng)絡(luò)數(shù)據(jù)流封裝到數(shù)據(jù)報(bào),然后將數(shù)據(jù)報(bào)發(fā)送出去;在信息的接收端,UDP協(xié)議將數(shù)據(jù)報(bào)轉(zhuǎn)換成實(shí)際數(shù)據(jù)報(bào)內(nèi)容。

1、首先在UDP網(wǎng)絡(luò)編程中沒有服務(wù)器端和客戶端這種說(shuō)法,兩個(gè)socket之間沒有虛擬鏈路,只是接收和發(fā)送數(shù)據(jù)報(bào)文而已。

2、這里面有兩個(gè)重要的類:DatagramSocket 和DatagramPacket。前者是用來(lái)發(fā)送和接收數(shù)據(jù)包的套接字,后者表示數(shù)據(jù)包,每條報(bào)文僅根據(jù)該包中的包含的信息從一臺(tái)機(jī)器 ? ? ? ?路由到另一臺(tái)機(jī)器。

3、DatagramSocket 的兩個(gè)構(gòu)造函數(shù):

? ? ?DatagramSocket():構(gòu)造數(shù)據(jù)報(bào)套接字并將其綁定到本地主機(jī)上任何可用的端口。

? ? ?DatagramSocket(int?port):創(chuàng)建數(shù)據(jù)報(bào)套接字并將其綁定到本地主機(jī)上的指定端口。

? ? ?在我們下面的DEMO中,UDPServerTest類中先發(fā)送數(shù)據(jù)報(bào),使用的是套接字的無(wú)參構(gòu)造器,而UDPClientTest類中先接收數(shù)據(jù)報(bào),必須監(jiān)聽某一個(gè)端口,所以使用的是套接字的有參構(gòu)造器。

4、DatagramPacket:創(chuàng)建的時(shí)候分為接收和發(fā)送兩種

? ? ??DatagramPacket(byte[]?buf, int?length):用來(lái)接收長(zhǎng)度為?length?的數(shù)據(jù)包。

? ? ??DatagramPacket(byte[]?buf, int?length,?InetAddress?address, int?port):用來(lái)將長(zhǎng)度為?length?的包發(fā)送到指定主機(jī)上的指定端口號(hào)。

1 public class UDPServerTest 2 { 3 public static void main(String[] args) throws IOException 4 { 5 DatagramSocket ds = new DatagramSocket(); 6 String str = "hello world"; 7 //構(gòu)造用于發(fā)送的數(shù)據(jù)包,指定主機(jī)和端口號(hào) 8 DatagramPacket packet = new DatagramPacket(str.getBytes(), 9 str.length(), InetAddress.getByName("localhost"), 5555); 10 ds.send(packet); 11 12 //讀取從客戶端發(fā)送過(guò)來(lái)的響應(yīng) 13 byte[] buffer = new byte[1024]; 14 DatagramPacket packet2 = new DatagramPacket(buffer,buffer.length); 15 ds.receive(packet2); 16 String str2 = new String(buffer,0,packet2.getLength()); 17 System.out.println(str2); 18 ds.close(); 19 } 20 } public class UDPClientTest {public static void main(String[] args) throws Exception{DatagramSocket ds = new DatagramSocket(5555);byte[] buffer = new byte[1024];DatagramPacket packet = new DatagramPacket(buffer, buffer.length);ds.receive(packet);String str = new String(buffer, 0, packet.getLength());System.out.println(str);// 接收到數(shù)據(jù)包之后,客戶端返回響應(yīng)回去String str2 = "welcome";DatagramPacket packet2 = new DatagramPacket(str2.getBytes(), str2.length(), packet.getAddress(), packet.getPort());ds.send(packet2);ds.close();} }

執(zhí)行過(guò)程:

?

1、上面的程序中,第一步是服務(wù)器端(暫且以這種叫法來(lái)區(qū)分這兩個(gè)類)創(chuàng)建一個(gè)UDP套接字,沒有指定端口,使用的是系統(tǒng)分配的端口。然后構(gòu)建了一個(gè)數(shù)據(jù)包,包中指定 ? ? ?了目標(biāo)機(jī)器的ip和端口號(hào)。

2、作為客戶端,創(chuàng)建了一個(gè)UDP套接字,并且綁定了端口,如果想要接收到服務(wù)端發(fā)送過(guò)來(lái)的報(bào)文,綁定的端口必須和服務(wù)器端發(fā)送的包中指定的端口一致。

3、客戶端打印了包中的內(nèi)容之后,想要返回一些內(nèi)容回去。這個(gè)時(shí)候,服務(wù)器端的ip和端口號(hào)可以從之前發(fā)送過(guò)來(lái)的數(shù)據(jù)包中獲取。

? ???DatagramPacket packet2 = new DatagramPacket(str2.getBytes(), str2.length(), packet.getAddress(), packet.getPort());

4、在服務(wù)器接收數(shù)據(jù)包的時(shí)候,已經(jīng)不需要再像客戶端創(chuàng)建套接字一樣去綁定端口了,因?yàn)槟壳氨O(jiān)聽的端口和客戶端發(fā)送的包中指定的端口是一樣的。

5、打印看下服務(wù)器端的ip和監(jiān)聽的端口號(hào):

serverIp =/127.0.0.1;serverPort=62965

6、其中DatagramSocket的receive(DatagramPacket?p)方法在接收到數(shù)據(jù)包前一直阻塞。

?

轉(zhuǎn)載于:https://www.cnblogs.com/dongguacai/p/5747603.html

總結(jié)

以上是生活随笔為你收集整理的Java网络编程二:Socket详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。