关于C#中RCON协议实现的令人难以置信的问题

时间:2011-05-03 12:33:48

标签: c# sockets sleep stringbuilder

我再一次需要你的帮助来解决我的这个问题......已经有一天了,我似乎无法找出为什么在我的代码和输出中发生这种情况。

好的.....所以基本上我正在尝试在C#中实现Valve的RCON Protocol,到目前为止,我得到expected output给出下面的代码和示例用法:

用法:

RconExec(socket, "cvarlist");

代码:

private string RconExec(Socket sock, string command)
{
    if (!sock.Connected) throw new Exception("Not connected");

    //sock.DontFragment = true;
    sock.ReceiveTimeout = 10000;
    sock.SendTimeout = 10000;
    //sock.Blocking = true;

    Debug.WriteLine("Executing RCON Command: " + command);

    byte[] rconCmdPacket = GetRconCmdPacket(command);
    sock.Send(rconCmdPacket); //Send the request packet
    sock.Send(GetRconCmdPacket("echo END")); //This is the last response to be received from the server to indicate the end of receiving process
    RconPacket rconCmdResponsePacket = null;

    string data = null;

    StringBuilder cmdResponse = new StringBuilder();

    RconPacket packet = null;
    int totalBytesRead = 0;

    do
    {
        byte[] buffer = new byte[4]; //Allocate buffer for the packet size field
        int bytesReceived = sock.Receive(buffer); //Read the first 4 bytes to determine the packet size
        int packetSize = BitConverter.ToInt32(buffer, 0); //Get the packet size

        //Now proceed with the rest of the data
        byte[] responseBuffer = new byte[packetSize];

        //Receive more data from server
        int bytesRead = sock.Receive(responseBuffer);

        //Parse the packet by wrapping under RconPacket class
        packet = new RconPacket(responseBuffer);
        totalBytesRead += packet.String1.Length;

        string response = packet.String1;
        cmdResponse.Append(packet.String1);

        Debug.WriteLine(response);

        Thread.Sleep(50);

    } while (!packet.String1.Substring(0,3).Equals("END"));

    Debug.WriteLine("DONE..Exited the Loop");
    Debug.WriteLine("Bytes Read: " + totalBytesRead + ", Buffer Length: " + cmdResponse.Length);

    sock.Disconnect(true);

    return "";
}

问题:

这还不是最终的代码,因为我只是在Debug窗口中测试输出。如果我将代码修改为实际状态,则会出现一些问题。

  1. 删除Thread.Sleep(50)

    • 如果我删除Thread.Sleep(50),则输出无法完成并最终抛出异常。我注意到'END'终止字符串是由服务器提前发送的。只有当整个列表完成时,才会由服务器发送此字符串。 Exception Thrown我测试了这么多次,同样的事情发生了,如果我不删除该行,列表完成并且函数正确地退出循环。
  2. 在循环中删除Debug.WriteLine(response);并在循环外使用Debug.WriteLine(cmdResponse.ToString());输出字符串,仅显示部分列表数据。如果我将从循环读取的实际字节数与StringBuilder实例的长度进行比较,它们是否相同?单击here以获取生成的输出。

  3. 鉴于上述两种情况,为什么会发生这种情况?

1 个答案:

答案 0 :(得分:3)

您并没有考虑Socket.Receive读取的字节数少于提供的缓冲区长度。返回值告诉您实际读取的字节数。我看到你正确地将这个值存储在变量中,但我看不到任何使用它的代码。

您应该准备好多次调用Receive来检索整个包。特别是当您收到包裹数据时。

我不确定这是你问题的原因。但它可能是,因为客户端的短暂延迟可能足以填满网络缓冲区,以便在一次调用中读取整个包。

尝试使用以下代码检索包数据:

int bufferPos = 0;
while (bufferPos < responseBuffer.Length)
{
    bufferPos += socket.Receive(responseBuffer, bufferPos, responseBuffer.Length - bufferPos, SocketFlags.None);
}

注意:当第一次调用Receive(您收到数据包的数据长度的那个)没有返回4个字节时,您也应该支持这种情况。