Проблемы многопоточных сетевых приложений - PullRequest
0 голосов
/ 14 марта 2019

Я просмотрел по меньшей мере сотню потоков, и ни один из них не дал мне решения моей конкретной проблемы, и я устал смотреть,

Я пытаюсь эмулировать игровой сервер runescape в C #,

Мне нужно получить множество соединений, проанализировать запросы и отправить ответы на них, пока игра не загрузит все файлы, а затем перейти к действительному протоколу входа в систему. это должно быть в состоянии обрабатывать несколько соединений в секунду, как, например, я обрабатываю 3 основных протокола: «jaggrab», «ondemand», а затем обычный протокол игры, т.е. вход в систему, обновление плеера и т. д. все они обрабатываются на одном и том же порту

Класс My Server прослушивает соединения в одном потоке, в то время как в другом потоке мой PipelineFactory обрабатывает соединения в очереди по очереди настолько быстро, насколько это возможно, в то время как существует поток TaskPool, который выполняет объекты PoolableTask через свои отдельные интервалы ... это работает нормально, за исключением того, что как только я принимаю соединение, потоки пула и сервера начинают блокироваться, и, более того, объект сервера перестает прослушивать соединения все вместе, но поток, кажется, продолжает работать .. Я предполагаю, что это потому, что TcpListener.Pending () не обновляется? но я не могу найти функцию для обновления этого списка в документах или в любом месте

Кажется, я использую потоки так, как все учебники по многопоточности объясняют их работу? я действительно не понимаю, что я делаю неправильно .. вот важные части моего кода:

MainEntry.cs:

using System; 
using System.Threading;   

using gameserver.evt;
using gameserver.io;
using gameserver;
using gameserver.io.player;

public static class MainEntry
{  

    public static void Main(string[] args)
    { 
        Console.WriteLine("Starting game server..."); 
        new Thread(new ThreadStart(Server.Run)).Start(); 
        new Thread(new ThreadStart(TaskPool.Run)).Start();
        new Thread(new ThreadStart(PipelineFactory.Run)).Start(); 
        //TODO maybe pool these
    }  

} 

Server.cs:

using gameserver.io.player;
using gameserver.io.sql;
using gameserver.model;
using gameserver.model.player;
using util;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using gameserver.io.player.pipeline;
using gameserver.evt;
using util.cache;

namespace gameserver.io
{
    public class Server 
    {

        public static TcpListener serverSocket;
        public  static Dictionary<int, Player> players = new Dictionary<int, Player>(Constants.MaxPlayers); 
        public static Database database; 
        public const int ListenPort = 43594;
        public static bool running = true;
        public static readonly Cache cache = new Cache(Constants.CacheDir); 

        public static void Bind()
        {
            serverSocket = new TcpListener(IPAddress.Parse("0.0.0.0"), ListenPort);

            serverSocket.Start();
            Console.WriteLine("Server started at 0.0.0.0:" + ListenPort); 
        }

        public static void Run()
        {
            if (serverSocket == null)
                Bind();
            while(IsRunning())
            {
            Console.Write("serve"); 
                    var incoming = serverSocket.AcceptTcpClient();
                    if (incoming != null)
                    {
                        PipelineFactory.queue.Enqueue(new PlayerSocket(incoming));
                    }   
                Thread.Sleep(25);
            }  
        }

        private static void Destroy()
        {
            serverSocket.Stop();
            //saveall players 
            //Environment.Exit(0);
        }

        public static bool Online(Player player)
        {
            if(player == null)
            {
                return false;
            }
            for (int i = 0; i < players.Count; i++)
            {
                if (player.username == players[i].username)
                {
                    return true;
                }
            }
            return false;
        }
        public static Database Database => database ?? (database = new Database());
        public static bool IsRunning() => running;
    }
}

TickPool.cs:

using gameserver.io;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace gameserver.evt
{
    public class TaskPool 
    {

        public static bool running = true;
        public static List<PoolableTask> tasks = new List<PoolableTask>(); 

        public static void Run()
        { 
            do
             {
                long cur = Environment.TickCount;

                Console.Write("tick");
                foreach (PoolableTask t in tasks)
                {
                    if (cur - t.last >= t.interval)
                    {
                        if (t == null) 
                            continue; 
                        t.Execute();
                            t.last = cur; 
                    }

                }
                Thread.Sleep(100); 
            } while (Server.IsRunning());
        }

        public static void Add(PoolableTask task)
        {
            if(!tasks.Contains(task))
                tasks.Add(task); 
        } 

        public static void Stop(PoolableTask task)
        {
            if(tasks.Contains(task)) 
                tasks.Remove(task); 
        }

    }
}

PipelineFactory.cs:

using gameserver.evt;
using gameserver.io.player.pipeline;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace gameserver.io.player
{
    // simple but effective state machine for all non player model related protocol
    public class PipelineFactory
    {

        public static Queue<PlayerSocket> queue = new Queue<PlayerSocket>(100);
        private static readonly LoginPipe loginPipe = new LoginPipe();
        private static readonly JaggrabPipe jgpipe = new JaggrabPipe();
        private static readonly HandshakePipe handshake = new HandshakePipe();
        private static readonly OnDemandPipe ondemand = new OnDemandPipe();

        public static void Run()
        {
            while (Server.IsRunning())
            {
                Console.Write("pipe");

                if (queue.Count() > 0)
                {
                    var socket = queue.First();
                    if (socket == null || !socket.GetSocket().Connected
                        || socket.state == PipeState.Play)
                    {
                        queue.Dequeue();
                        return;
                    }

                    switch (socket.state)
                    {
                        case PipeState.Handshake:
                            socket.currentPipeline = handshake;
                            break;
                        case PipeState.Jaggrab:
                            socket.currentPipeline = jgpipe;
                            break;
                        case PipeState.OnDemand:
                            socket.currentPipeline = ondemand;
                            break;
                        case PipeState.LoginResponse:
                        case PipeState.Block:
                        case PipeState.Finalize:
                            socket.currentPipeline = loginPipe;
                            break;

                        case PipeState.Disconnect:
                            //TODO: Database.saveForPlayer
                            socket.Close();
                            break;
                    }
                    try
                    {
                        if (socket.currentPipeline != null)
                            socket.state = socket.currentPipeline.HandleSocket(socket);
                    }
                    catch (Exception)
                    {
                        socket.Close();
                        queue.Dequeue();
                    }

                }
                Thread.Sleep(20);
            }
        }
    }
}

Сам протокол действительно не должен иметь значения, просто знайте, что он обрабатывается асинхронно В глубине души я программист на Java, но сначала пытаюсь углубиться в C # head, поэтому мои соглашения могут быть не идеальными, и я действительно не знаю, как / не понимаю документацию на intellisense, но в какой-то момент плохо изучаю ее

РЕДАКТИРОВАТЬ: я просто хочу отметить, что все работало отлично, прежде чем я попытался использовать потоки для многопоточности, когда у меня была реализация объекта PipelineFactory PoolableTask, он мог обрабатывать несколько соединений и т. Д., И был только основной поток, вызывающий 2 во время обработки циклов все на всем сервере, я пытаюсь распределить нагрузку на процессор, но это не работает для меня LOL

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...