在深入探讨UNIX网络编程时,我们关注的核心是TCP/IP连接的生命周期,特别是涉及套接字关闭的细节。在TCP连接的生命周期中,关闭连接通常涉及四个阶段,这被称为"四次挥手"(Four-Way Handshake)过程。下面将详细讨论相关知识点。
1. 主动关闭(Active Close):当某进程决定关闭连接时,它会调用`close()`函数,向对端发送一个FIN(Finish)分节,表明该进程不再发送数据。此时,套接字仍然可以接收数据,进入FIN_WAIT_1状态。
2. 被动关闭(Passive Close):当对端接收到FIN分节,它会回应一个ACK(Acknowledgement)分节,确认数据传输结束。同时,这个FIN会被传递给接收方的应用进程,作为文件结束符EOF。接收方进入CLOSE_WAIT状态,表示不再接收新数据,但可以发送数据。
3. 应用程序关闭(Application Close):一段时间后,接收EOF的应用进程调用`close()`关闭它的套接字,导致发送一个FIN分节,进入LAST_ACK状态。这意味着应用进程不再发送数据,但等待对端的确认。
4. 确认关闭(Acknowledged Close):原始发送FIN的一方接收到这个FIN,确认并发送一个ACK,连接正式终止,双方进入TIME_WAIT或CLOSED状态。
在UNIX系统中,`close()`函数的默认行为是将套接字标记为"已关闭",并立即返回给调用线程。套接字描述符不可再用于read或write操作,但TCP会尝试发送任何已排队的未发送数据,然后按照TCP的正常终止流程进行。
`shutdown()`函数提供了一个更精细的控制方式,它可以避免`close()`的一些限制。`shutdown()`可以分别关闭套接字的读或写通道,或者同时关闭两者。例如:
- `SHUT_RD`:关闭读取,不再接收数据,已接收的数据会被丢弃。
- `SHUT_WR`:关闭写入,执行半关闭,允许接收但不允许发送。TCP会发送剩余数据后跟着FIN。
- `SHUT_RDWR`:同时关闭读写,相当于连续调用`SHUT_RD`和`SHUT_WR`。
`SO_LINGER`套接字选项允许我们控制`close()`在面向连接协议(如TCP)上的行为。默认情况下,`close()`立即返回,但如果有未发送的数据,系统会尝试发送。`SO_LINGER`选项提供了以下三种情况:
- `l_onoff=0`:关闭linger选项,遵循默认行为。
- `l_onoff!=0`且`l_linger=0`:立即终止连接,发送RST分节,不进入TIME_WAIT状态。
- `l_onoff!=0`且`l_linger!=0`:设置延迟时间,等待所有数据发送并确认,或直到延迟时间到达。
如果在数据发送和确认前延迟时间结束,`close()`可能会返回EWOULDBLOCK错误,特别是在非阻塞套接字上。
理解TCP连接的关闭过程以及如何使用`close()`和`shutdown()`控制套接字的生命周期,对于进行高效的UNIX网络编程至关重要。正确地管理套接字状态和数据传输可以帮助确保网络通信的可靠性。