使用Wireshark對網絡通信撲捉,進行三次握手和四次揮手原理分析
在網絡的通信的時候,都有聽說過三次握手四次揮手。但是對其原理是否清晰?本篇文章通過使用wireshark對網絡通信撲捉,進行原理分析。
1 BIO代碼實現
- //服務端代碼
- public class ServerSocket {
- public static void main(String[] args) throws Exception {
- //創建ServerSocket對象,用于客戶端的連接
- java.net.ServerSocket serverSocket = new java.net.ServerSocket(8989);
- //定義輸入流對象讀取數據
- byte[] bytes = new byte[1024];
- try {
- while (true) {
- System.out.println("服務端發生阻塞,等待連接....");
- //調用accept方法監聽客戶端,阻塞方法
- Socket accept = serverSocket.accept();
- //調用Socket對象的方法獲取輸入流對象
- InputStream inputStream = ((Socket) accept).getInputStream();
- System.out.println("服務端發生阻塞,等待接收數據....");
- int read = inputStream.read(bytes);
- System.out.println(new String(bytes, 0, read));
- //關閉資源
- accept.close();
- }
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- if (serverSocket != null && !serverSocket.isClosed()) {
- serverSocket.close();
- }
- }
- }
- }
- //客戶端代碼
- public class ClientSocket {
- public static void main(String[] args) throws Exception {
- //創建Socket對象,與服務端Socket建立連接
- Socket socket=new Socket("127.0.0.1",8989);
- //獲取輸出流對象
- OutputStream outputStream=socket.getOutputStream();
- System.out.println("客戶端阻塞,接收鍵盤輸入....");
- //接收鍵盤輸入,模擬延遲消息發送
- Scanner scanner=new Scanner(System.in);
- String scannerString=scanner.next();
- outputStream.write(scannerString.getBytes());
- System.out.println("客戶端錄入完成....");
- //使用輸出流對象寫入數據
- // outputStream.write("itheima-TCP".getBytes());
- //釋放資源
- socket.close();
- }
2 Socket的API(構造方法)
- Socket():無參構造方法。
- Socket(InetAddress address,int port):創建一個流套接字并將其連接到指定 IP 地址的指定端口。
- Socket(InetAddress address,int port,InetAddress localAddr,int localPort):創建一個套接字并將其連接到指定遠程地址上的指定遠程端口。
- Socket(String host,int port):創建一個流套接字并將其連接到指定主機上的指定端口。
- Socket(String host,int port,InetAddress localAddr,int localPort):創建一個套接字并將其連接到指定遠程地址上的指定遠程端口。Socket 會通過調用 bind() 函數來綁定提供的本地地址及端口。
3 Socket的API( 普通方法)
- void bind(SocketAddress bindpoint):將套接字綁定到本地地址。
- void close():關閉此套接字。
- void connect(SocketAddress endpoint):將此套接字連接到服務器。
- InetAddress getInetAddress():返回套接字的連接地址。
- InetAddress getLocalAddress():獲取套接字綁定的本地地址。
- InputStream getInputStream():返回此套接字的輸入流。
- OutputStream getOutputStream():返回此套接字的輸出流。
- SocketAddress getLocalSocketAddress():返回此套接字綁定的端點地址,如果尚未綁定則返回null。
- SocketAddress getRemoteSocketAddress():返回此套接字的連接的端點地址,如果尚未連接則返回 null。
- int getLoacalPort():返回此套接字綁定的本地端口。
- intgetPort():返回此套接字連接的遠程端口
4 WebSocket的API(構造方法)
- ServerSocket():無參構造方法。
- ServerSocket(int port):創建綁定到特定端口的服務器套接字。
- ServerSocket(int port,int backlog):使用指定的 backlog 創建服務器套接字并將其綁定到指定的本地端口。
- ServerSocket(int port,int backlog,InetAddress bindAddr):使用指定的端口、監聽 backlog 和要綁定到本地的 IP 地址創建服務器
5 WebSocket的API(普通方法)
- Server accept():監聽并接收到此套接字的連接。
- void bind(SocketAddress endpoint):將 ServerSocket 綁定到指定地址(IP 地址和端口號)。
- void close():關閉此套接字。
- InetAddress getInetAddress():返回此服務器套接字的本地地址。
- int getLocalPort():返回此套接字監聽的端口。
- SocketAddress getLocalSoclcetAddress():返回此套接字綁定的端口的地址,如果尚未綁定則返回 null。
- int getReceiveBufferSize():獲取此 ServerSocket 的 SO_RCVBUF 選項的值,該值是從ServerSocket 接收的套接字的建議緩沖區大小。
- accept()方法會返回一個和客戶端Socket對象相連的Socket對象。使用Socket的getOutputStream可以向客戶端發送信息。使getIutputStreamke可以獲取客戶端傳過來數據。
6 TCP協議中,建立連接三次握手
1.簡述
在tcp協議中,雙方建立連接的時候是需要三次握手。這個連接建立需要一方主動打開,另外一方被動打開的。下圖為建立連接圖解。
上圖解釋
SYN(建立連接) 請求建立連接,并在其序列號字段進行序列號的初始值設定。建立連接,設置為1。
ACK(acknowledgement 確認) 確認號是否有效,一般置為1
PSH(push傳送) 提示接受端應用程序立即從TCP緩沖區把數據讀走。
FIN(finish結束) 希望斷開連接。
RST(reset重置) 對方要求重新建立連接,復位。
URG(urgent緊急) 緊急指針是否有效。為2,表示某一位被優先處理。
2. 網絡請求建立連接,經歷三次握手流程
a 第一次握手

在第一次"握手"時,客戶端向服務端發送SYN標志位,目的是與服務端建立連接。Seq代表sequence,number(發送數據流序號), 例如:Seq的值是5,說明在數據流中曾經一共發送了 1, 2, 3,4 這4次數據。而在本次"握手"中, Seq的值是0,代表客戶端曾經沒有給服務端發送數據。另外Len=0也可以看出來是沒有數據可供發送的,客戶端僅僅發送一個SYN標志位到服端代表要進行連接。
b 第二次握手
第二次"握手"時,服務端向客戶端發送 SYN ACK 標志位,其中ACK標志位表示是對收到的數據包的確認,說明服務端接收到了客戶端的連接。ACK的值是1,表示服務端期待下一次從客戶端發送數據流的序列號是1,而Seq=0代表服務端曾經并沒有給客戶端發送數據,而本次也沒有發送數據,因為Len=0也證明了這一點。
c 第三次握手
第三次“握手”時,客戶端向服務端發送的ACK標志位為1, Seq的值是1。Seq=l代表這正是服務端所期望的Ack=1。Len=0說明客戶端這次還是沒有向服務端傳遞數據,而客戶端向服務端發送ACK 標志位為1的信息,說明客戶端期待服務端下一次傳送的Seq的值是1。
3. 為什么要進行三次握手
為了防止服務器端開啟一些無用的連接,增加服務器開銷。以及防止已失效的連接請求報文段突然又傳送到了服務端,因而產生錯誤。
7 TCP協議中,連接斷開時候(四次揮手)
1. 四次揮手
即TCP連接的釋放(解除)。連接的釋放必須是一方主動釋放,另一方被動釋放。以下為客戶端主動發起釋放連接的圖解:

簡述流程
- a 客戶端到服務端,我要關了。
- b 服務端到客戶端,好的,我收到了。
- c 服務端到客戶端,我也關了。
- d 客戶端到服務端,好的,收到。
2. 四次揮手執行流程

- a 第一次揮手 在第一次"揮手"時,客戶端到服務器發送標志位FIN ACK,告知服務端客戶端關閉了。Seq=1表示本次數據流的序號為1,Ack=1表示客戶端期望服務端下一次發送的數據流的序號為1。len=0,說明沒有數據傳輸到服務端。
- b 第二次揮手 在第二次"揮手"時,服務端向客戶端發送標志位ACK,Seq=1代表的正是客戶端想看的Ack=1。Ack=2表示服務端期望下一次客戶端發送的數據流的序號為2。len=0,說明沒有數據傳輸到客戶端。
- c 第三次揮手 在第三次"揮手"時,服務端向客戶端發送標志位FIN ACK,告知客戶端服務端關閉了。Seq=1代表的正是客戶端想看的Ack=1。Ack=2表示服務端期望下一次客戶端發送的數據流的序號為2。len=0,說明沒有數據傳輸到客戶端。
- d 第四次揮手 在第四次"揮手"時,客戶端向服務端發送標志位ACK,告知服務端客戶端已經收到服務端關閉信息。Seq=2 代表的正是服務端想看的Ack=2,ACK=2表示客戶端期望下一次服務端發送的數據流的序號為2。
注意 BIO存在問題
1 客戶端已經連接服務端,尚未發送數據,read阻塞
2 新的客戶端無法正常連接
解決辦法
1 線程解決(mysql客戶端連接服務器)
2 線程池解決(線程池泄露)
3 NIO解決
4 websocket