源代码可以在书的作者的github上找到,也可以参考本人GitHub中参考源代码改出来的代码点击打开链接 tcp_deadlock.py
1) 死锁的原因:首先,客户端使用sendall()发送数据块,然后服务器使用recv()来接收、处理,接着将数据转换为大写,并再次使用sendall()调用将结果传回。当传输并不多的数据的时候,可以正常运行,但是传输很大的数据的时候就会出问题。因为,由于客户端要一次性把所有数据发送完才开始接收服务器返回的大写数据,因此在发送结束前客户端不会运行recv()调用。因此,越来越多的数据填满了操作系统的缓冲区,缓冲区就无法接受更多的数据了。
2) 死锁的解决办法:当处理大量数据时,为了避免死锁,有以下两种可能的方案。
第一,客户端和服务器可以通过套接字选项将阻塞关闭。这样一来,send()和recv()这样的调用在得知还不能发送或接收数据时就会立即返回。
第二,程序可以使用某种技术同时处理来自多个输入的数据。可以采用多线程或进程来处理。比如,一个用来像套接字发送数据,另一个可能就负责从套接字读取数据;也可以运行select()或pull()等操作系统调用,使得程序在发送套接字和接收套接字繁忙时等待,当它们之中任何一个空闲时就做出响应。
3) 已关闭连接,半开连接:当没有数据可用的时候,非阻塞套接字的recv()调用就可能会抛出一个异常。在这种情况下,需要一种技术去来确定套接字是否已经关闭。如果服务器在遇到文件结束符之前永远读取数据的话,那么客户端如何避免在套接字上进行完整的close()操作呢?客户端又如何防止运行很多recv()调用来接收服务器的响应呢?
解决方法就是,将套接字“半关”,即在一个方向上永久关闭通信连接,但不销毁套接字。在这种状态下,服务器再也不会读取任何数据,但它仍然能向客户端发送剩余的响应,因为该方向上的连接是没有关闭的。
shutdown()调用可以用来关闭双向套接字中任一方向的通信连接。代码中使用的套接字就是双向套接字。该调用的参数可以是以下三个符号之一。
- SHUT_WR:由于大多数情况下程序都知道自身的输出将于何时结束,但未必知道通信对方的输出何时结束,因此这是最常使用的参数值。SHUT_WR表示调用方将不再向套接字写入数据,而通信对方将不会再读取任何数据并且认为遇到了文件结束符。
- SHUT_RD:该参数用来关闭接收方向的套接字流。如果设置了该值,那么当通信对方尝试发送更多数据时,就会引发文件结束错误。
- SHUT_RDWR:设置该参数表示两个方向的通信都关闭。因为同样可以调用套接字的close()来关闭两个方向的通信,所以这个选项乍一看似乎是无用的。关闭套接字与关闭其两个方向的通信之间的不通电是一个较为高级的区别。如果操作系统允许多个程序共享同一个套接字,而close()仅仅结束了调用它的进程与套接字的关系,此时如果其他程序来调用该套接字的话,该套接字仍然是可用的。而shutdown()方法就不同了,一旦调用shutdown()方法,该套接字对于所有使用它的进程都会立即变得不可用。