服务器初始化
- 调用socket,创建文件描述符。
- 调用bind,将当前的文件描述符和ip/port绑定在一起,如果这个端口已经被其他进程占用了,就会bind失败。
- 调用listen,声明当前这个文件描述符作为一个服务器的文件描述符,为后面的accept做好准备。
- 调用accept,并阻塞,等待客户端连接过来。
建立连接的过程
- 调用socket,创建文件描述符。
- 调用connect,向服务器发起连接请求。
- 第一次握手:客户端调用connect,发出SYN段并阻塞等待服务器应答。此时的报文段不能携带数据,但要消耗掉一个序号。
- 第二次握手:服务器收到客户端的SYN,会应答一个SYN-ACK段表示“同意建立连接”。
- 第三次握手:客户端收到SYN-ACK后会从connect()返回,同时应答一个ACK段。ACK报文段可以携带数据,不携带数据则不消耗序号。
为啥要第三次握手?
-
第一次握手目的:客户端发包,服务端收到了。这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。
-
第二次握手目的:服务端发包,客户端收到了。这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接收能力和自己的发送能力是否正常。
-
第三次握手目的:客户端发包,服务端收到了。这样服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常。
-
因此,需要三次握手才能确认双方的接收与发送能力是否正常。
-
为了防止已失效的连接请求报文段突然传送到了服务器,服务器以为是客户端发送的新的连接请求,只要服务器发出确认,新的连接就确认了,而客户端不会理睬服务器的确认,也不会发送数据,这导致服务器资源白白浪费。
三次握手过程中可以携带数据吗?
- 第三次握手的时候,是可以携带数据的。但是,第一次、第二次握手不可以携带数据
- 第一次握手不可以放数据,原因就是会让服务器更加容易受到攻击了。
- 而第三次,此时对于客户端来说,他已经建立起连接了,并且也已经知道服务器的接收、发送能力是正常的了,所以能携带数据。
数据传输的过程
- 建立连接后,TCP协议提供全双工的通信服务; 所谓全双工的意思是, 在同一条连接中, 同一时刻, 通信双方可以同时写数据; 相对的概念叫做半双工, 同一条连接在同一时刻, 只能由一方来写数据。
- 服务器从accept()返回后立刻调用read(), 读socket就像读管道一样, 如果没有数据到达就阻塞等待。
- 这时客户端调用write()发送请求给服务器, 服务器收到后从read()返回,对客户端的请求进行处理, 在此期间客户端调用read()阻塞等待服务器的应答。
- 服务器调用write()将处理结果发回给客户端, 再次调用read()阻塞等待下一条请求。
- 客户端收到后从read()返回, 发送下一条请求,如此循环下去。
断开连接的过程
- 第一次挥手:客户端调用close()关闭连接, 客户端会向服务器发送FIN段。
- 第二次挥手:此时服务器收到FIN后, 会回应一个ACK, 同时read会返回0 。
- 第三次挥手:read返回之后, 服务器就知道客户端关闭了连接, 也调用close关闭连接, 这个时候服务器会向客户端发送一个FIN。
- 第四次挥手:客户端收到FIN, 再返回一个ACK给服务器。客户端进入TIME_WAIT(时间等待)状态。此时TCP未释放掉,需要经过时间等待计时器设置的时间2MSL后,客户端才进入CLOSED状态。
挥手为什么需要四次?
- 关闭连接时,当服务端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉客户端,“你发的FIN报文我收到了”。只有等到我服务端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四次挥手。
四次挥手释放连接时,等待2MSL的意义?
- (1)保证客户端发送的最后一个ACK报文段能够到达服务端。
- 这个ACK报文段有可能丢失,使得处于LAST-ACK状态的B收不到对已发送的FIN+ACK报文段的确认,服务端超时重传FIN+ACK报文段,而客户端能在2MSL时间内收到这个重传的FIN+ACK报文段,接着客户端重传一次确认,重新启动2MSL计时器,最后客户端和服务端都进入到CLOSED状态,若客户端在TIME-WAIT状态不等待一段时间,而是发送完ACK报文段后立即释放连接,则无法收到服务端重传的FIN+ACK报文段,所以不会再发送一次确认报文段,则服务端无法正常进入到CLOSED状态。
- (2)防止“已失效的连接请求报文段”出现在本连接中。
- 客户端在发送完最后一个ACK报文段后,再经过2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失,使下一个新的连接中不会出现这种旧的连接请求报文段。