在网络编程中,接收端必须关闭两个套接字吗?

3

我之前提出了一个问题,其中一个答案涉及到另一个问题。

也就是说,一般情况下,接收端应该创建两个套接字来建立接收端。像这样:

Python

from socket import *
sockfd = socket(...)
# ...
client_sockfd, addr = sockfd.accept()
# ...
client_sockfd.close()
sockfd.close()

C

int sockfd, client_sockfd;
sockfd = socket(...);
// ...
client_sockfd = accept(sockfd, ...);
// ...
shutdown(client_sockfd, 2);
shutdown(sockfd, 2);
close(client_sockfd);
close(sockfd);

那么我们可以跳过创建client_sockfd变量的任务吗?像这样:

Python

sockfd = socket(...)
# ...
sockfd, addr = sockfd.accept()
# ...
sockfd.close()

C

int sockfd;
struct sockaddr_in server, client;
socklen_t client_size = sizeof(client);
sockfd = socket(...);
// ...
sockfd = accept(sockfd, (struct sockaddr *)&client, &client_size);

或者是这样的:

Python

sockfd = socket(...)
# ...
client_sockfd, addr = sockfd.accept()
sockfd.close()
# ...
client_sockfd.close()

C

int sockfd = socket(...);
int client_sockfd;
// ...
client_sockfd = accept(sockfd, ...);
shutdown(sockfd, 2);
close(sockfd);
// ...
shutdown(client_sockfd, 2);
close(client_sockfd);

如上代码所示,我们是否可以只使用一个套接字来完成整个网络编程的接受端?这样做有什么问题吗?(至少我自己编写程序时没有遇到任何问题)


1
当你说“接收端”时,是指“接受端”吗?在大多数情况下,两端都进行接收和传输。 - ctrl-alt-delor
是的,我会修改问题。 - S-N
4个回答

3

你总是创建两个套接字:服务器套接字和第一个被接受的客户端套接字。何时关闭它们取决于你。

在Python中使用相同的名称仍会关闭服务器套接字(最终),因为对象引用计数归零,但在C中会泄漏套接字。


2
那我们可以跳过创建client_sockfd变量的任务吗?
对于像TCP这样的面向连接的协议,不行。在接收端涉及到的两个套接字是不同类型且具有不同目的,都是必需的。
在C语言中,通过socket()函数创建并与accept()函数一起使用的套接字通常称为“服务器套接字”。它的工作是等待传入的连接请求,并在接受一个请求时,准备与该请求的远程对等体进行通信的端点。通过成功调用accept()获得第二个(第三个、第四个……)套接字表示结果端点。
您使用服务器套接字来接受连接,可能同时拥有多个活动连接,并使用每个其他套接字(如果您喜欢,则为对等套接字)与特定客户端进行通信。关闭对等套接字会终止与关联远程对等体的连接。关闭服务器套接字会使系统停止在其地址/端口接受新连接。
您提出了这种替代方案:
int sockfd;
struct sockaddr_in server, client;
socklen_t client_size = sizeof(client);
sockfd = socket(...);
// ...
sockfd = accept(sockfd, (struct sockaddr *)&client, &client_size);

那并不是技术上的错误,但是一旦你将accept()的结果赋值给变量sockfd,你就无法再访问服务器套接字了(假设你没有将其文件描述符存储在另一个变量中,这将使问题无效)。这是资源泄漏。此外,它还会阻止您接受指定地址/端口的任何后续连接。直到进程终止(如果有的话),您甚至无法为该端口创建新的服务器套接字,因为现有的套接字将妨碍此操作。


所有这些都与数据报导向的套接字不同 - 没有与accept()相关的连接,并且对于此类协议,服务器套接字和对等方套接字之间没有固有的区别。但根据您的示例代码中accept()调用的外观,这不是您所询问的内容。

你(和许多其他人)所称呼的“服务器套接字”,更好地称为“监听套接字”(更准确地说是“被动套接字”)。另一端是“连接套接字”(技术上称为“主动套接字”)。 - ctrl-alt-delor
@ctrl-alt-delor,我承认有多种套接字的名称。但是我认为我与其他许多人一样使用“服务器套接字”这个术语的事实,反驳了您使用不同术语会更好的说法。 - John Bollinger
我突然明白了如何在网络编程中实现多线程套接字,而你的话让我意识到了这一点。 - S-N
@JohnBollinger,术语的问题在于当您向服务器添加一个活动(连接)套接字和向客户端添加一个被动(监听)套接字时。 - ctrl-alt-delor

0

我不确定是否存在“问题”,但似乎需要解释一下这里发生了什么:

当您调用sockfd.accept()时,它会阻塞直到有一个连接进来。当有一个连接进来时,它会返回另一个套接字,该套接字“代表”该连接,并可用于在连接上发送和接收数据。多次调用socket.close()的原因是,在您的第一个示例中,client_sockfd.close()关闭连接。您仍然可以调用sockfd.accept()再次接收另一个连接,许多套接字服务器在while循环中使用socket.accept()。调用sockfd.close()将关闭sockfd,通常是在不需要进行进一步的连接(对sockfd.accept()的调用)时。


0

在你的第一个例子中

from socket import *
sockfd = socket(...)
# ...
client_sockfd, addr = sockfd.accept()
# ...
client_sockfd.close()
sockfd.close()

sockfd 是监听器的描述符。它可以被重复使用。 在关闭 client_sockfd 后,您可以再次执行 accept

例如:

from socket import *
sockfd = socket(...)
while not is_finished:
   client_sockfd, addr = sockfd.accept()
   # ...
   client_sockfd.close()
sockfd.close()

你可以使用不同的fd变量接受多个socket,并与多个客户端通信。但是,这需要一些并发编程。


1
如果您将一个大于1的数字传递给socket,我认为您指的是listen()socket()没有这样的参数。这些示例中缺少了listen(),但是accept()需要它才能工作。传递给listen()的数字是后备队列大小,它仅控制等待被accept()接受的客户端的最大数量。无论后备队列的大小如何,如果需要,您始终可以同时处理多个已接受的客户端。 - Remy Lebeau

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接