优雅关闭以及如何检测对端已经关闭
发布日期:2021-05-08 05:59:40 浏览次数:21 分类:精选文章

本文共 3197 字,大约阅读时间需要 10 分钟。

优雅关闭socket在Linux网络编程中的应用

在Linux网络编程中,优雅关闭socket是一个非常重要的操作。它能够确保双方的数据传输完成后,连接能够有序地关闭,避免数据丢失或连接异常状态。以下将详细介绍优雅关闭的概念、实现方法以及如何检测对端是否已关闭。

优雅关闭的概念

优雅关闭(Graceful Shutdown)是指在网络通信中,某一方在完成所有数据传输后,按照预定的流程关闭连接,而不是立即断开。这对于多进程环境中的网络通信尤为重要,特别是在父进程和子进程之间的通信中。例如,在父进程和子进程之间建立连接后,子进程可能需要在完成数据传输后关闭套接字并退出,而父进程则需要在接收到EOF信号后关闭连接。这种优雅的关闭过程能够避免因未发送数据而导致的连接异常。

优雅关闭的场景

  • 多进程环境下的网络通信

    在多进程并发环境中,父进程和子进程之间建立连接。子进程完成数据传输后,需要关闭套接字并退出。为了确保服务端能够接收到EOF信号,直接调用close()函数是不够的,因为这只会释放套接字资源,不会触发服务端的EOF处理逻辑。因此,需要使用优雅关闭的方法。

  • 保持连接的一端主动关闭

    在某些情况下,一端(如客户端或服务器)需要在发送完所有数据后关闭连接。为了确保数据能够完整传输,优雅关闭能够保证对方能够读取到所有数据,并在适当的时机关闭连接。

  • 优雅关闭的实现

    在Linux系统中,优雅关闭一个套接字可以通过两种主要方法实现:shutdown()函数和setsockopt()函数中的SO_LINGER选项。

    使用shutdown函数

    shutdown函数定义

    shutdown()函数用于关闭套接字,并指定在关闭时的模式。函数定义如下:

    #include 
    int shutdown(int s, int how);

    how参数有三种取值:

    • SHUT_RD(0):关闭套接字的接收数据功能。关闭一方不会再接收数据,但另一方仍然可以发送数据。
    • SHUT_WR(1):关闭套接字的发送数据功能。关闭一方不会再发送数据,但另一方仍然可以接收数据。
    • SHUT_RDWR(2):关闭套接字的发送和接收数据功能。两方都无法再进行数据传输。

    使用shutdown实现优雅关闭

    在优雅关闭的场景中,通常使用SHUT_WR模式关闭套接字。这样可以确保服务端能够读取到EOF信号,从而停止接收数据。以下是一个示例代码:

    #include 
    #include
    #include
    int main() { int s; // 创建socket if ((s = socket(PF_INET, SOCK_STREAM, 0)) == 0) { return -1; } // 绑定地址 // 假设地址和端口正确 bind(s, (socklen_t)(offsetof(struct sockaddr, sa_family_name) + 2 * sa_port_name), sizeof(sockaddr)); // listen listen(s, 5); // 等待连接 int new_socket; if ((new_socket = accept(s, (socklen_t)offsetof(struct sockaddr, sa_family_name) + 2 * sa_port_name)) == -1) { return -1; } // 发送数据 char buffer[] = "Hello, world!"; send(new_socket, buffer, strlen(buffer), 0); // 优雅关闭 shutdown(new_socket, SHUT_WR); close(new_socket); return 0;}

    shutdown与close的区别

    • close()函数:仅释放套接字资源,不会触发连接的关闭逻辑。其他进程仍然可以通过套接字进行读写操作。
    • shutdown()函数:根据how参数切断套接字的读写能力。例如,SHUT_WR模式下,发送方会收到SIGPIPE信号,接收方会接收到EOF。

    需要注意的是,shutdown()后仍需要调用close()函数来释放套接字资源。

    使用so_linger选项

    SO_LINGER选项允许应用程序指定在close()时等待的时间,以确保发送缓冲区中的数据能够被发送完毕。struct linger结构体定义如下:

    struct linger {    int l_onoff; // 是否启用延迟    int l_linger; // 延迟时间(以秒为单位)};

    设置SO_LINGER选项的代码示例:

    #include 
    #include
    #include
    int main() { int s; if ((s = socket(PF_INET, SOCK_STREAM, 0)) == -1) { return -1; } struct linger ling; ling.l_onoff = 1; ling.l_linger = 10; // 设置延迟时间为10秒 if (setsockopt(s, SOL_SOCKET, SO_LINGER, (char *)&ling, sizeof(ling))) { return -1; } // 发送数据 char buffer[] = "This is a test message."; send(s, buffer, strlen(buffer), 0); // 关闭套接字 close(s); return 0;}

    SO_LINGER选项的设置方式有三种:

  • l_onoff为0:关闭延迟功能,close()会立即返回,不会等待发送缓冲区中的数据。
  • l_onoff为1且l_linger为0close()会发送一个RST包,丢弃发送缓冲区中的数据。
  • l_onoff为1且l_linger为非0close()会等待发送缓冲区中的数据完全发送完成,之后进行四次挥手。
  • 需要注意的是,当close()返回时,可能需要处理EWOULDBLOCK错误,表明发送缓冲区中的数据尚未发送完成。

    检测对端是否已关闭

    在网络编程中,检测对端是否已关闭是非常重要的。可以通过以下方法实现:

  • 使用read返回值

    当读取数据时,如果read()返回0且errnoEAGAIN,说明对端已关闭。

  • 使用心跳包机制

    定期发送心跳包,监测对端是否还在线。如果长时间未接收到心跳包,说明连接已断开。

  • 使用getsockopt函数

    检查套接字的连接状态。例如,getsockopt(s, SOL_SOCKET, SO_ERROR)可以返回错误状态。

  • 通过以上方法,开发者可以有效检测对端是否已关闭,从而在必要时进行优雅关闭或重建连接。

    总结

    优雅关闭是网络编程中确保连接有序关闭的重要操作。通过使用shutdown()函数和SO_LINGER选项,开发者可以实现数据完整传输和连接优雅关闭。在实际应用中,应根据具体场景选择合适的优雅关闭方式,并结合read()getsockopt()等函数进行对端状态检测,确保网络通信的稳定性和可靠性。

    上一篇:epoll的基本使用
    下一篇:如何将socket设置成非阻塞的,非阻塞socket与阻塞的socket在收发数据上的区别

    发表评论

    最新留言

    路过按个爪印,很不错,赞一个!
    [***.219.124.196]2025年03月21日 08时21分22秒