網路協議之:socket協議詳解之Socket和Stream Socket

flydean發表於2022-03-30

簡介

不管是在普通的網路程式設計中還是在netty中,都經常會提到一個詞叫做socket,好像socket是一個神奇的東西,使用socket我們可以建立客戶端到伺服器端的連線,並且和進行客戶端和伺服器端的通訊,那麼socket到底是什麼呢?它有哪些分類呢?一起來看看吧。

Socket是什麼

socket的中文翻譯是套接字,個人覺的這個翻譯真的是太差勁了,套接字聽起來毫無意義,所以很多人在第一次聽到socket這個詞的時候肯定很迷茫。

那麼什麼是socket呢?socket是一種不同程式間進行程式通訊的方法,這些程式可以在同一個伺服器上也可以在不同的伺服器上。

socket建立連線的基礎是IP協議,IP協議被用來進行資料的封裝和分組,然後才能夠在網路上進行傳輸。這種依賴於IP協議的socket,又叫做network socket。

通過network socket可以建立客戶端和伺服器端的連線,客戶端和伺服器端是通過socket address來發現對方的。

以java為例,我們來看下SocketAddress的定義:

public abstract class SocketAddress implements java.io.Serializable {

    static final long serialVersionUID = 5215720748342549866L;

}

可以看到SocketAddress只是一個籠統的定義,它可以有多種實現,它具體的實現中包含了傳輸協議,比如說是TCP,還是UDP,另外還需要包含一個IP地址和一個連線的埠。

其中IP地址和埠定義了連線的物件,協議定義的是連線方式。

基於不同的協議,可以衍生出不同的型別的sockets。比如依賴於TCP協議的叫做Stream sockets,依賴於UDP協議的叫做Datagram sockets,依賴於local files來進行資料傳輸的叫做Unix Domain Sockets.

接下來我們會在一個unix系統中詳細講解這幾種協議的使用。

在講解詳細的例子之前,我們需要使用到關於網路的命令,他們是ss,nc和socat。

在本文中,我使用的是centOS系統,所以你可以使用下面的命令進行安裝:

yum install iproute2 netcat-openbsd socat

Stream Socket

什麼是Stream Socket呢?從字面上可以看出,這個Socket連線是用來進行流傳輸的,如果要進行流的傳輸,那麼首先就需要建立一個穩定的網路連線,在穩定的連線方面,毫無疑問TCP(Transmission Control Protocol)是最常用也是極其高效的一種協議。

對於Stream Socket來說,它是有向性的,資料package需要從一個地址通過網路傳遞到另外一個地址,同時還需要接受到對方的處理返回結果,在這個過程中通常使用的就是TCP協議。

TCP協議能夠保證資料的穩定性和有序性,TCP的資料包可以保證傳送到物理網路介面的資料包順序。 如果網路介面接收到的資料包是無序的,那麼網路介面卡和作業系統將確保它們以正確的順序重新組合以供應用程式使用。

常見的基於TCP的Stream Socket就是我們常常訪問的http和https服務了,處理http和https服務的伺服器一般是nginx或者apache,那麼通常會有下面的Stream Socket地址:

124.225.141.53:80
124.225.141.53:443

上面我用的是網易的ip地址,其中80表示的是http,443表示的是https。

使用socat建立一個TCP伺服器

常用的TCP伺服器可以選擇apache或者nginx,這裡為了簡單起見,我們選擇使用socat來建立一個TCP伺服器。

socat是什麼呢?它是SOcket CAT的簡稱,可以用來模擬一個TCP的伺服器。

socat的命令很複雜,這裡我們只是簡單介紹一下它的應用:

socat -h
socat by Gerhard Rieger and contributors - see www.dest-unreach.org
Usage:
socat [options] <bi-address> <bi-address>

從上面的結果我們可以看出,socat可以接受一些地址,然後可以新增一些選項。

這裡我們使用socat來建立兩個連線,分別是TCP6和TCP4,socat有兩個選項可以做這項工作:

      tcp-connect:<host>:<port> groups=FD,SOCKET,CHILD,RETRY,IP4,IP6,TCP
      tcp-listen:<port> groups=FD,SOCKET,LISTEN,CHILD,RETRY,RANGE,IP4,IP6,TCP
      tcp4-connect:<host>:<port>        groups=FD,SOCKET,CHILD,RETRY,IP4,TCP
      tcp4-listen:<port>        groups=FD,SOCKET,LISTEN,CHILD,RETRY,RANGE,IP4,TCP
      tcp6-connect:<host>:<port>        groups=FD,SOCKET,CHILD,RETRY,IP6,TCP
      tcp6-listen:<port>        groups=FD,SOCKET,LISTEN,CHILD,RETRY,RANGE,IP6,TCP

這裡我們只需要建立兩個監聽TCP的服務,所以我們使用下面的命令:

socat TCP4-LISTEN:8888,fork /dev/null&
socat TCP6-LISTEN:8888,ipv6only=1,fork /dev/null&

上面的命令,我們在8888埠上監聽TCP4和TCP6的連線資訊,其中fork參數列示程式在接收到程式包之後繼續執行,如果不用fork,那麼程式會自動退出。

socat後面本來要接一個bi-address,這裡我們使用/dev/null,表示丟棄掉所有的income資訊。

TCP6-LISTEN有個特殊的引數叫做ipv6only,表示收到的資料包不要傳送到IPv4-mapped IPv6 addresses。

什麼是IPv4-mapped IPv6 addresses? 簡單點說就是將IPv4對映到了IPv6的地址中。

執行上述命令,我們會得到下面的輸出:

[1] 30877
[2] 30885

因為是後臺執行,所以我們返回了程式的ID。

使用ss檢查TCP連線

ss是一個非常強大的命令,我們可以通過使用ss來監測TCP sockets的資訊,它的使用情況如下:

ss -h
Usage: ss [ OPTIONS ]
       ss [ OPTIONS ] [ FILTER ]

我們主要看下面幾個將要用到的引數:

   -4, --ipv4          display only IP version 4 sockets
   -6, --ipv6          display only IP version 6 sockets
   -t, --tcp           display only TCP sockets
   -l, --listening     display listening sockets
  -n, --numeric       don't resolve service names

因為我們只監聽ipv4和ipv6的資料,所以這裡我們用-4和-6這兩個引數。

另外因為只需要監聽tcp sockets,所以需要使用-t引數。

因為是監聽,所以使用-l引數,最後我們希望看到具體的數字,而不是被解析成了服務名,所以這裡使用-n引數。

我們使用下面的命令看看結果:

ss -4 -tln

結果如下:

State       Recv-Q Send-Q                      Local Address:Port                                     Peer Address:Port              

LISTEN      0      5                                       *:8888                                                *:*  

表示監聽到了埠8888, 當然如果你的伺服器上有其他的程式,那麼可能不止這一條資料。

上面的命令只監聽了Ipv4,我們再看看Ipv6:

ss -6 -tln

可能得到下面的輸出:

ss -6 -tln
State       Recv-Q Send-Q                      Local Address:Port                                     Peer Address:Port              

LISTEN      0      5                                      :::8888                                               :::* 

和Ipv4的很類似,表示我們在Ipv6上監聽到了埠8888。

使用nc連線socket

我們已經建立好了了監聽TCP連線的伺服器,接下來我們嘗試使用nc命令來進行連線。

nc是Ncat的簡稱,它是一個非常有用的網路工具,可以做很多事情。我們來看下本例子中會用到的引數:

  -4                         Use IPv4 only
  -6                         Use IPv6 only
  -v, --verbose              Set verbosity level (can be used several times)
  -z                         Zero-I/O mode, report connection status only

因為需要連線到Ipv4和Ipv6,所以需要-4和-6引數。

另外我們需要輸出詳細的資訊,所以需要-v引數,最後我們直接建立連線,並不傳送任何資料,所以這裡使用-z引數,我們執行一下來看看效果:

nc -4 -vz 127.0.0.1 8888

看看下面的輸出結果:

nc -4 -vz 127.0.0.1 8888
Ncat: Version 7.50 ( https://nmap.org/ncat )
Ncat: Connected to 127.0.0.1:8888.
Ncat: 0 bytes sent, 0 bytes received in 0.01 seconds.

可以看到nc已經成功建立了連線,並且傳送了0 bytes的內容。

同樣的,我們建立到Ipv6的連線:

nc -6 -vz ::1 8888

這裡的 ::1表示的是Ipv6的本地地址。輸出結果如下:

Ncat: Version 7.50 ( https://nmap.org/ncat )
Ncat: Connected to ::1:8888.
Ncat: 0 bytes sent, 0 bytes received in 0.01 seconds.

總結

到此,我們介紹了Socket的基本分類Stream Socket的含義,並且使用unix中的工具搭建了socket伺服器和客戶端,當然這只是最簡單的說明描述,大家用來體會Stream Socket的流程即可。

本文已收錄於 http://www.flydean.com/15-stream-socket/

最通俗的解讀,最深刻的乾貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!

歡迎關注我的公眾號:「程式那些事」,懂技術,更懂你!

相關文章