讀懂Java中的Socket編程

2022-01-10 全棧開發者中心

  Socket,又稱為套接字,Socket是計算機網絡通信的基本的技術之一。如今大多數基於網絡的軟體,如瀏覽器,即時通訊工具甚至是P2P下載都是基於Socket實現的。本文會介紹一下基於TCP/IP的Socket編程,並且如何寫一個客戶端/伺服器程序。

 餐前甜點

  Unix的輸入輸出(IO)系統遵循Open-Read-Write-Close這樣的操作範本。當一個用戶進程進行IO操作之前,它需要調用Open來指定並獲取待操作文件或設備讀取或寫入的權限。一旦IO操作對象被打開,那麼這個用戶進程可以對這個對象進行一次或多次的讀取或寫入操作。Read操作用來從IO操作對象讀取數據,並將數據傳遞給用戶進程。Write操作用來將用戶進程中的數據傳遞(寫入)到IO操作對象。當所有的Read和Write操作結束之後,用戶進程需要調用Close來通知系統其完成對IO對象的使用。

  在Unix開始支持進程間通信(InterProcess Communication,簡稱IPC)時,IPC的接口就設計得類似文件IO操作接口。在Unix中,一個進程會有一套可以進行讀取寫入的IO描述符。IO描述符可以是文件,設備或者是通信通道(socket套接字)。一個文件描述符由三部分組成:創建(打開socket),讀取寫入數據(接受和發送到socket)還有銷毀(關閉socket)。

  在Unix系統中,類BSD版本的IPC接口是作為TCP和UDP協議之上的一層進行實現的。消息的目的地使用socket地址來表示。一個socket地址是由網絡地址和埠號組成的通信標識符。

  進程間通信操作需要一對兒socket。進程間通信通過在一個進程中的一個socket與另一個進程中得另一個socket進行數據傳輸來完成。當一個消息執行發出後,這個消息在發送端的socket中處於排隊狀態,直到下層的網絡協議將這些消息發送出去。當消息到達接收端的socket後,其也會處於排隊狀態,直到接收端的進程對這條消息進行了接收處理。

 TCP和UDP通信

  關於socket編程我們有兩種通信協議可以進行選擇。一種是數據報通信,另一種就是流通信。

  數據報通信

  數據報通信協議,就是我們常說的UDP(User Data Protocol 用戶數據報協議)。UDP是一種無連接的協議,這就意味著我們每次發送數據報時,需要同時發送本機的socket描述符和接收端的socket描述符。因此,我們在每次通信時都需要發送額外的數據。

  流通信

  流通信協議,也叫做TCP(Transfer Control Protocol,傳輸控制協議)。和UDP不同,TCP是一種基於連接的協議。在使用流通信之前,我們必須在通信的一對兒socket之間建立連接。其中一個socket作為伺服器進行監聽連接請求。另一個則作為客戶端進行連接請求。一旦兩個socket建立好了連接,他們可以單向或雙向進行數據傳輸。

  讀到這裡,我們多少有這樣的疑問,我們進行socket編程使用UDP還是TCP呢。選擇基於何種協議的socket編程取決於你的具體的客戶端-伺服器端程序的應用場景。下面我們簡單分析一下TCP和UDP協議的區別,或許可以幫助你更好地選擇使用哪種。

  在UDP中,每次發送數據報時,需要附帶上本機的socket描述符和接收端的socket描述符。而由於TCP是基於連接的協議,在通信的socket對之間需要在通信之前建立連接,因此會有建立連接這一耗時存在於TCP協議的socket編程。

  在UDP中,數據報數據在大小上有64KB的限制。而TCP中也不存在這樣的限制。一旦TCP通信的socket對建立了連接,他們之間的通信就類似IO流,所有的數據會按照接受時的順序讀取。

  UDP是一種不可靠的協議,發送的數據報不一定會按照其發送順序被接收端的socket接受。然後TCP是一種可靠的協議。接收端收到的包的順序和包在發送端的順序是一致的。

  簡而言之,TCP適合於諸如遠程登錄(rlogin,telnet)和文件傳輸(FTP)這類的網絡服務。因為這些需要傳輸的數據的大小不確定。而UDP相比TCP更加簡單輕量一些。UDP用來實現實時性較高或者丟包不重要的一些服務。在區域網中UDP的丟包率都相對比較低。

 Java中的socket編程

  下面的部分我將通過一些示例講解一下如何使用socket編寫客戶端和伺服器端的程序。

  注意:在接下來的示例中,我將使用基於TCP/IP協議的socket編程,因為這個協議遠遠比UDP/IP使用的要廣泛。並且所有的socket相關的類都位於java.net包下,所以在我們進行socket編程時需要引入這個包。

  客戶端編寫

  開啟Socket

  如果在客戶端,你需要寫下如下的代碼就可以打開一個socket。


  上面代碼中,host即客戶端需要連接的機器,port就是伺服器端用來監聽請求的埠。在選擇埠時,需要注意一點,就是0~1023這些埠都已經被系統預留了。這些埠為一些常用的服務所使用,比如郵件,FTP和HTTP。當你在編寫伺服器端的代碼,選擇埠時,請選擇一個大於1023的埠。

  寫入數據

  接下來就是寫入請求數據,我們從客戶端的socket對象中得到OutputStream對象,然後寫入數據後。很類似文件IO的處理代碼。


  關閉IO對象

  類似文件IO,在讀寫數據完成後,我們需要對IO對象進行關閉,以確保資源的正確釋放。

  伺服器端編寫

  打開伺服器端的socket


  上面的代碼創建了一個伺服器端的socket,然後調用accept方法監聽並獲取客戶端的請求socket。accept方法是一個阻塞方法,在伺服器端與客戶端之間建立聯繫之前會一直等待阻塞。

  讀取數據

  通過上面得到的socket對象獲取InputStream對象,然後安裝文件IO一樣讀取數據即可。這裡我們將內容列印出來。


  關閉IO對象

  還是不能忘記的,最後需要正確地關閉IO對象,以確保資源的正確釋放。

 附註一個例子

  這裡我們增加一個例子,使用socket實現一個回聲伺服器,就是伺服器會將客戶端發送過來的數據傳回給客戶端。代碼很簡單。


  編譯運行上面的代碼,進行如下請求,就可以看到客戶端請求攜帶的數據的內容。


 總結

  進行客戶端-伺服器端編程還是比較有趣的,同時在Java中進行socket編程要比其他語言(如C)要簡單快速編寫。

  java.net這個包裡面包含了很多強大靈活的類供開發者進行網絡編程,在進行網絡編程中,建議使用這個包下面的API。同時Sun.*這個包也包含了很多的網絡編程相關的類,但是不建議使用這個包下面的API,因為這個包可能會改變,另外這個包不能保證在所有的平臺都有包含。

  原文:Sockets programming in Java: A tutorial 翻譯:droidyue

相關焦點

  • Java socket編程
    一,網絡編程中兩個主要的問題一個是如何準確的定位網絡上一臺或多臺主機,另一個就是找到主機後如何可靠高效的進行數據傳輸。在TCP/IP協議中IP層主要負責網絡主機的定位,數據傳輸的路由,由IP位址可以唯一地確定Internet上的一臺主機。
  • Java Socket應用——通信是這樣煉成的
    (4)Datagram: 使用UDP,將數據保存在數據報中實現網絡通信。二、JAVA中網絡相關API的應用2.1、JAVA中InetAddress的應用// 獲取本機的InetAddress實例InetAddress address = InetAddress.getLocalHost();System.out.println("計算名:" + address.getHostName()); System.out.println
  • 基於Socket的java通信編程詳解
    Java最初是作為網絡程式語言出現的,其對網絡提供了高度的支持,使得客戶端和伺服器的溝通變成了現實,而在網絡編程中,使用最多的就是Socket。像大家熟悉的QQ、MSN都使用了Socket相關的技術。下面就讓我們一起揭開Socket的神秘面紗。
  • Java socket通信基本原理介紹
    Java socket通信基本原理介紹 Java socket通信在不斷的進行相關代碼的開發,下面我們就看看如何才能更好的使用有關技術為我們的編程工作帶來一定的幫助。
  • socket編程基礎,從了解到實現
    java生下來一開始就是為了計算機之間的通信,因此這篇文章也將開始介紹一下java使用socket進行計算機之間的通信,在上一篇文章中已經對網絡通信方面的基礎知識進行了總結,這篇文章將通過代碼案例來解釋說明。
  • 詳解Java網絡編程
    埠和套接字當網絡中的兩臺計算機進行通信時,除了確定計算機在網絡中的IP外,還需要確定計算機中的一個埠,埠並不是實際的物理設備,它是一個應用程式,這個應用程式來負責兩臺計算機的通信。一個IP標識了一臺主機(伺服器),主機可以提供多種服務,如web服務、ftp服務、遠程桌面等。主機的每個服務都會等待客戶端的連接,客戶端如何區別這些服務呢?
  • java web項目中使用Socket通信多線程、長連接
    很多時候在javaweb項目中我們需要用到Socket通信來實現功能,在web中使用Socket我們需要建立一個監聽程序,在程序啟動時,啟動
  • PHP SOCKET編程
    比如聊天室這樣的程序,客戶端連接之間可以交互,比聊天室中的玩家可以任意的其他人發消息。用多線程模式實現非常簡單,線程中可以直接讀寫某一個客戶端連接。而多進程模式就要用到管道、消息隊列、共享內存實現數據交互,統稱進程間通信(IPC)複雜的技術才能實現。代碼實例:
  • 【java網絡】編程基礎
    套接字是兩個主機之間邏輯連結的端點,可以用於發送和接收數據,Java對套接字的處理非常類似於對輸入輸出操作的處理,因此,程序從socket中讀寫就像從文件中讀寫一樣容易Java支持流套接字和數據報套接字:流套接字使用傳輸控制協議TCP進行數據傳輸,而數據報套接字使用的是用戶數據報協議UDP進行數據傳輸1、客戶/
  • Linux 編程之 Socket
    封裝socket編程的基本流程socket編程之bind函數int bind(int sockfd, const struct sockaddr *addr,socklen_t *addrlen);>功能描述:當用socket()函數創建套接字以後,套接字在名稱空間(網絡地址族)中存在,但沒有任何地址給它賦值。
  • Java Socket通訊如何進行客戶端的信息通信
    作者:佚名來源:網際網路|2010-03-18 17:39 Java Socket通訊在實際的使用中有很多關鍵代碼需要我們學習
  • 簡單說說Python Socket編程步驟?
    廢話不多說,開始今天的題目:問:簡單說說Python socket編程步驟?答:Socket是應用層與TCP/IP協議族通信的中間軟體抽象層,它是一組接口。在設計模式中,Socket其實就是一個門面模式,它把複雜的TCP/IP協議族隱藏在Socket接口後面,對用戶來說,一組簡單的接口就是全部,讓Socket去組織數據,以符合指定的協議。所以,我們無需深入理解tcp/udp協議,socket已經為我們封裝好了,我們只需要遵循socket的規定去編程,寫出的程序自然就是遵循tcp/udp標準的。
  • Java異步非阻塞編程的幾種方式
    逆地理接口:通過經緯度獲取這個經緯度所在的省市區縣以及響應的code:curl-i"http://xxx?latitude=31.08966221524924&channel=amap7a&near=false&longitude=105.13990312814713"
  • Python Socket 編程學習筆記
    這裡有個很好的解釋握手協議的文章:https://github.com/jawil/blog/issues/14,我也還在學習中,後面有機會會總結一下。埠:光有IP位址是不夠的,埠是識別一個IP位址下的具體進程,比如,web服務的標準埠是80,SMTP是25,FTP是21。
  • 手把手教你 Socket 通信(TCP/IP)
    建立網絡通信連接至少要一對埠號(socket)。socket 本質是編程接口(API),對TCP/IP的封裝,TCP/IP也要提供可供程式設計師做網絡開發所用的接口,這就是Socket編程接口;HTTP是轎車,提供了封裝或者顯示數據的具體形式;Socket是發動機,提供了網絡通信的能力。
  • Windows編程技術:Socket通信(上)
    Socket翻譯成中文就是「套接字」的意思,所謂的Socket編程就是指用計算機語言通過編程來實現計算機之間的通信問題。Socket通信技術即就是兩臺聯網或者多臺聯網的計算機之間的數據交換技術,這就涉及著通信端的協議等等問題。
  • 為什麼函數式編程在Java中很危險?
    在我的日常工作中,我身邊的開發者大多是畢業於CS編程頂級院校比如MIT、CMU以及Chicago,他們初次涉及的語言是Haskell、Scheme及Lisp。他們認為函數式編程是一種自然的、直觀的、美麗的且高效的編程樣式。
  • C風格的面向對象編程
    面向對象編程(OOP),最早是C++、java等面向對象語言的一個核心特點,之後發展成了一種編程思想。java則在這個基礎上做了裁剪,並且屏蔽了對指針的直接處理,語法更簡化。OOP隨著時間發展成了一種編程思想,也就不再受限於某幾種程式語言了。C風格的面向對象編程,是隨著Linux系統的流行而逐漸流行起來的一種設計模式。最早,在Linux驅動工程師裡被廣泛使用。
  • Linux C語言高級編程之Socket網絡編程!
    Socket網絡編程網絡上的兩個程序通過一個雙向的通信連接實現數據的交換,這個連接的一端稱為一個
  • Java認證:Java編程中實現中文排序
    Java認證:Java編程中實現中文排序  第一種情況:  Comparator cmp = Collator.getInstance(java.util.Locale.CHINA);  String[] arr = { 「張三」, 「李四」, 「王五」, 「劉六」 };