@ShinShil

Как реализовать игровой сервер на c#?

Всем привет! Может ли кто-нибудь объяснить как на C# реализовать игровой сервер. Его задача: держать подключённых пользователей, когда пользователь шлёт сообщение на сервер, сервер должен подключённым пользователям отослать сообщение.

После многих часов гугла и большого количества примеров есть два консольных приложения - GameServer и TestService.

Идея была такая: клиент подключается с помощью метода TcpClient.Connect(), соответственно на сервере это отлавливается с помощью TcpListener.Accept(). На сервере сохраняется клиент - из его потока TcpClient().GetStream() читаются данные с помощью BeginRead(), а при прочтении их можно записать в NetworkStream других клиентов.

Что пошло не так:
В TestService удалось подключиться и отослать ОДНО! сообщение и получить ответ от сервера, после этого свойство TcpClient.Connected стало false со стороны TestService, соответственно я не могу больше использовать этот экземпляр TcpClient, но мне нужно чтобы именно в его stream приходили сообщения от сервера. Со стороны сервера метод BeginRead после получения одного сообщения начинает бесконечно читать пустые сообщения.

Сервер - только метод старт, потому что только он реализован

public void Start()
        {
            try
            {
                TcpListener tcpListener = new TcpListener(IPAddress.Parse(ipAddress), port);
                tcpListener.Start();
                ServerLogger.Log("Server has been started");
                while(true)
                {
                    TcpClient  client = tcpListener.AcceptClient();
                    GameConnections.Add(new GameConnection(client, GameConnections.Count));
                }
            }
            catch(Exception ex)
            {
                ServerLogger.Error(ex.Message);
            }
        }


GameConnection
class GameConnection
    {
        string GameID { get; set; }
        string ConnectionID { get; set; }
        TcpClient Client { get; set; }
        byte[] acceptedData = new byte[1024];
        byte[] sendData = new byte[1024];
        byte[] buffer = new byte[1024];
        NetworkStream stream;
        static void HandleNewClient()
        {

        }
        public GameConnection(TcpClient client, int id)
        {
            Client = client;
            stream = client.GetStream();
            ConnectionID = "connection" + id;
            ServerLogger.Log(string.Format("Client connected. ConnectionID: {1}", GameID, ConnectionID));            
            stream.BeginRead(acceptedData, 0, acceptedData.Length, FirstReadCallback, null);
        }
        public void FirstReadCallback(IAsyncResult ar)
        {
            int bytesRead = stream.EndRead(ar);
            if(bytesRead > 0)
            {
                Console.WriteLine(bytesRead);                
                GameID = Encoding.UTF8.GetString(acceptedData, 0, bytesRead);
                Console.WriteLine("Game id - " + GameID);
                stream.BeginRead(acceptedData, 0, acceptedData.Length, ReadCallback, null);
                ServerLogger.Log(String.Format("{0} game id is {1}", ConnectionID, GameID));
            }else
            {
                ServerLogger.Log(String.Format("Empty first read from client({0})", ConnectionID));
                stream.BeginRead(acceptedData, 0, acceptedData.Length, FirstReadCallback, null);
            }
        }
        public void ReadCallback(IAsyncResult ar)
        {
            int bytesRead = stream.EndRead(ar);
            if(bytesRead > 0)
            {
                string command = Encoding.UTF8.GetString(acceptedData, 0, bytesRead);
                ServerLogger.Log(String.Format("Command from client({0}): {1}", ConnectionID, command));
                string serverAnswer = "some game command";
                Encoding.UTF8.GetBytes(serverAnswer, 0, serverAnswer.Length, sendData, 0);
                stream.Write(sendData, 0, serverAnswer.Length);
                stream.BeginRead(acceptedData, 0, acceptedData.Length, ReadCallback, null);
            }
            else
            {
                ServerLogger.Log(String.Format("Empty read from client({0})", ConnectionID));
                stream.BeginRead(acceptedData, 0, acceptedData.Length, ReadCallback, null);
            }
        }          
    }


TestService
class Program
    {
        static void Main(string[] args)
        {
            TcpClient client = new TcpClient();
            try
            {
                Console.WriteLine("Connecting to server");                
                client.Connect(IPAddress.Parse("127.0.0.1"), 8888);
                using (BinaryWriter writer = new BinaryWriter(client.GetStream()))
                {
                    byte[] data = new byte[1024];
                    string gameID = "1000";                    
                    Encoding.UTF8.GetBytes(gameID, 0, gameID.Length, data, 0);
                    writer.Write(data, 0, gameID.Length);
                    Console.WriteLine(Encoding.UTF8.GetString(data, 0, gameID.Length));
                }
                while (true)
                {
                    string command = Console.ReadLine();
                    if (command == "exit") break;
                    byte[] data = new byte[1024];
                    NetworkStream stream = client.GetStream();
                    using (BinaryWriter writer = new BinaryWriter(stream))
                    {
                        
                        writer.Write(command);
                    }
                    using (BinaryReader reader = new BinaryReader(stream))
                    {
                        reader.Read(data, 0, 1024);
                        Console.WriteLine("Server answer: {0}", Encoding.UTF8.GetString(data));
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            finally
            {
                client.Close();
            }
            Console.ReadLine();
        }
    }
  • Вопрос задан
  • 3023 просмотра
Решения вопроса 1
alexsandr0000
@alexsandr0000
Программист C#/C++/C
1. Посмотрите какой таймаут стоит на сервере и клиенте, т.к. скорее всего после передачи сообщения сокет закрывается по таймауту;
2. Нужно реализовать механизм опроса сервере на предмет новых сообщений в течении определенного интервала времени, который конечно же меньше таймаута. Например, клиент через 100 мс отправляет команду ASK, а сервер в ответ Empty. Если в течении определенного времени клиент не отвечаем, то закрываем его, чтобы не расходовать память.
3. Я бы обменивался с сервером через json или сериализуемыми побайтно структурами, а то по мере усложнения логики замучаетесь.
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы