繁体   English   中英

C#中一个非常简单的TCP服务器/客户端偶尔丢弃数据包。 我该怎么做才能防止这种情况发生?

[英]A very simple TCP server/client in C# occasionally drops packets. What can I do to prevent this?

我有一台PC内通信服务器/客户端设置为从一个程序向另一个程序发送和接收数据 - 在这种情况下,是一个正在监听文本命令和Unity3D的自定义服务器。

在大多数情况下,它可以工作,但每隔一段时间,它就会丢弃数据包,Unity不会在没有多次尝试的情况下获取它们。 数据包似乎已发送但丢失,因为我看到“已发送消息”控制台日志。 以下是服务器和客户端的代码:

服务器:

class TCPGameServer
{
    public event EventHandler Error;

    public Action<Data> ADelegate;

    TcpListener TCPListener;
    TcpClient TCPClient;
    Client ActiveClient;

    NetworkStream networkStream;

    StreamWriter returnWriter;
    StreamReader streamReader;

    Timer SystemTimer = new Timer();
    Timer PingTimer = new Timer();

    int Port = 8637;

    public TCPGameServer()
    {
        TCPListener = new TcpListener(IPAddress.Loopback, Port);
        SystemTimer.Elapsed += StreamTimer_Tick;
        SystemTimer.AutoReset = true;
        SystemTimer.Interval = 2000;

        PingTimer.Elapsed += PingTimer_Tick;
        PingTimer.AutoReset = true;
        PingTimer.Interval = 30000;
    }

    public void OpenListener()
    {
        TCPListener.Start();
        TCPListener.BeginAcceptTcpClient(AcceptTCPCallBack, null);
        Console.WriteLine("Network Open.");
    }

    public void GameLogout()
    {
        SystemTimer.AutoReset = false;
        SystemTimer.Stop();

        PingTimer.AutoReset = false;
        PingTimer.Stop();

        ActiveClient = null;

        returnWriter.Dispose();
        streamReader.Dispose();

        Console.WriteLine("The client has logged out successfully.");
    }

    private void AcceptTCPCallBack(IAsyncResult asyncResult)
    {
        TCPClient = null;
        ActiveClient = null;
        returnWriter = null;
        streamReader = null;

        try
        {
            TCPClient = TCPListener.EndAcceptTcpClient(asyncResult);

            TCPListener.BeginAcceptTcpClient(AcceptTCPCallBack, null);


            ActiveClient = new Client(TCPClient);
            networkStream = ActiveClient.NetworkStream;

            returnWriter = new StreamWriter(TCPClient.GetStream());
            streamReader = new StreamReader(TCPClient.GetStream());

            Console.WriteLine("Client Connected Successfully.");

            Data Packet = new Data();
            Packet.cmdCommand = Command.Login;
            Packet.strName = "Server";
            Packet.strMessage = "LOGGEDIN";
            SendMessage(Packet);


            SystemTimer.AutoReset = true;
            SystemTimer.Enabled = true;
            SystemTimer.Start();

            Ping();

            PingTimer.AutoReset = true;
            PingTimer.Enabled = true;
            PingTimer.Start();

        } catch (Exception ex)
        {
            OnError(TCPListener, ex);
            return;
        }


    }

    private void StreamTimer_Tick(object source, System.Timers.ElapsedEventArgs e)
    {
        CheckStream();
    }

    private void PingTimer_Tick(object source, System.Timers.ElapsedEventArgs e)
    {
        Ping();
    }

    private void Ping()
    {
        if (TCPClient.Connected)
        {
            Data Packet = new Data();
            Packet.cmdCommand = Command.Ping;
            Packet.strName = "Server";
            Packet.strMessage = "PING";

            SendMessage(Packet);
        }
    }


    public void CheckStream()
    {
        try
        {
            if (TCPClient.Available > 0 || streamReader.Peek() >= 0)
            {
                string PacketString = streamReader.ReadLine();
                Data packet = JsonConvert.DeserializeObject<Data>(PacketString);

                switch (packet.cmdCommand)
                {
                    case Command.Logout:
                        GameLogout();
                        break;
                    case Command.Message:
                        if (ADelegate != null)
                        {
                            ADelegate(packet);
                        }
                        break;
                    case Command.Ping:
                        Console.WriteLine("PONG!");
                        break;
               }
            }
        } catch (IOException e)
        {
            Console.WriteLine(e.Message);
        } catch (NullReferenceException e)
        {
            Console.WriteLine(e.Message);
        }
    }

    public void SendMessage(Data packet)
    {
        if (ActiveClient != null)
        {
            string packetMessage = JsonConvert.SerializeObject(packet);
            returnWriter.WriteLine(packetMessage);
            returnWriter.Flush();
        }
    }


    public void OnError(object sender, Exception ex)
    {
        EventHandler handler = Error;
        if (handler != null)
        {
            ErrorEventArgs e = new ErrorEventArgs(ex);
            handler(sender, e);
        }
    }

    public void RegisterActionDelegate(Action<Data> RegisterDelegate)
    {
        ADelegate += RegisterDelegate;
    }

    public void UnRegisterActionDelegate(Action<Data> UnregisterDelegate)
    {
        ADelegate -= UnregisterDelegate;
    }
} 

客户:

public class TCPNetworkClient
{

    public Action<Data> PacketDelegate;

    public TcpClient TCPClient;
    int Port = 8637;

    StreamReader streamReader;
    StreamWriter streamWriter;

    Timer StreamTimer = new Timer();

    public bool LoggedIn = false;



    public void Start()
    {
        if (LoggedIn == false)
        {
            TCPClient = null;
            StreamTimer.AutoReset = true;
            StreamTimer.Interval = 2000;
            StreamTimer.Elapsed += StreamTimer_Tick;


            try
            {
                TCPClient = new TcpClient("127.0.0.1", Port);

                streamReader = new StreamReader(TCPClient.GetStream());
                streamWriter = new StreamWriter(TCPClient.GetStream());

                StreamTimer.Enabled = true;
                StreamTimer.Start();
            }
            catch (Exception ex)
            {
                Debug.Log(ex.Message);
            }
        }

    }

    private void StreamTimer_Tick(System.Object source,    System.Timers.ElapsedEventArgs e)
    {
        if (TCPClient.Available > 0 || streamReader.Peek() >= 0)
        {
            string PacketString = streamReader.ReadLine();
            Data packet = JsonConvert.DeserializeObject<Data>(PacketString);

            PacketDelegate(packet);
        } 
    }

    public void Logout()
    {
        Data Packet = new Data();
        Packet.cmdCommand = Command.Logout;
        Packet.strMessage = "LOGOUT";
        Packet.strName = "Game";
        SendMessage(Packet);

        if (streamReader != null && streamWriter != null)
        {
            streamReader.Dispose();
            streamWriter.Dispose();
            TCPClient.Close();
            TCPClient = null;
            streamReader = null;
            streamWriter = null;
        }

        StreamTimer.Stop();
    }

    public void SendMessage(Data packet)
    {
        string packetMessage = JsonConvert.SerializeObject(packet);

        try
        {
            streamWriter.WriteLine(packetMessage);
            streamWriter.Flush();
        } catch (Exception e)
        {

        }
    }

    public void RegisterActionDelegate(Action<Data> RegisterDelegate)
    {
        PacketDelegate += RegisterDelegate;
    }

    public void UnRegisterActionDelegate(Action<Data> UnregisterDelegate)
    {
        PacketDelegate -= UnregisterDelegate;
    }
}

我不确定发生了什么,或者是否还有其他需要添加到系统中的额外检查。 注意:它是TCP,所以“当”完全有效时,我可以将客户端放入我可能写的可能不完全依赖或使用Unity的其他程序中。

  1. new TcpClient("127.0.0.1", Port)不适合客户端。 只需使用TcpClient() 没有必要指定IP和端口,这两者最终都会出错。
  2. TCPClient.Available几乎总是一个bug。 您似乎假设TCP是基于数据包的。 您无法测试是否传入完整邮件。 TCP仅提供无边界字节流。 因此,此Available检查不会告诉您整行是否可用。 此外,可能有多行。 读取的正确方法是始终运行读取循环,只需读取行而不进行检查。 到达的任何行都将以这种方式处理。 不需要计时器等
  3. 服务器有同样的问题。

问题(2)可能以某种方式导致丢失数据包的出现。 无论如何你需要解决这个问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM