1

我正在使用 c# 开发一个信使程序,并且遇到了一些问题。

服务器,客户端有三个连接(每个用于聊天,文件传输,纸牌游戏)。

对于第一个和第二个连接,它工作得很好。

但是问题出现在第三个套接字上,与前两个套接字相比,它处理的数据包类型较少。

这不是关于不接收数据包或没有连接,而是一次获取(或发送)多个(我打算发送)数据包。服务器日志一直说,一键,服务器接收到大约 3~20 个相同的数据包,并将它们发送给目标客户端。

在我的第三个连接的部分代码之前,我将解释这个东西是如何工作的。

connection1,2 和 connection3 (导致这个问题)之间的区别只是我建立连接的时间。1,2 连接到主窗体的 form_load 函数,并且工作正常。

当我加载游戏表单(不是主表单)时,连接 3 建立连接。此外,前两个套接字的监听线程在主窗体上,第三个在它自己的窗体上监听线程。这是我能找到的唯一区别。连接和监听线程是一样的。这是我的游戏表格代码。

public void GPACKET() //A Thread function for receiving packets from the server
{
    int read = 0;
    while (isGameTcpClientOnline)
    {

        try
        {
            read = 0;
            read = gameNetStream.Read(greceiveBuffer, 0, 1024 * 4);

            if (read == 0)
            {
                isGameTcpClientOnline = false;
                break;
            }
        }
        catch
        {
            isGameTcpClientOnline = false;
            gameNetStream = null;
        }
        Packet.Packet packet = (Packet.Packet)Packet.Packet.Desirialize(greceiveBuffer);

        switch ((int)packet.Type)
        {

            case (int)PacketType.gameInit:
                {
                    gameinit = (GameInit)Packet.Packet.Desirialize(greceiveBuffer);

                    //codes for handling the datas from the packet...
                    break;
                }
            case (int)PacketType.gamepacket:
                {
                    gp = (GamePacket)Packet.Packet.Desirialize(greceiveBuffer);
                    //codes for handling the datas from the packet...

                    break;
                }

        }
    }

}

public void setPacket(bool turn) //makes the packet, and sends it to the server..
{
    if (turn)
        turnSetting(false);
    else
        turnSetting(true);

    gps = new GamePacket();
    gps.Type = (int)PacketType.gamepacket;
    gps.isFirstPacket = false;
    gps.sym = symbol;
    gps.ccapacity = cardCapacity;
    gps.currentList = current_list[0].Tag.ToString();
    gps.isturn = turn;
    gps.opname = opid;
    List<string> tempList = new List<string>();

    foreach (PictureBox pb in my_list)
    {
        tempList.Add(pb.Image.Tag.ToString());
    }

    gps.img_list = tempList;


    Packet.Packet.Serialize(gps).CopyTo(this.gsendBuffer, 0);
    this.Send();
    label5.Text = symbol + ", " + current_list[0].Tag.ToString();

}

public void Send() //actually this part sends the Packet through the netstream.
{

    gameNetStream.Write(this.gsendBuffer, 0, this.gsendBuffer.Length);
    gameNetStream.Flush();

    for (int j = 0; j < 1024 * 4; j++)
    {
        this.gsendBuffer[j] = 0;
    }

}

我真的不知道为什么我会遇到这个问题。是关于连接点吗?还是关于接收点?还是关于发送点?如果我在连接1,2(在主窗体上。如果我这样做,我应该让“GPACKET”功能也在主窗体上运行)的同一个地方建立这个连接?

4

1 回答 1

2

这看起来像是经典的“假设我们读取了整个数据包”,这里的“数据包”是指您的逻辑消息,而不是底层传输数据包。例如:

read = gameNetStream.Read(greceiveBuffer, 0, 1024 * 4);
...
Packet.Packet packet = (Packet.Packet)Packet.Packet.Desirialize(greceiveBuffer);

首先,这让我感到非常不满意readDesirialize但是:是什么让你认为我们阅读了整个数据包?我们可以阅读:

  • 一整包(仅)
  • 一包的一半
  • 一个字节
  • 三包
  • 一个数据包的最后 2 个字节,1 个完整数据包,以及第三个数据包的前 5 个字节

TCP 只是一个流;保证Read给您的只是“至少 1 个字节,最多 {count} 个字节,或一个 EOF”。Write调用 to将映射任何类似于调用的内容是非常不寻常的Read。您的工作是了解协议,并决定要缓冲多少数据,然后将该缓冲区的多少视为一个数据包,而不是为下一个数据包保留它们。

另请参阅:有多少种方式可以搞乱 IO?,特别是“网络数据包:你发送的不是(通常)你得到的”。


要准确填充 4096 字节缓冲区:

int expected = 4096, offset = 0, read;
while(expected != 0 &&
    (read = gameNetStream.Read(greceiveBuffer, offset, expected)) > 0)
{
    offset += read;
    expected -= read;
}
if(expected != 0) throw new EndOfStreamException();
于 2013-05-21T11:30:27.253 回答