本文共 5567 字,大约阅读时间需要 18 分钟。
所谓的“三次握手”:为了对每次发送的数据量进行跟踪与协商,确保数据段的发送和接收同步,根据所接收到的数据量而确认数据发送、接收完毕后何时撤消联系,并建立虚连接。
简单来理解就是因为建立的是虚拟连接,所以为了确保客户端和服务端之间的通信而进行的一系列互相确认的过程。
建立连接时,客户端发送syn包(seq=j)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。
SYN_SENT表示请求连接,当你要访问其它的计算机的服务时首先要发个同步信号给该端口,此时状态为SYN_SENT,如果连接成功了就变为ESTABLISHED,此时SYN_SENT状态非常短暂。
服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(seq=k),即SYN+ACK包,此时服务器进入SYN_RECV状态。
SYN_RECV是指服务端被动打开后,接收到了客户端的SYN并且发送了ACK时的状态。再进一步接收到客户端的ACK就进入ESTABLISHED状态。
客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。
ESTABLISHED即表示连接成功。
服务端先经历socket、bind、listen,这是一套标准流程,socket生成并返回一个FD,绑定端口,并开启监听。客户端调用connect便开始了三次握手的过程,注意此时并不需要服务端accept客户端请求。
概念比较生疏,直接先通过一小段代码看一下效果
服务端代码创建socket并绑定9090端口,先通过System.in.read();阻塞,键盘随便输入任何内容,服务端就开始执行accept,接受客户端请求,当收到客户端请求后,交给一个新的线程处理,也就是说服务端只负责处理accept请求。
import java.io.*;import java.net.ServerSocket;import java.net.Socket;public class ServerSocketBIO { public static void main(String[] args) { ServerSocket serverSocket = null; try { serverSocket = new ServerSocket(9090); System.out.println("---服务端启动---"); System.in.read(); while (true) { Socket client = serverSocket.accept(); System.out.println("---接收客户端请求---"); new Thread(() -> { InputStream inputStream; try { inputStream = client.getInputStream(); BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream); byte[] bytes = new byte[1024]; int read; while ((read = bufferedInputStream.read(bytes)) > 0) { String s = new String(bytes, 0, read); System.out.println("收到客户端发来的数据:" + s); } } catch (IOException e) { e.printStackTrace(); } finally { try { client.close(); } catch (IOException e) { e.printStackTrace(); } } }).start(); } } catch (IOException e) { e.printStackTrace(); } finally { try { serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } }}
客户端只管发送数据
import java.io.*;import java.net.Socket;public class SocketBIOCli { public static void main(String[] args) throws Exception { Socket socket = new Socket("localhost", 9090); System.out.println("客户端启动..."); OutputStream outputStream = socket.getOutputStream(); BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream)); BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); while (true) { String readLine = reader.readLine(); bufferedWriter.write(readLine); bufferedWriter.flush(); } }}
1、启动服务端
2、查看网络连接情况,进程ID1864,开始监听9090端口
3、此时9090端口上未抓取到任何数据
4、启动客户端,注意此时服务端处于阻塞状态(System.in.read()),并没有接受客户端连接。
5、三次握手完成
15:52:55.843293 IP 192.168.70.113.41691 > 192.168.70.114.9090: Flags [S], seq 222719304, win 14600, options [mss 1460,sackOK,TS val 8547774 ecr 0,nop,wscale 6], length 015:52:55.843312 IP 192.168.70.114.9090 > 192.168.70.113.41691: Flags [S.], seq 3048072190, ack 222719305, win 14480, options [mss 1460,sackOK,TS val 20217172 ecr 8547774,nop,wscale 7], length 015:52:55.843461 IP 192.168.70.113.41691 > 192.168.70.114.9090: Flags [.], ack 3048072191, win 229, options [nop,nop,TS val 8547774 ecr 20217172], length 0
6、再看服务端连接情况,此时已经完成连接,并且状态为ESTABLISHED,只是还没有分配给具体进程。
7、客户端同时也完成了连接
客户端192.168.70.113.41689,发送 Flags [S]表示SYN包,seq 222719304, win 14600,到服务端地址192.168.70.114.9090。
15:52:55.843293 IP 192.168.70.113.41691 > 192.168.70.114.9090: Flags [S], seq 222719304, win 14600, options [mss 1460,sackOK,TS val 8547774 ecr 0,nop,wscale 6], length 0
服务端接受到请求,发送Flags [S.],多了一个点,这个点就表示 ACK,所以服务端发送了SYN+ACK包,seq 3048072190, ack 222719305, win 14480,到客户端地址,ack = 客户端的seq+1。
15:52:55.843312 IP 192.168.70.114.9090 > 192.168.70.113.41691: Flags [S.], seq 3048072190, ack 222719305, win 14480, options [mss 1460,sackOK,TS val 20217172 ecr 8547774,nop,wscale 7], length 0
最后客户端回复ACK包,ack 3048072191, win 229,ack=服务端的seq+1
15:52:55.843461 IP 192.168.70.113.41691 > 192.168.70.114.9090: Flags [.], ack 3048072191, win 229, options [nop,nop,TS val 8547774 ecr 20217172], length 0
maximum segment size 最大报文段长度(单位:字节),用于在TCP连接建立时,收发双方协商通信时每一个报文段所能承载的最大数据长度(不包含TCP及IP的协议头长度)。
滑动窗口大小,用户动态调节两台主机之间的数据传输。
1、为什么是三次握手,二次握手有什么问题?四次握手有什么问题?
2、如果已经建立了连接,但是客户端突然出现了故障,怎么办?
TCP保活机制,一般都会通过心跳来试探对方是否存活。
3、什么是TCP半连接?
处于SYN_RECV状态的服务端,完成了第二次握手,正在等待客户端回复ACK确认。
4、什么是SYN攻击?
利用三次握手的漏洞,客户端大量的向服务端发送连接请求,但在第三次握手时却不回复服务端ACK,导致服务端中存在大量的半连接请求,耗费CPU和内存资源。
防护手段
5、怎么检测SYN攻击?
查看半连接请求:netstat -natp | grep SYN_RECV
转载地址:http://tolrb.baihongyu.com/