在 VB.NET 中连接到源游戏服务器

发布于 2024-08-10 09:56:07 字数 2228 浏览 2 评论 0原文

我正在开发一个应用程序,用于检测在 LAN 上运行的基于源的游戏。根据 Valve 提供的 规范,我将其范围缩小到了我想要的范围:在端口 27015 上建立 UDP 连接,发送 A2S_INFO 查询(0xFFFFFFFF 后跟“TSource Engine Query”)并解析二进制回复。

这是我正在使用的代码:

Dim sIP As String = "192.168.0.154"
Dim nPort As Integer = 27015

Dim connectionMessage As String = "ÿÿÿÿTSource Engine Query" & Chr(0)

Dim endPoint As New IPEndPoint(IPAddress.Parse(sIP), nPort)

Dim client As Socket = New Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 6000)
client.SendTo(Encoding.ASCII.GetBytes(connectionMessage), endPoint)
Console.WriteLine("Message sent to " & sIP & ":" & nPort & vbCrLf & connectionMessage)
Dim sBuffer(1400) As Byte
Try
    client.ReceiveFrom(sBuffer, endPoint)
    MsgBox(System.Text.Encoding.Unicode.GetString(sBuffer))
Catch ex As SocketException
    MsgBox("Failed to receive response on " & sIP & ":" & nPort & ":" & vbCrLf & ex.Message)
End Try

我不断收到 SocketError.TimedOut 异常,通知我:

无法在 192.168.0.154:27015 上接收响应: 连接尝试失败,因为连接方在一段时间后没有正确响应,或者建立的连接失败,因为连接的主机未能响应

我相当确定这非常接近答案,因为以下非常简单的 PHP 版本可以工作就像一个魅力:

$ip = "192.168.0.154";
$port = 27015;

$fp = fsockopen("udp://".$ip,$port, $errno, $errstr);
socket_set_timeout($fp, 6);

$prefix = "\xff\xff\xff\xff";
$command = "TSource Engine Query";
$msg = "$prefix$command";
fputs($fp, $msg, strlen($msg));

$response = "";
do {
    $response .= fgets($fp, 16);
    $status = socket_get_status($fp);
} while ($status['unread_bytes']);
fclose ($fp);

echo $response;

这为我提供了符合规范的答复。

我已经很接近了,但是我的 VB.NET 代码出了点问题。我做错了什么?

解决方案

编码类型对于传输二进制数据无效。我将以下内容替换

Encoding.ASCII.GetBytes(connectionMessage)

为:

System.Text.Encoding.[Default].GetBytes(connectionMessage)

并立即解决了。

“pub”建议的更强大的解决方案可能是指定代码页(尽管我还没有测试过这一点):

Encoding.GetEncoding("iso-8859-1").GetBytes(connectionMessage)

I'm developing an application that detects Source-based games running on the LAN. Following up on the specifications provided by Valve, I have it narrowed down to exactly what I want: making a UDP connection on port 27015, sending an A2S_INFO query (0xFFFFFFFF followed by "TSource Engine Query") and parsing the binary reply.

This is the code I'm working with:

Dim sIP As String = "192.168.0.154"
Dim nPort As Integer = 27015

Dim connectionMessage As String = "ÿÿÿÿTSource Engine Query" & Chr(0)

Dim endPoint As New IPEndPoint(IPAddress.Parse(sIP), nPort)

Dim client As Socket = New Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 6000)
client.SendTo(Encoding.ASCII.GetBytes(connectionMessage), endPoint)
Console.WriteLine("Message sent to " & sIP & ":" & nPort & vbCrLf & connectionMessage)
Dim sBuffer(1400) As Byte
Try
    client.ReceiveFrom(sBuffer, endPoint)
    MsgBox(System.Text.Encoding.Unicode.GetString(sBuffer))
Catch ex As SocketException
    MsgBox("Failed to receive response on " & sIP & ":" & nPort & ":" & vbCrLf & ex.Message)
End Try

I keep getting SocketError.TimedOut exceptions, informing me:

Failed to receive response on 192.168.0.154:27015:
A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond

I'm fairly certain this is very close to the answer, because the following very simple PHP version works like a charm:

$ip = "192.168.0.154";
$port = 27015;

$fp = fsockopen("udp://".$ip,$port, $errno, $errstr);
socket_set_timeout($fp, 6);

$prefix = "\xff\xff\xff\xff";
$command = "TSource Engine Query";
$msg = "$prefix$command";
fputs($fp, $msg, strlen($msg));

$response = "";
do {
    $response .= fgets($fp, 16);
    $status = socket_get_status($fp);
} while ($status['unread_bytes']);
fclose ($fp);

echo $response;

This provides me with a reply in accordance with the specifications.

I'm close, but something's amiss with my VB.NET code. What am I doing wrong?

The solution

The encoding type is not valid for transmitting binary data. I replaced the following:

Encoding.ASCII.GetBytes(connectionMessage)

with:

System.Text.Encoding.[Default].GetBytes(connectionMessage)

And it was instantly solved.

A more robust solution, suggested by "pub", might be to specify the code page (although I haven't tested this):

Encoding.GetEncoding("iso-8859-1").GetBytes(connectionMessage)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

Bonjour°[大白 2024-08-17 09:56:07

对于世界各地的计算机上没有将 iso-8859-1 代码页设置为默认值的人,请使用:

Encoding.GetEncoding("iso-8859-1").GetBytes(connectionMessage)

For those couple guys around the world who doesn't have iso-8859-1 code page set as default on computer, use:

Encoding.GetEncoding("iso-8859-1").GetBytes(connectionMessage)

毁我热情 2024-08-17 09:56:07

Did you take a look at this Multi-Threaded Game Server Browser in VB.Net article at CodeProject? If you don't want to re-use the code that's already been written, I'm sure you can check how they did it to see if you can find your problem.

Hope that helps, peace.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文