【问题标题】:How to build a Client/Server application correctly?如何正确构建客户端/服务器应用程序?
【发布时间】:2018-01-22 04:01:29
【问题描述】:

我正在构建一个客户端/服务器应用程序以了解基于客户端/服务器的游戏如何工作,但该应用程序似乎无法正常运行,这是我的问题:

  1. 为什么我关闭客户端应用时这个应用不能写“玩家XXX离开”
  2. 如何正确接收来自服务器的数据? (我想从服务器接收位置信息以在客户端呈现它)

这里是完整的代码:

服务器:

public class ServerApp
{
    public class Point
    {
        public int x;
        public int y;
    }

    TcpListener listener;
    List<Player> players;

    string[,] data = new string[20, 40];

    public ServerApp()
    {
        players = new List<Player>();
    }

    void PlayerJoin(TcpClient client)
    {
        lock (data)
        {
            if (data[0, 0] == "*")
            {
                data[0, 0] = "O";
                Player player = new Player();
                player.Entity = "O";
                player.Location = new Vector2(0,0);
                player.Client = client;
                players.Add(player);
            }
            else if (data[0, 39] == "*")
            {
                data[0, 40] = "P";
                Player player = new Player();
                player.Entity = "P";
                player.Location = new Vector2(0, 0);
                player.Client = client;
                players.Add(player);
            }
            else if (data[19, 0] == "*")
            {
                data[20, 0] = "I";
                Player player = new Player();
                player.Entity = "I";
                player.Location = new Vector2(0, 0);
                player.Client = client;
                players.Add(player);
            }
            else if (data[19, 39] == "*")
            {
                data[20, 40] = "U";
                Player player = new Player();
                player.Entity = "U";
                player.Location = new Vector2(0, 0);
                player.Client = client;
                players.Add(player);
            }
        }
    }

    void InitData(ref string[,] data)
    {
        for (int i = 0; i < 20; i++)
        {
            for (int j = 0; j < 40; j++)
            {
                if (i != 0 && i != 19)
                {
                    if (j != 0 && j != 39)
                    {
                        data[i, j] = " ";
                    }
                    else
                    {
                        data[i, j] = "*";
                    }
                }
                else
                {
                    data[i, j] = "*";
                }
            }
        }
    }

    void InitServer()
    {
        listener = new TcpListener(new IPAddress(new byte[] { 127, 0, 0, 1 }), 6666);
        listener.Start();
    }

    void SyncDataToAllClient()
    {
        foreach (var player in players)
        {
            StreamWriter sw = new StreamWriter(player.Client.GetStream());
            int[] pos = new int[2];
            pos[0] = player.Location.x;
            pos[1] = player.Location.y;
            sw.Write(pos[0]);
            sw.Write(pos[1]);
            sw.Close();
        }
    }

    public void Run()
    {
        InitServer();
        InitData(ref data);

        while (true)
        {
            if(listener.pending())
            {
            var myclient = listener.AcceptTcpClient();
            if (myclient != null)
            {
                Console.WriteLine(string.Format("Client from {0} connected!", ((IPEndPoint)myclient.Client.RemoteEndPoint).Address));
                PlayerJoin(myclient);

                Thread.Sleep(1000 * 5);

                StreamWriter sw = new StreamWriter(myclient.GetStream());
                sw.Write(data);
            }
            CheckClient();
            SyncDataToAllClient();
            }
        }
    }

    private void CheckClient()
    {
        foreach (var p in players)
        {
            if (!p.Client.Connected)
            {
                Console.WriteLine("Player {0} leave!", ((IPEndPoint)p.Client.Client.RemoteEndPoint).Address);
                players.Remove(p);
                data[p.Location.y, p.Location.x] = "*";
            }
        }
    }
}

客户:

public class ClientApp
{
    TcpClient client;

    string[,] data = new string[20, 40];
    bool quit;
    public void Run()
    {
        EstablishConntionToServer();
        InitData(ref data);
        while (!quit)
        {
            SyncDataFromServer(ref data);
            PrintData(data);
        }
    }

    void EstablishConntionToServer()
    {
        client = new TcpClient();
        client.Connect(new IPAddress(new byte[] { 127, 0, 0, 1 }), 6666);
    }

    void SyncDataFromServer(ref string[,] data)
    {
        Stream s = client.GetStream();
        char[] buffer=new char[1];
        StreamReader sr = new StreamReader(s);
        sr.Read(buffer,0,1);
    }

    void InitData(ref string[,] data)
    {
        for (int i = 0; i < 20; i++)
        {
            for (int j = 0; j < 40; j++)
            {
                //data[i, j] = "*";
                if (i != 0 && i != 19)
                {
                    if (j != 0 && j != 39)
                    {
                        data[i, j] = " ";
                    }
                    else
                    {
                        data[i, j] = "*";
                    }
                }
                else
                {
                    data[i, j] = "*";
                }
            }
        }
    }

    void PrintData(string[,] data)
    {
        Console.Clear();
        for (int i = 0; i < 20; i++)
        {
            for (int j = 0; j < 40; j++)
            {
                Console.Write(data[i, j]);
            }
            Console.Write(Environment.NewLine);
        }

        System.Threading.Thread.Sleep(500);
    }
}

【问题讨论】:

  • 假设的“C/S”是指“客户端/服务器”?您的问题的某些部分暗示了这一点,但从未明确说明过。
  • 你的问题太宽泛了。对最简单部分的简短回答:您无法检查客户端状态,因为您的代码卡在AcceptTcpClient() 调用中。如果您使用调试器查看代码在做什么,您就会看到。解决此类问题的常用方法是编写异步代码,这样您就可以在网络 I/O 事件发生时对其进行响应,而不会阻止任何一个线程响应。我有一个例子,在我最近发布的this answer 中。

标签: c# networking


【解决方案1】:

解决方案:

为了通过服务器传输客户端移动,您可能希望使用 UDP 来提高速度。这是游戏中经常需要的最有效的数据传输方式。

至于您的其他问题,它很可能不知道客户端离开了,您的连接可能从未正确关闭,因此连接必须超时才能被识别为已关闭。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多