一般编程:在服务器套接字中,accept()方法究竟发生了什么。在低级别,服务器套接字与客户端套接字有何不同?
答案 0 :(得分:28)
在低级别套接字只是套接字,无论它们是在服务器还是客户端应用程序中使用。两者的区别在于系统调用各种应用程序。
服务器套接字将调用bind()
与端口关联。他们希望与端口关联,以便其他程序知道到达哪个端口。客户端套接字可以调用bind()
,但几乎从不这样做,因为没有太多意义。如果套接字没有调用bind()
,操作系统只会为它选择一个短暂的端口,这对客户端来说很好,因为它们正在进行调用;没有人需要来打电话给他们。
服务器套接字调用listen()
。在其他答案中对此进行了很好的解释。
服务器套接字调用accept()
,我认为这是你问题的症结所在,因为它起初有点神秘。需要掌握的重要一点是,在调用accept()
时,内核将传回 new 套接字。它现在与原始侦听套接字分开,是您的服务器用于与其对等方通信的内容。
理解tcp连接依赖于(1)本地地址(2)本地端口(3)外部地址的4元组时,理解监听套接字如何继续监听的关键在于理解tcp连接(4)外国港口。这些定义了唯一的连接。在accept()
传递回新套接字之前,内核使用这些值来创建各种结构,以便与tcp / ip堆栈协作,具有此元组的所有流量将转到连接的套接字。即使您的服务器可能与本地地址192.168.1.100端口80有一千个连接,但地址和端口的客户端组合将始终不同,因此元组始终是唯一的。
答案 1 :(得分:13)
首先,服务器套接字通常绑定到众所周知的名称(在本例中为端口),并且它们与listen()
建立起来。这就是真正的差异发生的地方,因为客户端套接字用connect()
建立自己。在套接字上调用listen()
会导致内核的tcp / ip实现开始接受发送到套接字绑定名称(端口)的连接。无论您是否致电accept()
,都会发生这种情况。
accept()
只是让您的服务器能够访问已连接到侦听套接字的客户端套接字并与之交互。
答案 2 :(得分:10)
如果您真的感兴趣,我会建议您阅读TCP/IP Illustrated, Volume 2。如果你想要一个较少的“胆量”答案,那么:
socket()
系统调用中指定的协议和bind()
系统调用中指定的寻址信息组成。listen()
系统调用时,网络堆栈会创建一个队列,其中放置挂起的连接。对backlog
的{{1}}参数给出了对队列大小的提示。listen()
从队列中提取新连接。accept()
调用中指定服务器端点,然后使用connect()
或send()
发送数据,向服务器套接字发送消息。write()
时,会将连接推送到服务器端的队列,直到服务器接受连接。此描述仅对TCP / IP套接字有效。由于UDP套接字不是(必然)连接,因此UDP的情况更简单且完全不同。
答案 3 :(得分:3)
如果不指定编程语言,很难完全回答您的问题。我会回答关于python的问题,因为据我所知,答案可能具有代表性。
如果s
是socket
对象,则服务器首先通过调用s.bind(('',12345))
绑定到端口。这将在服务器模式下创建套接字。它准备捕获端口12345上的数据。
然后拨打s.listen(10)
。这会将套接字引入服务器模式。这意味着此套接字将接收连接请求(一次最多10个待处理请求)。
当我们到达s.accept()
时,操作系统已经知道我们正在侦听端口12345. s.accept()
仅仅说明了我们将要收到的请求。在python中,s.accept()
将返回(connection,address)
,其中connection是通过另一个端口的连接。在这种情况下,connection
与客户端打开的套接字对象没有太大区别。从现在开始,这是相当对称的。