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


簡介

不管是在普通的網絡編程中還是在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/

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

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM