1.connect函数调用会激发TCP三路握手协议,调用失败返回以下三种情况:
1.TCP客户端没有收到SYN分节,返回ETIMEOUT错误。如果发送一个SYN分节,等待6s没有响应,再发送一个,等待24s没有响应,再发送一个,等待75s没有响应就返回本 错误。
2.如果对客户端SYN的响应是RST(表示复位),则表示该服务器主机没有进程在等待与之连接(1.服务器上没有监听套接字,2.服务器主动取消了与客户端的连接,3.接收到一个不存在的连接上的分节),返回ECONNREFUSED(硬错误)
3.如果客户端发出的SYN分节在中间某个路由器返回“destination unreachable”,持续发出SYN分节,75S后仍热未收到响应,返回EHOSTUNREACH 或者ENETUNREANCH
2.listen(int sockfd, int backlog);socket套接字建立时,默认是主动连接,调用了listen函数后由主动连接变为被动,指示内核应接收指向该套接字的连接,套接字状态由CLOSED 变为LISTEN状态。
内核为sockfd指向的套接字维护两个队列。一个未完成队列(incomplete connection dequeue),一个已完成队列(completed connection dequeue),两个队列之和等于backlog值。每当connect函数发起syn包时,系统在未完成队列创建一个新项然后响应三路握手的第二个分节,服务器的syn响应,同时捎带客户端的syn的ack,这个新建项一直保存到第三个分节到达或者超时。如果三路握手完成,就从未完成队列移到已完成队列队尾,当进程调用accpet时,把已完成队列的对头返回给进程,出于未完成队列的项状态为SYNC_RECVED,已完成队列的状态为ESTABLISHED。
3.close()函数会使套接字描述符引用计数减一,shutdown()产生一些列的Fin终止连接。
4.
1.TCP连接的建立
- 三次握手建立连接时,发送方再次发送确认的必要性
-
- 主要是为了防止已失效的连接请求报文段突然又传到了B,因而产生错误。假定出现一种异常情况,即A发出的第一个连接请求报文段并没有丢失,而是在某些网络结点长时间滞留了,一直延迟到连接释放以后的某个时间才到达B,本来这是一个早已失效的报文段。但B收到此失效的连接请求报文段后,就误认为是A又发出一次新的连接请求,于是就向A发出确认报文段,同意建立连接。假定不采用三次握手,那么只要B发出确认,新的连接就建立了,这样一直等待A发来数据,B的许多资源就这样白白浪费了。
- 四次挥手释放连接时,等待2MSL的意义
-
- 第一,为了保证A发送的最有一个ACK报文段能够到达B。这个ACK报文段有可能丢失,因而使处在LAST-ACK状态的B收不到对已发送的FIN和ACK报文段的确认。B会超时重传这个FIN和ACK报文段,而A就能在2MSL时间内收到这个重传的ACK+FIN报文段。接着A重传一次确认。
- 第二,就是防止上面提到的已失效的连接请求报文段出现在本连接中,A在发送完最有一个ACK报文段后,再经过2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。
状 态 | 描 述 |
CLOSED | 关闭状态,没有连接活动或正在进行 |
LISTEN | 监听状态,服务器正在等待连接进入 |
SYN RCVD | 收到一个连接请求,尚未确认 |
SYN SENT | 已经发出连接请求,等待确认 |
ESTABLISHED | 连接建立,正常数据传输状态 |
FIN WAIT 1 | (主动关闭)已经发送关闭请求,等待确认 |
FIN WAIT 2 | (主动关闭)收到对方关闭确认,等待对方关闭请求 |
TIMED WAIT | 完成双向关闭,等待所有分组死掉 |
CLOSING | 双方同时尝试关闭,等待对方确认 |
CLOSE WAIT | (被动关闭)收到对方关闭请求,已经确认 |
LAST ACK | (被动关闭)等待最后一个关闭确认,并等待所有分组死掉 |
TCP建立与释放的变迁如图所示:
- 客户进程变迁的过程(粗实线)
-
- 连接建立:设一个主机的客户进程发起连接请求(主动打开),这时本地TCP实体就创建传输控制快(TCB),发送一个SYN为1的报文,进入SYN_SENT状态。当收到来自进程的SYN和ACK时,TCP就发送出三次握手中的最后一个ACK,进而进入连接已经建立的状态ESTABLISHED。
- 连接释放:设运行客户进程主机本地TCP实体发送一个FIN置为1的报文,等待着确认ACK的到达,此时状态变为FIN_WAIT_1。当运行客户进程主机收到确认ACK时,则一个方向的连接已经关闭。状态变成FIN_WAIT_2。当运行客户进程的主机收到运行服务器进程的主机发送的FIN置为1的报文后,应响应确认ACK时,这是另一个连接关闭。但此时TCP还要等待一段时间后才删除原来建立的连接记录。返回到初始的CLOSED状态,这是为了保证原来连接上的所有分组都从网络中消失了。
- 服务器进程变迁的过程(粗虚线)
-
- 连接建立:服务器进程发出被动打开,进入监听状态LISTEN。当收到SYN置为1的连接请求报文后,发送确认ACK,并且报文中的SYN也置为1,然后进入SYN_RCVD状态。在收到三次握手最后一个确认ACK时,就转为ESTABLISHED状态。
- 连接释放:当客户进程的数据已经传送完毕。就发出FIN置为1的报文给服务器进程,进入CLOSE_WAIT状态。服务器进程发送FIN报文段给客户进程,状态变为LAST_ACK状态。当收到客户进程的ACK时,服务器进程就释放连接。删除连接记录。回到原来的CLOSED状态。
5.Accept函数返回前连接终止
三路握手完成连接建立后,客户端却发来一个RST(复位),在服务器端看来,就在连接该由TCP排队,等着服务器进程调用Accept时RST到达,Accept返回ECONNABORTED,这时服务器忽略就行,再次调用Accept函数。
6.服务器进程终止(进程崩溃)
服务器进程崩溃后,会首先发送Fin到客户端,客户端read()函数返回EOF,客户端返回ACK和SNC,由于服务器进程已经崩溃,客户端会收到RST消息,这时套接字变为可读,read()函数返回-1,errno里面有错误码。
7.服务器主机崩溃
客户端TCP持续重传分节,试图从服务器上获得一个ACK,会在9分钟内重传12次,然后放弃。在read()函数返回错误ETIMEOUT,如果中间路由器出了问题,会返回EHOSTUNREACH或者EHOSTUNREACH。如果在重传期间服务器重启了,这时服务器由于丢失了所有连接信息,返回一个RST,客户端收到后read()返回ECONNRESET错误。
8.close()与shutdown()不同
1.close()函数调用后,套接字引用计数减一,只有当引用计数为0时,才会关闭套接字。shutdown()不管套接字计数激发TCP正常连接终止序列。
2.close()读和写同时关闭,shutdown()可控。
9.socket选项 KeepAlive
给一个TCP选项设置保持存活后,如果2小时内在该套接字任一方都没有数据交换,TCP就自动给对端发送一个保持存活探测分节,这是一个对端必须响应的分节,会导致如下三种情况:
1.对端以期望的ack响应,在又经过无动静的2小时后,TCP发送另一个分节。
2.对端以RST响应,告知本端,对端已崩溃,返回错误ECONNRESET,套接字本身关闭。
3.对端对保持存活探测分节没有任何响应,TCP继续发送8个分节,11分15秒后放弃。
10.Linger