阻塞socket和非阻塞socket

写操作

  • 阻塞socket:
    一直到发送完所有的数据,写操作才返回
  • 非阻塞socket:
    • 当socket的发送缓冲区中没有空间时,直接返回EWOULDBLOCK错误
    • 当socket的发送缓冲区中有足够的空间 或 不足以拷贝所有待发送数据的空间时,则拷贝前n个能够容纳的字节,并返回实际拷贝的字节数

读操作

  • 阻塞socket:
    • 当socket的接收缓冲区中没数据时,读操作会一直阻塞到有数据流入
    • 当socket的接收缓冲区中的数据量大于期望读取的数据量时,读取期望的字节数,返回实际读取的字节数
    • 当socket的接收缓冲区中的数据量小于期望读取的数据量时,读取全部的数据,返回实际读取的字节数
  • 非阻塞socket:
    • 当socket的接收缓冲区中没有数据时,直接返回EWOULDBLOCK错误
    • 当socket的接收缓冲区中有数据时,和阻塞socket的行为一致

accept

  • 阻塞socket:
    • 当连接队列中没有建立好的连接时,accept将阻塞,直到有可用的连接才返回
    • 当连接队列中有建立好的连接时,accept会从连接队列中弹出第一个连接,并返回一个新的socket,之后服务器端就可以使用这个新的socket和客户端进行通信了
  • 非阻塞socket:
    • 当连接队列中没有建立好的连接时,直接返回EWOULDBLOCK错误
    • 当连接队列中有建立好的连接时,和阻塞socket的行为一致

connect

  • 阻塞socket:
    • 首先发送sync包到服务器,在收到服务器返回的ack+sync包后,connect才返回,否则一直阻塞
  • 非阻塞socket:
    • connect并不等待连接建立好才返回,而是立即返回,返回的错误码是EINPROGRESS

非阻塞connect和epoll

Python例子:

import time  
import socket  
import select

if "EPOLLRDHUP" not in dir(select):  
    select.EPOLLRDHUP = 0x2000
epoll = select.epoll()

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
sock.setblocking(False)

try:  
    sock.connect(("127.0.0.1", 80))
except IOError as ex:  
    if ex.errno != socket.errno.EINPROGRESS:
        raise

epoll.register(sock.fileno(),  
    select.EPOLLIN | select.EPOLLOUT | 
    select.EPOLLERR | select.EPOLLHUP | 
    select.EPOLLRDHUP)

while True:  
    time.sleep(0.02)
    events = epoll.poll()
    if not events:
        continue

    for fd, event in events:
        if event & select.EPOLLIN:
            print "in"
        if event & select.EPOLLOUT:
            print "out"
        if event & select.EPOLLERR:
            print "err"
        if event & select.EPOLLHUP:
            print "hup"
        if event & select.EPOLLRDHUP:
            print "rdhup"
    break
sock.close()  

Posix 定义了两条与 select 和 非阻塞 connect 相关的规定:

  • 当连接建立成功时,socket描述符变为可写
  • 当连接建立失败时,socket描述变为可读、可写、错误、关闭

感谢浏览tim chow的作品!

如果您喜欢,可以分享到: 更多

如果您有任何疑问或想要与tim chow进行交流

可点此给tim chow发信

如有问题,也可在下面留言: