天天看点

非阻塞套接字

普通套接字的缺陷

非阻塞套接字
非阻塞套接字

普通套接字一次只能服务一个客户端,而且若无客户端连接,accept()一直阻塞,无数据传输,recv一直阻塞,传输效率极低。

普通服务器的IO模型

非阻塞套接字

操作系统监听用户进程的状态,若无数据,则一直等待,知道用户请求数据,操作系统才进行数据处理。

普通套接字实现的服务端的瓶颈

  • 在没有新的套接字来之前, 不能处理已经建立连接的套接字的请求(多个客户端连接只有一个被服务)。
  • 在没有接受到客户端请求数据之前, 不能与其他客户端建立连接!

非阻塞套接字与普通套接字的区别

In [34]: import socket

In [35]: socket_server = socket.socket() 

In [36]: socket_server.setblocking(False) #将scoket设置为非阻塞 注意:必须在bind和listen之 
                                           前!

In [37]: socket_server.bind(('', 8087))

In [38]: socket_server.listen(5)

In [39]: socket_server.accept() #没有连接就会引发BlockingIOError异常
---------------------------------------------------------------------------
BlockingIOError                           Traceback (most recent call last)
<ipython-input-39-68b225acfa4a> in <module>()
----> 1 socket_server.accept()

c:\users\admin\appdata\local\programs\python\python36-32\lib\socket.py in accept(self)
    203         For IP sockets, the address info is a pair (hostaddr, port).
    204         """
--> 205         fd, addr = self._accept()
    206         # If our type has the SOCK_NONBLOCK flag, we shouldn't pass it onto the
    207         # new socket. We do not currently allow passing SOCK_NONBLOCK to

BlockingIOError: [WinError 10035] 无法立即完成一个非阻止性套接字操作。
           
#客户端连接
In [40]: conn, addr = socket_server.accept() #有连接则正确返回

In [41]: conn.recv(1024) #没有数据发送就会引发BlockingIOError异常
---------------------------------------------------------------------------
BlockingIOError                           Traceback (most recent call last)
<ipython-input-41-3b8d10a9535d> in <module>()
----> 1 conn.recv(1024)

BlockingIOError: [WinError 10035] 无法立即完成一个非阻止性套接字操作。
           

非阻塞IO模型

非阻塞套接字

操作系统在处理第一个客户端套接字的数据传输服务时,仍可处理其他客户端的请求,而不是一直阻塞在第一个客户端。

使用非阻塞套接字实现并发

     上节的套接字只能处理一个客户端请求,非阻塞套接字则能实现多个客户端的同时连接并传输数据。

     方法:设置监听套接字和客户端套接字为非阻塞,使用客户端套接字队列,遍历队列进行数据传输

服务端

import socket
sock = socket.socket() #生成套接字
sock.setblocking(False) #设置非阻塞
sock.bind(('0.0.0.0',8800)) #绑定
sock.listen(5) #设置监听数量
conn_que=[] #客户端套接字队列
while True: #循环查询是否有客户端连接
    try:
      conn, addr = sock.accept() #有连接则设置非阻塞,加入套接字队列
      print(addr, '链接')
      conn.setblocking(False)
      conn_que.append(conn)
    except Exception as e: #无连接pass继续查询
        pass
    new_que=[i for i in conn_que] #复制套接字队列到新列表,以免后续的删除操作错误(可以试一下队 
                                   列删除操作)
    for con in new_que: #查询每个客户端的数据传输
      try:
          date = con.recv(1024)
          if date:
              print(date)
              con.send(date)
          else:
              print('断开连接', con)
              con.close()
              conn_que.remove(con)
      except Exception as e:
          pass



           

客户端

import socket

client = socket.socket()

client.connect(('127.0.0.1',8800))

while True:
    data = input('please write the message:')
    client.send(data.encode())
    print(client.recv(1024).decode())
    if data == 'Q' or data == 'q':
        break

client.close()
           

运行结果

非阻塞套接字
非阻塞套接字