我正在为游戏制作一个非常简单的大厅系统。每个客户端定期通过UDP广播两个数据包,以最初发现其他客户端并传输用户信息,准备就绪等。该游戏正在针对Windows和Linux(64位)进行开发。正在发送UDP广播,但未收到
在Windows端,我已经得到了大厅系统完美无缺的工作。当我在一台Windows机器上进入大厅时,该人在其他机器中弹出。同样,立即检查和断开连接。换句话说,它是有效的。
现在的问题:Linux。网络代码几乎完全相同,只有少数必要的平台特定更改。我第一次尝试Windows < - > Linux。使用Wireshark,我发现Linux方面的确在广播数据包并从Windows中接收它们,但是游戏从未抓住数据包。我在select语句(套接字而不是套接字+ 1)中发现了一个错误,但修复它并没有帮助。 Windows盒子广播数据包,但它根本没有收到来自Linux盒子的数据包!然后我发现虽然两台机器都在广播和接收(再次通过Wireshark确认),但两台机器上的游戏都无法“看见”数据包。我很确定它不是一个防火墙问题(在任一平台上关掉所有东西,测试,恢复一切,没有变化),网络连接似乎没问题(可以手动ping每台主机)。我也检查过以确保端口确实可用(它们是)。
下面是广播包代码:
void NetworkLinux::BroadcastMessage(const std::string &msg,
const char prefix)
{
string data(prefix + msg);
if (sendto(linuxSocket, data.c_str(), static_cast<int>(data.length()), 0,
reinterpret_cast<sockaddr*>(&broadcastAddr), sizeof(broadcastAddr)) == -1)
{
Display_PError("sendto");
}
}
和接收数据包的代码:
const Message NetworkLinux::ReceiveMessage()
{
char buffer[recvBufferLength];
fill(buffer, buffer + recvBufferLength, 0);
sockaddr_in sender;
int senderLen = sizeof(sender);
fd_set read_fds;
FD_ZERO(&read_fds);
FD_SET(linuxSocket, &read_fds);
timeval time;
time.tv_sec = 0;
time.tv_usec = 16667; // microseconds, so this is ~1/60 sec
int selectResult = select(linuxSocket + 1, &read_fds,
nullptr, nullptr, &time);
if (selectResult == -1)
{
Display_PError("select");
}
else if (selectResult > 0) // 0 means it timed-out
{
int receivedBytes = recvfrom(linuxSocket, buffer,
recvBufferLength, 0, reinterpret_cast<sockaddr*>(&sender),
reinterpret_cast<socklen_t*>(&senderLen));
if (receivedBytes == -1)
{
Display_PError("recvfrom");
}
else if (receivedBytes > 0)
{
Message msg;
msg.prefix = buffer[0];
msg.msg = string(buffer + 1, buffer + receivedBytes);
msg.address = sender.sin_addr;
return msg;
}
}
Message m;
m.prefix = 'N';
return m;
}
为什么select()
继续回来为0的时候,我可以看到的数据包到达?此外,为什么它在Windows < - > Windows场景下工作,但不是Linux < - > Linux或Linux < - > Windows?
编辑:这是套接字创建/设置代码,根据要求。计算出的示例IP /广播地址为:192.168.1.3/192.168.1.255,192.168.1.5/192.168.1.255,它们与Windows端生成和使用的内容相匹配。
bool NetworkLinux::StartUp()
{
// zero addr structures
memset(&machineAddr, 0, sizeof machineAddr);
memset(&broadcastAddr, 0, sizeof broadcastAddr);
// get this machine's IP and store it
machineAddr.sin_family = AF_INET;
machineAddr.sin_port = htons(portNumber);
inet_pton(AF_INET, GetIP().c_str(), &(machineAddr.sin_addr));
// get the netmask and calculate/store the correct broadcast address
broadcastAddr.sin_family = AF_INET;
broadcastAddr.sin_port = htons(portNumber);
GetNetMask();
broadcastAddr.sin_addr.s_addr = machineAddr.sin_addr.s_addr | ~netmask;
char bufIP[INET_ADDRSTRLEN], bufBroadcast[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &machineAddr.sin_addr, bufIP, INET_ADDRSTRLEN);
inet_ntop(AF_INET, &broadcastAddr.sin_addr, bufBroadcast,
INET_ADDRSTRLEN);
Log("IP is: " + string(bufIP) + "\nBroadcast address is: "
+ string(bufBroadcast));
// create socket
linuxSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (linuxSocket == -1)
{
Display_PError("socket");
return false;
}
Log("Socket created.");
// switch to broadcast mode
int broadcast = 1;
if (setsockopt(linuxSocket, SOL_SOCKET, SO_BROADCAST, &broadcast,
sizeof broadcast) == -1)
{
Display_PError("setsockopt");
close(linuxSocket);
return false;
}
Log("Socket switched to broadcast mode.");
// bind it (this simplifies things by making sure everyone is using the same port)
if (bind(linuxSocket, reinterpret_cast<sockaddr*>(&machineAddr),
sizeof(machineAddr)) == -1)
{
Display_PError("bind");
close(linuxSocket);
return false;
}
Log("Socket bound.");
return true;
}
相关的东西:你应该使用多播代替广播,这是对网络更好的(后来你也可以通过多个网络途径的东西,或者可以开始使用IPv6更容易,它不支持广播)。另外,你有没有考虑过使用一些库来处理大部分的东西?这应该会减少可能的错误点数量。 – PlasmaHH
您还需要展示如何设置套接字。 'sockaddr_in','setsockopt()'等等。 –
@PlasmaHH我曾经看过多播,但读过一些路由器不支持它;但是,我仍然可以在未来看到它。无论如何,这只是因为它*应该*工作而困扰我,但并不是。我查看的大多数库都需要某种服务器地址,这在这种情况下不起作用,因为客户端不知道局域网中有哪些用户。 – Gemini14