何时/为啥使用 s.shutdown(socket.SHUT_WR)?

Posted

技术标签:

【中文标题】何时/为啥使用 s.shutdown(socket.SHUT_WR)?【英文标题】:When/why to use s.shutdown(socket.SHUT_WR)?何时/为什么使用 s.shutdown(socket.SHUT_WR)? 【发布时间】:2016-05-08 21:53:15 【问题描述】:

我刚开始学习python网络编程。我正在阅读 Python 网络编程基础,无法理解 s.shutdown(socket.SHUT_WR) 的用法,其中 s 是套接字对象。 这是使用它的代码(其中 sys.argv[2] 是用户想要发送的字节数,四舍五入为 16 的倍数):

import socket, sys
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

HOST = '127.0.0.1'
PORT = 1060

if sys.argv[1:] == ['server']:
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.bind((HOST, PORT))
    s.listen(1)
    while True:
        print 'Listening at', s.getsockname()
        sc, sockname = s.accept()
        print 'Processing up to 1024 bytes at a time from', sockname
        n = 0
        while True:
            message = sc.recv(1024)
            if not message:
                break
            sc.sendall(message.upper())  # send it back uppercase
            n += len(message)
            print '\r%d bytes processed so far' % (n,),
            sys.stdout.flush()
        print
        sc.close()
        print 'Completed processing'

elif len(sys.argv) == 3 and sys.argv[1] == 'client' and sys.argv[2].isdigit():

    bytes = (int(sys.argv[2]) + 15) // 16 * 16  # round up to // 16
    message = 'capitalize this!'  # 16-byte message to repeat over and over

    print 'Sending', bytes, 'bytes of data, in chunks of 16 bytes'
    s.connect((HOST, PORT))

    sent = 0
    while sent < bytes:
        s.sendall(message)
        sent += len(message)
        print '\r%d bytes sent' % (sent,),
        sys.stdout.flush()

    print
    s.shutdown(socket.SHUT_WR)

    print 'Receiving all the data the server sends back'

    received = 0
    while True:
        data = s.recv(42)
        if not received:
            print 'The first data received says', repr(data)
        received += len(data)
        if not data:
            break
        print '\r%d bytes received' % (received,),

    s.close()

else:
    print >>sys.stderr, 'usage: tcp_deadlock.py server | client <bytes>'

这是作者提供的解释,我觉得很难理解:

其次,您将看到客户端在完成发送传输后对套接字进行了shutdown() 调用。这解决了一个重要问题:如果服务器将永远读取直到它看到文件结尾,那么客户端如何避免必须对套接字执行完整的 close() 并因此禁止自己执行许多 recv( ) 调用它仍然需要进行以接收服务器的响应?解决方案是“半关闭”套接字——也就是说,永久关闭一个方向的通信,但不破坏套接字本身——这样服务器就不能再读取任何数据,但仍然可以将任何剩余的回复发送回另一个方向,仍然是开放的。

我对它的理解是,它会阻止客户端应用程序进一步发送数据,因此也会阻止服务器端进一步尝试读取任何数据。

我无法理解的是,为什么在这个程序中使用它,在什么情况下我应该考虑在我的程序中使用它?

【问题讨论】:

close vs shutdown socket?的可能重复 我不明白为什么要使用它。我知道它的作用。 【参考方案1】:

我对它的理解是它会阻止客户端 应用程序进一步发送数据,因此也将阻止 服务器端不再尝试读取任何数据。

你的理解是正确的。

我无法理解的是为什么在这个程序中使用它......

正如您自己的陈述所暗示的,如果没有客户端的s.shutdown(socket.SHUT_WR),服务器将不会退出等待数据,而是永远坚持其sc.recv(1024),因为不会向服务器发送连接终止请求。 由于服务器将永远无法到达其sc.close(),因此客户端也不会退出等待数据,而是永远坚持其s.recv(42),因为服务器不会发送连接终止请求。 阅读this answer to "close vs shutdown socket?" 也可能会有所启发。

【讨论】:

【参考方案2】:

解释是半生不熟的,它适用于这个特定的代码,总的来说,我会全力以赴地投票认为这是不好的做法。

现在要了解为什么会这样,您需要查看服务器代码。该服务器通过阻塞执行工作,直到它接收到 1024 个字节。收到后,它会处理数据(使其大写)并将其发回。现在问题在于硬编码值 1024。如果您的字符串短于 1024 字节怎么办?

要解决此问题,您需要告诉服务器 - 没有更多数据传来,所以从 message = sc.recv(1024) 返回,然后您通过关闭一个方向的套接字来做到这一点。

您不想完全关闭套接字,因为那样服务器将无法向您发送回复。

【讨论】:

这只是一个示例代码,不会在任何地方使用。服务器在收到 1024 个字节之前不会阻止执行,相反 sc.recv(1024) 会返回它收到的字节数,即使它们小于 1024(只要它们大于 0)。 recv 将阻塞直到它得到一些东西。您不能保证它会收到您发送的完整字符串。只接收到第一个字符就可以返回 是的,我在上面写了同样的东西。

以上是关于何时/为啥使用 s.shutdown(socket.SHUT_WR)?的主要内容,如果未能解决你的问题,请参考以下文章

测试[ip:端口]是否打开

何时使用 socket.io ?以及何时使用简单的 Http 调用? [复制]

Socket.io 如何判断某人何时离开

为啥/何时应该使用静态声明变量?

为啥以及何时使用重组分支?

何时以及为啥要使用 Java 同步? [复制]