zoukankan      html  css  js  c++  java
  • 《网络编程实战》笔记 | 11 优雅地关闭还是粗暴地关闭?

    0 前言

    TCP 连接是双向的,分写入和读取两个方向。回顾 TCP 的四次挥手,在连接关闭发起方 A 发出FIN报文后,A 进入FIN_WAIT1 阶段,当对端 B 收到 FIN 报文后,B 进入 CLOSE_WAIT 状态,此时 A->B 发送方向的连接就释放了,后面只有 B->A 发送数据,A 只是应答。这时连接处于半关闭(half-close)状态。之所以有半关闭状态的存在,是因为 B 可能还有一些数据处理,需要将处理结果发送给 A。等处理结束,B 再去关闭 B->A 方向的连接。这种关闭方式是“优雅地”,也是我们从计算机网络这本书里学到的TCP连接关闭方式。但是具体到代码实现,我们就会发现还有“粗暴地”关闭情况存在,“半关闭状态”虽然存在,但是应用程序用不起来。

    1 “粗暴地” close 函数

    close 函数是我们在学习 socket 编程时,最常见地关闭套接字的接口函数。实际上这个函数是对套接字引用计数减一,当引用计数为 0,就会对套接字彻底释放,并且会关闭 TCP 两个方向的数据流。不直接关闭的原因是套接字可以被多个进程共享,若通过fork的方式产生子进程,套接字引用计数+1,调用一次close则-1。
    具体关闭方式:
    1. 输入方向:系统内核设置套接字为不可读,任何读操作都会返回异常
    2. 输出方向: 系统内核将发送缓冲区数据发送对端,最后发送一个FIN报文,再对套接字写操作会返回异常。
    对端若还继续发送报文,会收到一个RST报文。很粗暴,若客户端调用close发起连接关闭,服务器处理数据结果是没法再发送给客户端的。

    2 “优雅地” shutdown 函数

    shutdown 函数原型如下:

    #include <sys/socket.h>
    int shutdown(int sockfd, int how);
    

    其中 how 是设置选项,有三个主要选项:

    • SHUT_RD(0):关闭读方向,读操作直接返回EOF。接收缓冲区已有数据直接丢弃,新数据到达,则正常ACK,然后丢弃数据。对端不会感知到数据被丢弃。
    • SHUT_WR(1):关闭写方向,“半关闭”的状态,写操作报错。写入缓冲区的数据会被立即发送出去,并发送一个FIN报文给对端。
    • SHUT_RDWR(2):相当于执行了SHUT_RD和SHUT_WR。

    3 close 与 shutdown 的区别

    1. close 会关闭连接,释放所有对应资源,而shutdown 不会释放掉套接字和所有的资源
    2. close 存在引用计数的概念,并不一定会导致套接字不可用;shutdown 则直接使套接字不可用,如果有别的进程使用该套接字,会受到影响。
    3. close 的引用计数不一定会发出 FIN 报文,而shutdown 总使发出 FIN 报文。

    注意:close 后再收到报文,会返回 RST 包。当再次向收到 RST 包的套接字写入数据,会产生 SIGPIPE 信号,系统默认处理是退出进程。

  • 相关阅读:
    Web服务器的Socket通信
    Love story
    Excel初探索 - 2008/3
    One Person Game ZOJ Problem Set 3329
    只有程序员才看得懂的情书
    Collecting Bugs_poj2096
    Scout YYF I_poj3744
    hdu 3336 Count the string
    hdu 1669 Jamie's Contact Groups
    poj 1240
  • 原文地址:https://www.cnblogs.com/1million/p/14339857.html
Copyright © 2011-2022 走看看