學習網絡編程?進階Netty?Mina? 你得先了解Socket的由來

it派小牧 發佈 2020-07-02T23:41:07+00:00

Socket與TCP/IP的關係七十年代,美國國防部高研署將TCP/IP的軟體提供給加利福尼亞大學Berkeley分校後,TCP/IP很快被集成到Unix中,同時出現了許多成熟的TCP/IP應用程式接口(API),也就是Socket接口。

Socket與TCP/IP的關係

七十年代,美國國防部高研署將TCP/IP的軟體提供給加利福尼亞大學Berkeley分校後,TCP/IP很快被集成到Unix中,同時出現了許多成熟的TCP/IP應用程式接口(API),也就是Socket接口。簡單來說,從用戶的角度來看,這些API接口都是一系列的函數,我們可以使用這些函數進行網絡應用程式的開發。然後從網絡角度看,用戶使用這些函數對應用層發送請求,完成網絡數據傳輸。

主要作用就是實現程序間的通信,相當於RPC,還有文件傳輸、信息交互。

九十年代初,由Microsoft聯合了其他幾家公司共同制定了一套 WINDOWS下的網絡編程接口,即Windows Sockets規範。目前,Windows下的Internet軟體都是基於 WinSock開發的。

Socket

Socket翻譯是套接字的意思,根據上面的介紹,Socket起源於Unix,Unix的模式是「open -> wirte/read ->close」,Socket就是這種模式的實現。看下它在OSI七層所處於的位置。

從圖上可以看出Socket位於應用層和傳輸層之間,它具體的作用是把TCP傳輸層複雜的操作抽象成了 API接口,供應用層去調用的。

再來了解下socket通信的兩個重要概念。

1、埠

網絡中可以被命名和尋址的通信埠,作業系統可分配的,且是每一個主機唯一的。我們開發的服務、使用的開源項目都有的不是嗎。例如tomcat默認8080,mysql 3306,redis 6379等。

從OSI七層協議了解來說,傳輸層提供進程通信能力。網絡通信不單單是尋址,還包括進程標識,所以TCP提供了 Protocol Port 用來標識它。

2、地址

一般網絡通信中的客戶端和服務端都處於兩台機器上,所以網絡上的每台機器都有著其唯一的地址。

來個Socket原理圖(百度來的啊)

這個圖還算挺清晰的。拿生活中最常見的打電話場景來說,喊你朋友來喝酒,先撥號,你朋友聽到電話鈴聲響起接聽,建立通話連接,然後巴拉巴拉的說一堆,講完後掛電話,意味著連接釋放、關閉。

服務端先初始化Socket,與埠進行綁定(bind),然後監聽(listen),再調用 accept阻塞等待客戶端的連接。

客戶端初始化Socket,發起連接(connect),與服務端建立連接後,就可以發起數據請求了,請求完畢後,連接中斷。這個時候客戶端和服務端的一次連接就結束了。

上面這些函數接口都由系統內核去實現了,不需要我們去管。稍微科普下。

  • socket()函數。用於創建一個和標識一個唯一socket。有三個參數,protofamily:協議域。type:socket類型。protocol:指定協議,常用的協議有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等。
  • bind()函數。把一個地址族中的特定地址賦給socket。也有三個參數,sockfd:給唯一socket綁定名稱。addr:指向要綁定給sockfd的協議地址。addrlen:對應地址長度。
  • listen()、connect()函數。就是服務端監聽socket,客戶端調用connect()發起連接請求。
  • accept()函數。TCP伺服器監聽到請求之後,然後調用accept()函數接收請求。
  • read()、write()函數。指的網絡通信連接好後的I/O讀寫操作。

Socket提供三種類型套接字。

  • 流式套接字(SOCK_STREAM)。是最常用的套接字類型,TCP/IP協議族中的 TCP 協議使用此類接口。提供了一個面向連接、無差錯、發送先後順序一致的、無記錄邊界和非重複的網絡信包傳輸。常見的文件傳輸協議(FTP)使用此方式。
  • 數據報式套接字(SOCK_DGRAM)。提供了一個無連接服務。數據包以獨立包形式被發送,不提供無錯保證,數據可能丟失或重複。一下子想到了UDP,網絡文件系統(NFS)使用此方式。
  • 原始式套接字(SOCK_RAW)。常用於開發新的協議實現或訪問現有服務中配置的新設備。

JAVA中要實現Socket通信的主要兩個類是SocketServerSocket類庫,位於java.net包中。ServerSocket用於伺服器端,Socket是建立網絡連接時使用的。一段簡單代碼演示:

//============服務端========//

ServerSocket ss = new ServerSocket(8888); //啟動伺服器

Socket s = ss.accept(); //等待客戶端連接

System.out.println("客戶端:"+s.getInetAddress().getLocalHost()+"已連接到伺服器");

BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));

//讀取客戶端發送來的消息

String mess = br.readLine();

System.out.println("客戶端:"+mess);

BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));

bw.write(mess+"\n");

bw.flush();

//============客戶端========//

Socket s = new Socket("127.0.0.1",8888);

//構建IO

InputStream is = s.getInputStream();

OutputStream os = s.getOutputStream();

BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));

//向伺服器端發送一條消息

bw.write("測試客戶端和伺服器通信,伺服器接收到消息後返回到客戶端\n");

bw.flush();

//讀取伺服器返回的消息

BufferedReader br = new BufferedReader(new InputStreamReader(is));

String mess = br.readLine();

System.out.println("伺服器:"+mess);

Socket的連接(TCP三次握手)

上面說了客戶端通過connect()函數向服務端發起連接請求,這個連接的過程也是挺複雜的。俗稱為三次握手。

第一次握手,客戶端發送syn包(同步序列編號,syn=j)到服務端,然後進入SYN_SEND狀態,等待伺服器回應。

第二次握手,服務端確定客戶端的syn包(ack=j+1),同時自己也發送一個syn包(syn=k)返回給客戶端,此時伺服器進入SYN_RECV 就緒狀態。

第三次握手,客戶端收到伺服器發送的 SYN+ACK 包,向伺服器發送確認包ACK(ack=k+1)完畢後,此時進入連接狀態,三次握手完成,可以進行數據的傳輸了。

這個有興趣的可以通過linux自己來查看下,比如我抓取本地mysql的連接信息。

通過命令:tcpdump -iany tcp port 3306。抓取到的信息結果如下,看到沒 seq,ack,[S],[S.],[p]等。

[S]很明顯表示這是一個SYN請求。

[S.]表示是SYN+ACK確認包。

[P]表示數據推送,不管是服務端推送還是客戶端推送。

這個抓取的信息包含了TCP連接的三次握手流程信息。

四次揮手,TCP連接完後又要釋放不是嗎。。。。。。。。。。。。。。。。我就納悶了,怎麼會這麼麻煩,頭都要搞炸。

關鍵字: