C++:将 UDP 袜子转移到新位置
C++: Transferring UDP sockaddr to new location
概述
我正在用C++编写一个简单的测试游戏服务器。目前,我有一个绑定套接字侦听任何人,当某种数据包类型到达时,我想将 sockaddr 结构保存到一个对象中,该对象将被传递给一个单独的线程。该单独的线程将遍历所有套接字并尝试向它们写入数据。
出于测试目的,我只想执行以下操作:
- 侦听数据包并确定其类型(完成(
- 将源地址信息传递给单独的函数(完成(
- 创建一个新套接字,并将其绑定到新端口上(已完成,但这里可能有问题?
- 使用之前传入的sockaddr_in通过新绑定/创建的套接字发送数据(完成?
设置
接收
目前,全局侦听器的配置方式如下:
void lobbyactions_thread() {
struct sockaddr_in any;
memset(&any, 0, sizeof(any));
any.sin_family = AF_INET;
any.sin_port = htons(LOBBYACTIONS_PORT);
any.sin_addr.s_addr = htonl(INADDR_ANY);
unsigned int any_sz = sizeof(any);
packets::LOBBYACTION_PACKET jpacket;
int socketfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
bind(socketfd, (struct sockaddr*) &any, sizeof(any))
while (server_manager::STAY_ALIVE) {
struct sockaddr_in* new_con = new struct sockaddr_in;
memset(new_con, 0, sizeof(*new_con));
unsigned int new_con_size = sizeof(*new_con);
recvfrom(socketfd, &jpacket, packets::LOBBYACTION_PACKET_SIZE, 0, (sockaddr*)new_con, &new_con_size);
bool save = false;
if (jpacket.type == JOIN) { save = true; server_manager::join(jpacket.id, new_con); }
// ...
jpacket.okay = true;
if (!save) {
sendto(socketfd, &jpacket, packets::LOBBYACTION_PACKET_SIZE, 0, (sockaddr*)&any, any_sz);
free(any);
}
}
}
保存和临时发送
处理加入时,我想将收件人保存到其他收件人/用户/连接的内部队列中。出于测试目的,我有这个连接方法,它将简单地创建新套接字,绑定它,并发送一些数据作为概念证明。
struct Slot {
int playerSocket;
int playerAssignedPort;
struct sockaddr_in* sourceAddress;
unsigned int sourceAddressSize;
};
void join(int id, struct sockaddr_in* raddr) {
Slot* ps = new Slot();
ps->playerAssignedPort = 8810 // temporary
ps->playerSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(ps->playerAssignedPort);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
ps->sourceAddressSize = sizeof(addr);
bind(ps->playerSocket, (struct sockaddr*)&addr, ps->sourceAddressSize);
ps->sourceAddress = raddr;
ps->sourceAddress->sin_port = htons(ps->playerAssignedPort);
// ...
sendto(ps->playerSocket, &updatePacket, packets::GAMESTATE_PACKET_SIZE, 0, (sockaddr*)(ps->sourceAddress), ps->sourceAddressSize);
// ...
}
问题
为什么我不能绑定到这个套接字?我是否经过了错误的any
部分?还是这真的是不可能的?任何帮助或指示(可能是可怕的双关语(都会很棒。
为什么当我保存由recvfrom
填充的结构sockaddr_in时,由于某种原因我可以不再发送消息,尽管是通过另一个端口和另一个套接字?奇怪的是,我发现当客户端和服务器必须通过本地路由器时,这是行不通的,但如果客户端和服务器不必通过本地路由器,这实际上有效!可能是我需要收集其他数据包/标头信息吗?还是路由器正在做一些(或缺乏(会限制消息的事情?可能是有一些奇怪的端口转发问题,而不是这里实际上的软件问题吗?
编辑
需要注意的是,目前在lobbyactions_thread()
方法中,即接收、处理和发送,一切都在工作。问题在于join()
方法。
更新的代码,部分来自自己的发现和用户@selbie
您的bind
调用失败有两个原因。 首先,您正在尝试将多个套接字绑定到同一端口。如果端口 8810 上的第一个绑定成功,则同一端口上的第二个套接字绑定尝试将失败。 此外,您正在尝试绑定到远程地址,而不是本地地址 - 这将始终失败。
我认为您可能会混淆bind
函数的意图。 对于 UDP 套接字,绑定调用指定在哪个端口(以及哪个本地 IP 地址(上发送和接收数据报消息。标准行为是绑定到 IP 的INADDR_ANY(所有具有 IP 地址的适配器(。 就像您拥有大厅代码一样。
当玩家通过 UDP 消息加入时,您不希望绑定到新套接字的远程地址,因为这确实会失败。 相反,您希望以 IP 和端口 0 的形式绑定到INADDR_ANY(以获得自动分配的可用端口(。
所以改变这个:
bind(ps->playerSocket, (struct sockaddr*)&addr, ps->sourceAddressSize); // <- Wont
捆!
对此:
sockaddr_in addrTemp = {}; // zero-init the local bind address (INADDR_ANY and port 0)
addrTemp.sin_family = AF_INET;
int result = bind(ps->playerSocket, (sockaddr*)&addrTemp, sizeof(addrTemp));
您可以稍后调用getsockname
以获取由于在绑定调用中使用端口 0 而分配给套接字的端口号。
此外,请考虑一种设计,即在专用端口号上为所有播放器使用单个插槽。这可能更适合 NAT 遍历,您不必担心端口用完。 如果您正在构建客户端-服务器游戏与点对点游戏,则可能会有很大差异。
- C++:将 UDP 袜子转移到新位置
- lua_newuserdata QMetaObject 上的新放置位置
- 将数据复制到磁盘上新位置的语法
- (虚拟)父级中的新位置
- 在 Omnet++ 中的模拟时间内更改/设置节点的新位置
- 每次我分配新的位置时,都会隐式地称为destuructor
- 放置新位置的错误警告
- 给定一个用三角形镶嵌的补丁,如何修改其新的顶点位置
- 如何正确删除一行控件并在该位置动态创建一个新控件
- 自复制文件到新位置tchar不兼容
- 在不隔离内存的情况下使用新的位置是安全的吗?
- 使用新的位置时Sigbus
- 即使在同一类型上,也可以将击曲线调用新的位置
- 我可以使用新的位置添加到向量
- 将字符数复制到新字符串位置的字符串操作,例如 S[0]
- 使用新的位置来创建静态常量指针,指向静态内存缓冲区
- 如何根据父对象的相对变化计算子对象的新全局位置
- 在容器中使用新放置位置
- 如何在此处使用新展示位置
- 在不同大小的类上放置新的位置