Java網絡編程二:Socket詳解

Socket又稱套接字,是連接運行在網絡上兩個程序間的雙向通訊的端點。

一、使用Socket進行網絡通信的過程

服務端:服務器程序將一個套接字綁定到一個特定的端口,并通過此套接字等待和監聽客戶端的連接請求。

客戶端:客戶端程序根據你服務器所在的主機名和端口號發出連接請求。

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

Socket通信示例

主機A的應用程序和主機B的應用程序通信,必須通過Socket建立連接,而建立Socket必須由底層的TCP/IP協議來建立TCP連接。建立TCP連接需要底層IP協議來尋址網絡中的主機。IP地址只能幫助我們找到目標主機,但是一個主機上面有多個應用程序,如何才能找到我們需要的應用程序,這個時候就可以通過端口號來指定了。 

二、簡易服務端、客戶端模擬

服務器端:

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

執行結果:

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     }

執行結果:

您好,您收到了來自服務端的中秋祝福

1、上面展示的是一個簡易的服務端和客戶端通信的建立過程。

2、我們通過交互圖來詳細介紹這個過程:

? ??

3、首先在server端,指定端口號創建serverSocket對象,通過serverSocket的accpet方法獲取套接字,這個方法的特點是:偵聽并接受到此套接字的連接,此方法在連接傳入之前一直阻塞。這也就意味著,如果沒有客戶端連接請求過來,服務端會一致阻塞在這里。

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

5、在客戶端這邊,通過指定的服務器主機名和服務器監聽的端口號,得到套接字Socket,這個時候就表示服務端和客戶端的連接已經建立了,然后通過輸入輸出流來進行通信了。

三、半關閉的socket

  在上面的Demo中,我們是以行作為通信的最小數據單位,服務器端也是逐行進行處理的。但是我們在大多數場景下,通信的數據單位是多行的,這時候Socket的輸出流如何表達輸出的數據已經結束?

  在IO學習過程中提到過,如何要表示輸出已經結束,則通過關閉輸出流來實現,但是在socket中是行不通的,因為關閉socket,會導致無法再從該socket中讀取數據了。為了解決這種問題,java提供了兩個半關閉的方法:

1、shutdownInput():關閉該Socket的輸入流,程序還可以通過該Socket的輸出流輸出數據。

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

如果我們對同一個Socket實例先后調用shutdownInput和shutdownOutput方法,該Socket實例依然沒有被關閉,只是該Socket既不能輸出數據,也不能讀取數據。

服務器端:

 1         ServerSocket ss = new ServerSocket(5555);
 2         Socket socket = ss.accept();
 3         PrintStream ps = new PrintStream(socket.getOutputStream());
 4         ps.println("服務器端:開源中國杭州論壇");
 5         ps.println("服務器端:杭州G20峰會");
 6         //關閉輸出流,表明輸出已經結束
 7         socket.shutdownOutput();
 8         //判斷該socket是否關閉
 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("客戶端:歡迎參加開源中國論壇");
12         ps.println("客戶端:歡迎參加G20峰會");
13         is.close();
14         ps.close();
15         s.close();
16     

執行結果:

???

??

  在服務器端程序中可以看到,在輸出兩段字符串之后,調用了shutdownOutput方法,表示輸出已經結束。隨即又去判斷了socket是否關閉,執行的結果為false,表示socket并未關閉。

  但是在調用了這兩個半關閉的方法關閉了輸出輸入流之后,該socket無法再次打開該輸出流或者輸入流。因此這種場景不適合保持持久通信狀態的交互使用,只適合一站式的通信協議.例如http協議:客戶端連接到服務器之后,開始發送數據,發送完成之后無須再次發送數據,只需要讀取服務器響應數據即可,讀取數據完畢之后,該socket連接也被關閉了。

四、基于UDP協議的網絡編程

  前面介紹的socket編程都是基于TCP協議的,現在來看下基于UDP協議的編程,TCP和UDP的區別在上一章已經有過介紹。

  UDP協議的主要作用就是完成網絡數據流和數據報之間的轉換-----在信息的發送端,UDP協議將網絡數據流封裝到數據報,然后將數據報發送出去;在信息的接收端,UDP協議將數據報轉換成實際數據報內容。

1、首先在UDP網絡編程中沒有服務器端和客戶端這種說法,兩個socket之間沒有虛擬鏈路,只是接收和發送數據報文而已。

2、這里面有兩個重要的類:DatagramSocket 和DatagramPacket。前者是用來發送和接收數據包的套接字,后者表示數據包,每條報文僅根據該包中的包含的信息從一臺機器 ? ? ? ?路由到另一臺機器。

3、DatagramSocket 的兩個構造函數:

? ? ?DatagramSocket():構造數據報套接字并將其綁定到本地主機上任何可用的端口。

? ? ?DatagramSocket(int?port):創建數據報套接字并將其綁定到本地主機上的指定端口。

? ? ?在我們下面的DEMO中,UDPServerTest類中先發送數據報,使用的是套接字的無參構造器,而UDPClientTest類中先接收數據報,必須監聽某一個端口,所以使用的是套接字的有參構造器。

4、DatagramPacket:創建的時候分為接收和發送兩種

? ? ??DatagramPacket(byte[]?buf, int?length):用來接收長度為?length?的數據包。

? ? ??DatagramPacket(byte[]?buf, int?length,?InetAddress?address, int?port):用來將長度為?length?的包發送到指定主機上的指定端口號。

 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         //構造用于發送的數據包,指定主機和端口號
 8         DatagramPacket packet = new DatagramPacket(str.getBytes(),
 9                 str.length(), InetAddress.getByName("localhost"), 5555);
10         ds.send(packet);
11         
12         //讀取從客戶端發送過來的響應
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);// 接收到數據包之后,客戶端返回響應回去String str2 = "welcome";DatagramPacket packet2 = new DatagramPacket(str2.getBytes(), str2.length(), packet.getAddress(), packet.getPort());ds.send(packet2);ds.close();}
}

執行過程:

?

1、上面的程序中,第一步是服務器端(暫且以這種叫法來區分這兩個類)創建一個UDP套接字,沒有指定端口,使用的是系統分配的端口。然后構建了一個數據包,包中指定 ? ? ?了目標機器的ip和端口號。

2、作為客戶端,創建了一個UDP套接字,并且綁定了端口,如果想要接收到服務端發送過來的報文,綁定的端口必須和服務器端發送的包中指定的端口一致。

3、客戶端打印了包中的內容之后,想要返回一些內容回去。這個時候,服務器端的ip和端口號可以從之前發送過來的數據包中獲取。

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

4、在服務器接收數據包的時候,已經不需要再像客戶端創建套接字一樣去綁定端口了,因為目前監聽的端口和客戶端發送的包中指定的端口是一樣的。

5、打印看下服務器端的ip和監聽的端口號:

serverIp =/127.0.0.1;serverPort=62965

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

?

轉載于:https://www.cnblogs.com/dongguacai/p/5747603.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/286637.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/286637.shtml
英文地址,請注明出處:http://en.pswp.cn/news/286637.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Android之提示Could not find com.android.support:appcompat-v7:25.3.1.

1 問題 編譯第三方項目,錯誤提示如下 Could not find com.android.support:appcompat-v7:25.3.1. Required by:project :sampleproject :sample > com.yanzhenjie:permission:1.0.7project :sample > com.yanzhenjie.alertdialog:alertdialog:1.0.1 Search …

在 .NET 中使用 FixedTimeEquals 應對計時攻擊

計時攻擊 在計算機安全中,計時攻擊(Timing attack)是旁道攻擊 (Side-channel attack) 的一種,而旁道攻擊是根據計算機處理過程發出的信息進行分析,包括耗時,聲音,功耗等…

解讀大型網站系統架構的演化

解讀大型網站系統架構的演化 大型網站的架構是根據業務需求不斷完善的,根據不同的業務特征會做特定的設計和考慮,本文只是講述一個常規大型網站會涉及的一些技術和手段。作者:李平來源:LEE的博客前言 一個成熟的大型網站&#xff…

【ArcGIS風暴】西北地區氣象臺站年均NDVI與年均氣溫和降水的相關性分析

在研究植被動態變化與氣候的關系時,通常通過計算植被NDVI值與氣溫和降水的相關系數來描述相關性的大小。如下圖所示,計算了西北地區分布的氣象臺站與氣溫和降水的相關性并作圖可視化。 下面詳細說明整個實現過程。 一、計算相關系數 1. 原理分析 通過計算年均N…

python 全解坦克大戰 輔助類 附完整代碼【雛形】

我正在博客之星評選,歡迎投票給我 會從投票人中抽獎機械鍵盤書,中了會私聊地址 投票連接是:https://bbs.csdn.net/topics/603955346 投票連接是:https://bbs.csdn.net/topics/603955346 投票連接是:https://bbs.csdn.…

vc++ mfc中拖動效果的實現 借助于CImageList

拖動是界面編程頻繁使用的一個效果,在windows系統下可謂大行其道。縱觀時下的應用軟件幾乎各個都支持各種各樣拖動的效果,windows7更是把拖動做到了極致。其實說起來拖動的實現也很簡單,對于有句柄的對象都可以通過MoveWindow或SetWindowPos實…

從瀏覽器地址欄輸入url到顯示頁面的步驟

從瀏覽器地址欄輸入url到顯示頁面的步驟(以HTTP為例)- 在瀏覽器地址欄輸入URL- 瀏覽器查看緩存,如果請求資源在緩存中并且新鮮,跳轉到轉碼步驟 - 如果資源未緩存,發起新請求 - 如果已緩存,檢驗是否足夠新鮮,足夠…

有序的Map集合--LinkedHashMap

提出問題: 在寫一個dao的時候,我的需求是這個dao是一個萬能的,目前的方法只有一個查詢出實體類對應的表中所有的數據,通過傳入的對象,利用反射獲取實體類中的屬性名,屬性類型,利用字符串拼接獲取…

Leetcode之僅僅反轉字母

1 題目 給定一個字符串 S,返回 “反轉后的” 字符串,其中不是字母的字符都保留在原地,而所有字母的位置發生反轉。 示例 1:輸入:"ab-cd" 輸出:"dc-ba" 示例 2:輸入:"a-bC-dEf-ghIj" 輸出:"j-Ih-gfE-dCba" 示例 3:輸入:&qu…

【SignalR全套系列】之在.Net Core 中實現長輪詢

微信公眾號:趣編程ACE關注可了解更多的.NET日常實戰開發技巧,如需源碼 請后臺留言 源碼;前文回顧【SignalR全套系列】之在.Net Core 中實現Server-Send Events消息推送【SignalR全套系列】之在.NetCore中實現WebSocket雙工通信【SignalR全套系列】之在.N…

完美解決ArcGIS10.2和Erdas9.2軟件沖突的方法:共存!

用過ArcGIS和Erdas軟件的都知道,二者存在著服務的沖突,為了解決共存的問題,筆者曾多次重裝系統,但未能如愿以償。其實不需要相互關閉服務如此麻煩。那如何在安裝有arcgis的電腦安裝erdas并解決沖突呢?直接上視頻教程。…

架構師之路16年精選50篇

2016精選索引,點擊標題閱讀相關文章。【方法論】《秒殺系統架構優化思路》《分布式ID生成器》《互聯網架構,如何進行容量設計》《線程數究竟設多少合理》《單點系統架構的可用性與性能優化》《關于負載均衡的一切》《異構服務器負載均衡及過載保護》《LV…

yii 前后臺分離及登陸驗證

2019獨角獸企業重金招聘Python工程師標準>>> 比較合理的做法其實是分成兩個框架來布署;然而這樣說法也太絕對。 事實上是針對不同系統,應采用不同的方法。如為CMS系統,則不需很復雜的權限管理,但如果有管理員與會員之分…

雙谷人才財務管理(序)

IT是一個迅速發展的行業,教育是一個良心的行業,兩者交集——IT培訓,在整個市場中占有一個不可或缺的地位。好多大學生,抱怨找不到工作;好多企業找不到合適的程序員,這幾年這個問題越來越嚴重。。。。于是IT…

Android之打開手機系統相冊

1、需求 打開系統相冊,獲取圖片進行掃描操作 2、代碼實現 Intent pickIntent new Intent(Intent.ACTION_PICK,MediaStore.Images.Media.EXTERNAL_CONTENT_URI);pickIntent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");Ca…

Kibana

2019獨角獸企業重金招聘Python工程師標準>>> Kibana是一個基于瀏覽器頁面的Elasticsearch前端展示工具。Kibana全部使用HTML語言和Javascript編寫的,查詢語法是基于Lucene的查詢語法。允許布爾運算符、通配符和字段篩選。注意關鍵字要大寫 全文搜索 在搜…

電腦QQ能登上,網頁打不開的解決辦法

打開360衛士,在功能大全里選擇網絡優化,斷網急救箱。 可以看到DNS服務異常、瀏覽器配置異常,點擊立即修復。

個人黑名單 抄襲恥辱墻

抄襲恥辱墻 博主抄襲文原文我愛Python數據挖掘https://blog.csdn.net/weixin_38037405/article/details/125862095https://blog.csdn.net/A757291228/article/details/117464313黃埔數據分析https://blog.csdn.net/weixin_39060517/article/details/118024847https://blog.csdn…

快速掌握 ASP.NET 身份認證框架 Identity(一)

推薦關注「碼俠江湖」加星標,時刻不忘江湖事ASP.NET Core 內置了一個強大的身份認證框架 Identity,掌握它可以讓我們快速開發高安全的身份認證功能,不僅如此,它還是一個基于數據庫的用戶管理系統,其中包含了大量的輔助…

淺談大型web系統架構

動態應用,是相對于網站靜態內容而言,是指以c/c、php、Java、perl、.net等服務器端語言開發的網絡應用軟件,比如論壇、網絡相冊、交友、BLOG等常見應用。動態應用系統通常與數據庫系統、緩存系統、分布式存儲系統等密不可分。   大型動態應用…