今天在课堂上发布了关于套接字编程中的API设计的问题。
为什么listen()和accept()作为不同的函数提供,而不是合并到一个函数中?
据我所知,listen标记已连接的套接字已准备好接受连接并在传入连接的队列上设置最大边界。如果将accept和listen合并,是否可以维护这样的队列?
还是有其他解释吗?
提前致谢。
答案 0 :(得分:4)
listen()
表示“开始倾听客户”accept()
表示“接受客户端,阻止,直到必要时连接”分离这两个是有意义的,因为如果它们被合并,那么单个合并的函数将被阻塞。这会导致非阻塞I / O程序出现问题。
例如,让我们选择一个想要侦听新客户端连接的典型服务器,同时监控新消息的现有客户端连接。像这样的服务器通常使用非阻塞I / O模型,因此不会在任何一个特定套接字上阻塞它。所以它需要一种在服务器套接字上“开始监听”而不会阻塞它的方法。一旦启动了对服务器套接字的监听,服务器套接字就会被添加到通过select()
监控的套接字桶中(在某些系统上称为poll()
)。 select()
调用将指示服务器套接字上是否有客户端挂起。然后程序可以调用accept()
,而不用担心在该套接字上阻塞。
答案 1 :(得分:1)
listen(2)
使TCP套接字成为服务器套接字,即创建一个队列来接受来自客户端的连接请求。只有侦听端口和可能的IP地址绑定(因此您需要在listen(2)
之前调用bind(2)
)。 accept(2)
然后实际从该队列获取此类连接请求并将其转换为连接的套接字(双向通信所需的四个部分 - 源IP地址,源端口号,目标IP地址,和目标端口号 - 已分配)。 listen(2)
仅被调用一次,而accept(2)
通常被多次调用。
答案 2 :(得分:1)
在引擎盖下,bind
为套接字描述符分配地址和端口。这意味着端口现在为该套接字保留,因此系统将无法将同一端口分配给另一个应用程序(存在例外,但我不会在此处详述)。它也是一次性的单插槽操作。
然后listen
负责确定可以为给定套接字描述符排队的连接数,并指示您现在愿意接收连接。
另一方面,accept
用于从挂起连接队列中取出第一个连接,并创建一个新的套接字来处理通过它的进一步通信。它可以被多次调用,通常是。默认情况下,如果队列中没有连接,则此操作将被阻止。
现在假设您要使用异步IO机制(如epoll,poll,kqueue,select等)。如果listen和accept是单个API,那么如何指示给定套接字是否愿意接收连接?异步机制需要知道你也希望处理这种类型的事件。
语义完全不同,将它们分开是有道理的。