使用UDP套接字中的Connect()、Send()和Recv()函数出现问题。

4
为了我的大学作业,我需要创建一个快节奏的网络游戏,因此选择使用UDP而不是TCP。我知道编程UDP和TCP之间的许多差异,并阅读了MSDN关于winsock的相关部分的大部分文档。在MSDN上,它指出通过connect()函数创建UDP套接字应该将套接字绑定到指定的地址和端口,并且能够使用send()和recv()函数与创建的套接字。

对于我的应用程序,我创建一个客户端,并使用环回地址使用connect()函数发送多个数据包。客户端在调用select()后,然后接收它发送出去的数据包。但是,我从recv()函数中得到的结果是SOCKET_ERROR,使用WSAGetLastError()的错误描述是“现有连接被远程主机强制关闭”。

如果我使用bind()函数并使用sendto()来通过环回地址发送数据,则可以无错误地接收数据包...有人知道为什么connect()函数没有执行它应该执行的操作吗?是否有人能够使用connect()函数使用UDP套接字?


1
UDP是一种无连接协议。在MSDN上哪里说要使用connect()函数?我想你肯定是在阅读TCP文档... - Tony Delroy
6
connect() 方法对 UDP 做了一些操作:它设置了 UDP 套接字发送数据包和接收数据包的地址。这样,稍后你就可以只调用 send() 而不是 sendto()。虽然并不特别有用,但也不是完全没有用处。 - Jeremy Friesner
4
@Tony:不,connect对于无连接的套接字完全有效。它只是将套接字固定为仅允许发送到/从单个远程地址接收(因此您可以使用sendrecv而不是sendtorecvfrom)。 - R.. GitHub STOP HELPING ICE
4个回答

18

如果您希望程序接收UDP数据包,则需要调用bind()函数。connect()函数仅在调用send()时设置套接字发送数据包的地址;它不会将套接字与本地UDP端口关联以接收数据包。要实现该功能,必须调用bind()函数。


谢谢Jeremy,先调用bind再连接似乎解决了问题。当我在服务器端编写更多代码时,我会知道更多情况。 - Sent1nel
1
如果您编写的UDP服务器仅服务于一个远程地址,那么实际上所谓的调用是什么?您只需调用bind()来绑定服务器端口,它已经将下一个recv()调用与特定的端口和IP地址相关联。我已经测试过了。另一方面,在具有唯一UDP服务器的UDP客户端中,您只需要使用connect()连接到远程地址-send()将理解发送数据的位置。 - V. Dalechyn

4

"UNIX网络编程"指出,在UDP客户端套接字上进行的连接调用提前确定并存储有关目标套接字地址的所有状态(掩码、选择接口等),从而节省了在每个::sendto调用上执行此操作的成本。这本书声称,由于减少了这种开销,::send与::sendto之间的速度可能快高达3倍-数据可以直接传输到NIC驱动程序,绕过大部分IP堆栈处理。高性能游戏程序员可能需要考虑这一点。


1

-3

请记住,UDP协议是一种“无连接”协议,这意味着您永远不会连接到主机,只需发送数据。因此,您可以看到,将连接视为操作对于UDP来说是没有意义的。

对于UDP,您应该使用sendto()和recvfrom()函数,在这些函数中指定地址和缓冲区即可,TCP中舒适处理的其他所有内容都已经消失了,您必须自己处理这些事情。

在MSDN文档中提到,实际上可以以某种方式使用普通的send/recv函数与UDP一起使用,但是既然已经有了单独的函数,为什么还要这样做呢?正如其他评论所述,UDP的connect()执行的是其他操作,它不是基本上的“连接”操作,而是一种设置用于UDP使用的send()/recv()的过滤器。


关于键盘懒惰的问题,为什么要这样做呢?调用一次远程主机和端口的connect,然后多次调用send需要比不调用connect并多次调用sendto更少的输入。这不仅是函数名的两个字符,还包括目标地址的额外参数。 - Jesse Chisholm
1
连接可以被解释为设置远程对等地址。因此,套接字是连接还是无连接并不重要。 - FaceBro
请注意,调用UDP套接字上的connect()方法的另一个副作用是,从那时起,UDP套接字将仅接收来自您在connect()调用中指定的端点的传入UDP数据包;来自其他来源的传入UDP数据包将被丢弃。当然,是否对您造成问题取决于您是否希望从其他来源接收UDP数据包 :) - Jeremy Friesner

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