首页 > 代码库 > Python——网络编程,如何避免死锁?

Python——网络编程,如何避免死锁?

问题描述:什么是死锁?

死锁发生在当一个服务器和客户端同时试图往一个连接上写东西或同时从一个连接上读的时候。在这种情况下,没有进程可以得到任何数据(如果它们都正在读),因此,如果它们正在写,向外的buffer会被充满,结果他们就好象被骗了,什么都做不了。

示例服务器代码:

import socket,tracebackhost=""port=51423sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)sock.bind((host,port))sock.listen(1)while True:    try:        clientsock,clientaddr= sock.accept()    except KeyboardInterrupt:        raise    except:        traceback.print_exc()        continue    try:        print("Got connection from ",clientsock.getpeername)        while True:            data = clientsock.recv(4096)            if not len(data):                break            clientsock.sendall(data)    except (KeyboardInterrupt,SystemExit):            raise    except:            traceback.print_exc()    try:        clientsock.close()    except KeyboardInterrupt:        raise    except:        traceback.print_exc()
View Code

示例客户端代码:

import socketimport sysport=51423host="localhost"data=b"x"*10485760sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)sock.connect((host,port))byteswritten=0while byteswritten<len(data):    startpos = byteswritten    endpos = min(byteswritten+1024,len(data))    byteswritten+=sock.send(data[startpos:endpos])    sys.stdout.write("wrote %d bytes\r"% byteswritten)    sys.stdout.flush()sock.shutdown(1)print("All data sent.")while True:    buf = sock.recv(1024).decode()    if not len(buf):        break    sys.stdout.write(buf)
View Code

在运行上述服务器代码的情况下运行客户端代码,得到如下结果:

服务器:(ev1)[root@Simonxu bin]# python testserver.pyGot connection from  <built-in method getpeername of socket object at 0x7f5e18d2b460>客户端:(ev1)[root@Simonxu bin]# python test.pywrote 164864 bytes

可以看出,上述服务器和客户端卡在的wrote 164864 bytes。

-------------------------------------------------------------------分析--------------------------------------------------------------------------------------------

已知客户端程序试图发送一个10MB的数据,每次传输1KB,同时显示发送数据动态,并在所有数据发送完成后,从服务器每次1KB读取并写数据。

而服务器在建立套接口连接后,从客户端每次读取4KB数据,在接收到数据后,直接把数据发回客户端。

而矛盾的是,由于客户端在发送一个10MB大小的数据,这要发送较长时间,在发送的过程中没有办法读取数据,因此服务器返回的数据就堆积在客户端的接收缓冲区。

当接收缓冲区满了之后,服务器的sendall()函数发生错误,循环死锁,服务器不再接收客户端发来的数据。客户端也无法继续发送数据。就形成了上面的情况。

 

------------------------------------------------------------------如何避免死锁?----------------------------------------------------------------------------------

1、可以在客户端每次执行完send()后,进行一次recv()以接收服务器发来的数据,避免buffer充满。

2、可以让客户端发送的数据较少,这样在buffer充满之前就可以从缓冲区读取服务器发回的数据。

3、采用多线程或其他一些方法,使客户端可以同时发送和接收。

-----------------------------------------------------------------知识补充------------------------------------------------------------------------------------------

缓冲区:

1. tcp 收发缓冲区默认值

  [root@ www.linuxidc.com]# cat /proc/sys/net/ipv4/tcp_rmem 
  4096    87380   4161536

  87380  :tcp接收缓冲区的默认值
  [root@ www.linuxidc.com]# cat /proc/sys/net/ipv4/tcp_wmem
  4096    16384   4161536

  16384  : tcp 发送缓冲区的默认值
2. tcp 或udp收发缓冲区最大值


  [root@ www.linuxidc.com]# cat /proc/sys/net/core/rmem_max
  131071

  131071:tcp 或 udp 接收缓冲区最大可设置值的一半。
  也就是说调用 setsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcv_size, &optlen);  时rcv_size 如果超过 131071,那么
  getsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcv_size, &optlen); 去到的值就等于 131071 * 2 = 262142

  [root@ www.linuxidc.com]# cat /proc/sys/net/core/wmem_max 
  131071

  131071:tcp 或 udp 发送缓冲区最大可设置值得一半。
  跟上面同一个道理

3. udp收发缓冲区默认值

  [root@ www.linuxidc.com]# cat /proc/sys/net/core/rmem_default  
  111616:udp接收缓冲区的默认值

  [root@ www.linuxidc.com]# cat /proc/sys/net/core/wmem_default
  111616

  111616:udp发送缓冲区的默认值
4. tcp 或udp收发缓冲区最小值
  tcp 或udp接收缓冲区的最小值为 256 bytes,由内核的宏决定;

  tcp 或udp发送缓冲区的最小值为 2048 bytes,由内核的宏决定

Python——网络编程,如何避免死锁?