簡介
上一篇文章我們講解了Socket的分類和最常用到的Stream Socket,Stream Socket一般是基於TCP協議的,所以我們經常在web服務中能夠看到他們的身影。當然TCP協議有個孿生兄弟叫做UDP,那麼基於UDP來做傳輸協議的socket協議就叫做Datagram Socket,今天我們一起來詳細瞭解一下Datagram Socket。
什麼是Datagram Socket
和有連線的Stream Socket不同,Datagram Socket是無連線的。有連線的Stream Socket表明這個socket是穩定可靠的,所以我們可以在Stream socket中進行穩定的資料傳輸,當然這個穩定是說資料包不會丟失,但是並不一定能夠確保資料包不被篡改。
Datagram Socket這種無連線的通常被用在容許資料部分丟失的場景,比如語音、視訊等等,無連線的好處就是不需要TCP那樣複雜的建立連線的步驟,所以相對而言更加簡單。
Datagram Socket通常使用的就是UDP協議作為底層的資料傳輸協議。
對於UDP來說,因為UDP協議本身並不會保證資料的順序和資料異常的處理,這些都需要在應用程式中自己實現。
常見的UDP應用有DNS(Domain Name System)服務,NTP(Network Time Protocol)服務等等。
在JDK的java.net包中提供了對Datagram Socket的封裝,在其中定義了三個連線的狀態:
class DatagramSocket implements java.io.Closeable {
...
static final int ST_NOT_CONNECTED = 0;
static final int ST_CONNECTED = 1;
static final int ST_CONNECTED_NO_IMPL = 2;
...
}
分別表示沒有建立連線,建立了連線和建立了連線,但是還沒有到實現的level。
另外,在DatagramSocket中還包含了一個連線的地址和埠:
InetAddress connectedAddress = null;
int connectedPort = -1;
使用socat來建立UDP服務
注意,在使用後續的命令之前,需要在unix環境中執行安裝命令:yum install iproute2 netcat-openbsd socat
和之前的Stream Socket一樣,我們也可以使用socat命令,來建立一個UDP伺服器,我們需要用到socat的下面幾個引數:
udp4-listen:<port> groups=FD,SOCKET,LISTEN,CHILD,RANGE,IP4,UDP
udp6-listen:<port> groups=FD,SOCKET,LISTEN,CHILD,RANGE,IP6,UDP
我們需要監聽udp4和udp6的資料,所以這裡使用 udp4-listen和udp6-listen兩個引數。
後面的埠號可以自定義,這裡我們還是使用同樣的8888埠,對應的命令如下:
socat UDP4-LISTEN:8888,fork /dev/null&
socat UDP6-LISTEN:8888,ipv6only=1,fork /dev/null&
上面的命令,我們在8888埠上監聽UDP4和UDP6的連線資訊,其中fork參數列示程式在接收到程式包之後繼續執行,如果不用fork,那麼程式會自動退出。
socat後面本來要接一個bi-address,這裡我們使用/dev/null,表示丟棄掉所有的income資訊。
UDP6-LISTEN有個特殊的引數叫做ipv6only,表示收到的資料包不要傳送到IPv4-mapped IPv6 addresses。
什麼是IPv4-mapped IPv6 addresses? 簡單點說就是將IPv4對映到了IPv6的地址中。
執行上述命令,我們會得到下面的輸出:
[1] 16174
[2] 16184
因為是後臺執行,所以我們返回了程式的ID。
使用ss命令來監控Datagram Sockets
ss命令可以用來檢查socket的狀態,這裡我們需要用到ss的這樣幾個引數:
-4, --ipv4 display only IP version 4 sockets
-u, --udp display only UDP sockets
-l, --listening display listening sockets
-n, --numeric don't resolve service names
因為我們只監聽ipv4和ipv6的資料,所以這裡我們用-4和-6這兩個引數。
另外因為只需要監聽udp sockets,所以需要使用-u引數。
因為是監聽,所以使用-l引數,最後我們希望看到具體的數字,而不是被解析成了服務名,所以這裡使用-n引數。
我們使用下面的命令看看結果:
ss -4 -uln
可以得到下面的結果:
State Recv-Q Send-Q Local Address:Port Peer Address:Port
UNCONN 0 0 *:8888 *:*
上面的命令只監聽了Ipv4,我們再看看Ipv6:
ss -6 -uln
可以得到下面的結果:
State Recv-Q Send-Q Local Address:Port Peer Address:Port
UNCONN 0 0 :::8888 :::*
和Ipv4的很類似,表示我們在Ipv6上監聽到了埠8888。
使用nc建立和UDP Socket的連線
我們已經建立好了了監聽UDP連線的伺服器,接下來我們嘗試使用nc命令來進行連線。
nc是Ncat的簡稱,是一個非常小並且高效的網路工具。我們來看下本例子中會用到的引數:
-4 Use IPv4 only
-6 Use IPv6 only
-u, --udp Use UDP instead of default TCP
-v, --verbose Set verbosity level (can be used several times)
-z Zero-I/O mode, report connection status only
因為需要連線到Ipv4和Ipv6,所以需要-4和-6引數。
預設情況下nc使用的是TCP協議,如果要使用udp則需要使用-u這個引數。
另外我們需要輸出詳細的資訊,所以需要-v引數,最後我們直接建立連線,並不傳送任何資料,所以這裡使用-z引數,我們執行一下來看看效果:
nc -4 -u -vz 127.0.0.1 8888
看看下面的輸出結果:
Ncat: Version 7.50 ( https://nmap.org/ncat )
Ncat: Connected to 127.0.0.1:8888.
Ncat: UDP packet sent successfully
Ncat: 1 bytes sent, 0 bytes received in 2.02 seconds.
表示UDP連線成功。
同樣的,我們可以使用下面的命令來連線到UDP socket:
nc -6 -u -vz ::1 8888
其中::1表示的是本機的ipv6地址.
可以得到下面的結果:
Ncat: Version 7.50 ( https://nmap.org/ncat )
Ncat: Connected to ::1:8888.
Ncat: UDP packet sent successfully
Ncat: 1 bytes sent, 0 bytes received in 2.02 seconds.
表示UDP連線成功。
總結
本文講解了datagram socket的基本概念,並且使用一些unix的基本命令來構建了udp伺服器和客戶端,方便大家理解。
本文已收錄於 http://www.flydean.com/16-datagram-socket/
最通俗的解讀,最深刻的乾貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!
歡迎關注我的公眾號:「程式那些事」,懂技術,更懂你!