C# 如果发送了许多消息,如何写入MemoryStream

C# 如果发送了许多消息,如何写入MemoryStream,c#,tcp,memorystream,C#,Tcp,Memorystream,下面是我现在如何从流中读取数据: public List<ServerClient> clients = new List<ServerClient>(); while (true) { Update(); } private void Update() { //Console.WriteLine("Call"); if (!serverStarted) { return;

下面是我现在如何从流中读取数据:

public List<ServerClient> clients = new List<ServerClient>();

while (true)
{
    Update();
}

    private void Update()
    {
        //Console.WriteLine("Call");
        if (!serverStarted)
        {
            return;
        }

        foreach (ServerClient c in clients.ToList())
        {
            // Is the client still connected?
            if (!IsConnected(c.tcp))
            {
                c.tcp.Close();
                disconnectList.Add(c);
                Console.WriteLine(c.connectionId + " has disconnected.");
                CharacterLogout(c.connectionId);
                continue;
                //Console.WriteLine("Check for connection?\n");
            }
            else
            {



                // Check for message from Client.
                NetworkStream s = c.tcp.GetStream();
                if (s.DataAvailable)
                {
                    string data = c.streamReader.ReadLine();

                    if (data != null)
                    {
                        OnIncomingData(c, data);
                    }

                }
                //continue;
            }
        }

        for (int i = 0; i < disconnectList.Count - 1; i++)
        {
            clients.Remove(disconnectList[i]);
            disconnectList.RemoveAt(i);
        }


    }
}

这是我的:

public class ServerClient
{
    public TcpClient tcp;
    public int accountId;
    public StreamReader streamReader;
    public int connectionId;
    public ServerClient(TcpClient clientSocket)
    {
        tcp = clientSocket;
    }
}
这是我的
OnConnection
功能:

    private void OnConnection(IAsyncResult ar)
    {
        connectionIncrementor++;
        TcpListener listener = (TcpListener)ar.AsyncState;
        NetworkStream s = clients[clients.Count - 1].tcp.GetStream();
        clients.Add(new ServerClient(listener.EndAcceptTcpClient(ar)));
        clients[clients.Count - 1].connectionId = connectionIncrementor;
        clients[clients.Count - 1].streamReader = new StreamReader(s, true);
        StartListening();


        //Send a message to everyone, say someone has connected!
        Dictionary<string, string> SendDataBroadcast = new Dictionary<string, string>();
        SendDataBroadcast.Add("connectionId", clients[clients.Count - 1].connectionId.ToString());

        Broadcast("001", SendDataBroadcast, clients, clients[clients.Count - 1].connectionId);
        Console.WriteLine(clients[clients.Count - 1].connectionId + " has connected.");
    }
专用连接无效(IAsyncResult ar)
{
连接增强器++;
TcpListener侦听器=(TcpListener)ar.AsyncState;
NetworkStream s=clients[clients.Count-1].tcp.GetStream();
添加(新的服务器客户端(listener.endAcceptCpcClient(ar));
客户端[clients.Count-1].connectionId=connectionIncrementor;
客户端[clients.Count-1]。streamReader=新的streamReader(s,true);
惊人的倾听();
//向每个人发送消息,说有人已连接!
Dictionary SendDataBroadcast=新字典();
SendDataBroadcast.Add(“connectionId”,clients[clients.Count-1].connectionId.ToString());
广播(“001”,发送数据广播,客户端,客户端[clients.Count-1].connectionId);
Console.WriteLine(客户端[clients.Count-1].connectionId+“已连接”);
}
正常情况下,一切正常。但是,如果我尝试每1秒发送更多请求,就会出现问题。收到的消息不完整。它只接收发送的消息的一部分

Debug.Log(“客户端世界:+json”)我可以看到消息是完整的,但在服务器上我看到它不是完整的

如果我发送的请求较少,则不会发生这种情况


因此,我认为我应该创建一个
MemoryStream
,并在那里放一条消息,然后阅读。然而,我真的不知道我如何才能做到这一点。你能帮忙吗?

我觉得客户端比服务器更可疑


不是线程安全的。使用
ClientWorldServer.Send
时是否以线程安全的方式调用它?将您的呼叫锁定或排队至
ClientWorldServer。使用或其他同步原语发送
。还有一个您可以使用的工具。

整个代码不是很好,但我将集中讨论您的具体问题。这很可能与
StreamReader
的数据缓冲有关
StreamReader
的缓冲区大小(可以传递给构造函数)默认为1024字节。当您调用
ReadLine
时,流阅读器完全可以从底层流中读取多行内容。在您的例子中,您有一个while循环,在该循环中您枚举连接的客户端,并且在循环的每个迭代中创建新的
StreamReader
,并从中读取一行。当消息速率较低时,一切看起来都很好,因为在循环迭代之间只有一行到达。现在假设客户机快速发送了两条json消息,每条消息都是800字节,它们都到达了您的套接字。现在调用
StreamReader.ReadLine
。因为缓冲区大小是1024—它将从套接字(
NetworkStream
)读取1024个字节,并将前800个字节作为一行返回给您。您处理该行并放弃
StreamReader
,转到while循环的下一个迭代。这样做还可以丢弃部分消息(下一条消息的224字节),因为它们已经从套接字读取到StreamReader缓冲区中。我认为,从这一点来看,应该清楚如何解决这个问题——不要每次都创建一个新的
StreamReader
,而是为每个客户端创建一个(例如存储为
服务器客户端的成员
)并使用它。

文本太多,尝试创建一个新的。也就是说,如何/从何处调用
ClientWorldServer.Send
?您正在读取每个连接的一行,或者读取循环中的行?我已更新了我的问题,供您查看。是的,这个函数是在while循环中调用的。StreamReader在其中缓冲数据,所以每次像现在这样创建新的
StreamReader
是错误的。相反,为每个客户端创建一个
StreamReader
,然后在循环中读取其中的行。您可以对代码进行建议的更改并将其添加为答案吗?在所做的测试中,我可以确认客户端正在发送整条消息,但服务器没有读取它。我在这里对客户端进行了检查:我可以看到
json
字符串已满且正确。但是,我可以确定整个字符串都是在流中写入的吗?除了
Debug.Log(“客户端世界:+json”)之外,我如何检查它?抱歉,无法从此处读取pastebin,因此无法验证您的测试。如果将
Debug.Log
发送到服务器,则它可能会显示正确的包事件,因此我重复我的问题:您如何调用
ClientWorldServer.Send
?尝试在
writer.WriteLine(json)周围放置一个
lock
;writer.Flush()。如果这有帮助的话,尽管可能不是一个好的解决方案,但至少你找到了问题的根源,这就是你所说的
lock(writer){writer.WriteLine(json);writer.Flush();Debug.Log(“Client World:+json);}
?我也更新了我的问题。你能帮我在服务器端创建一个
StreamMemory
吗?我不知道我能帮你做什么,除了已经在。如果你真的确信这是一个生产者/消费者的问题(某种类型的缓冲区完全问题),考虑上面提到的BuffjCug收藏。有趣的是,缺失的部分不是结束,而是乞讨。如果您所说的是问题所在,并且在一个套接字中发送了两条800字节的消息,但默认缓冲区是1024,这不意味着我将获得整个第一条消息和第二条消息的开头部分吗?@TonyStark否,您将获得第二条消息的结尾而不是开头,因为您放弃了开头(在我的示例中是前224字节)。您读取了一行(800字节),但没有读取第二行。在下一次迭代中,您将读取第二行,从225字节开始,而前224行将丢失。您建议我存储
StreamReader
fo吗
    private void OnConnection(IAsyncResult ar)
    {
        connectionIncrementor++;
        TcpListener listener = (TcpListener)ar.AsyncState;
        NetworkStream s = clients[clients.Count - 1].tcp.GetStream();
        clients.Add(new ServerClient(listener.EndAcceptTcpClient(ar)));
        clients[clients.Count - 1].connectionId = connectionIncrementor;
        clients[clients.Count - 1].streamReader = new StreamReader(s, true);
        StartListening();


        //Send a message to everyone, say someone has connected!
        Dictionary<string, string> SendDataBroadcast = new Dictionary<string, string>();
        SendDataBroadcast.Add("connectionId", clients[clients.Count - 1].connectionId.ToString());

        Broadcast("001", SendDataBroadcast, clients, clients[clients.Count - 1].connectionId);
        Console.WriteLine(clients[clients.Count - 1].connectionId + " has connected.");
    }