Все сетевые клиенты выполняют все вызывающие команды SQL одновременно - PullRequest
0 голосов
/ 08 июня 2019

У меня есть группа клиентов, подключенных к серверу. Когда клиент отправляет данные на сервер, он создает поток для обработки сообщения.

        serverSocket.BeginAccept( new AsyncCallback( AcceptCallback ), null );

    //client.cs
    private void RecieveCallback( IAsyncResult ar )
    {
        Socket socket = (Socket)ar.AsyncState;

        try
        {
            int recieved = socket.EndReceive( ar );

            if ( recieved <= 0 )
            {
                CloseClient( index );
            }
            else
            {
                byte[] dataBuffer = new byte[recieved];
                Array.Copy( buffer, dataBuffer, recieved );
                ServerHandleNetworkData.HandleNetworkInformation( index         //ServerTCP.cs has an static array of clients
                                                                                //index is this clients position in the array
                                                                , dataBuffer
                                                                );
                socket.BeginReceive( buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback( RecieveCallback ), socket );
            }
        }
        catch ( Exception e )
        {
            Console.WriteLine( "[Client::RecieveCallBack] " + e.Message, index );
            CloseClient( index );
        }
    }

Эти потоки вызывают статические методы, которые обрабатывают информацию о сети. Иногда они также вызывают команды SQL.

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

example

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

Мой план заключается в том, чтобы создать поток для циклического перемещения по списку команд SQL в очереди.

При обработке сетевых данных клиенты сначала подписываются на событие вызываемой команды, при этом они также передают делегата, который будет вызван для обозначения окончания событий. Этому делегату передаются данные, возвращаемые командой sql. Затем они ставят в очередь вызываемую команду и передают данные параметров для команды SQL.

Я упаковал все свои команды SQL в такой класс.

    public static void SetEntityCoodinates( int x, int y, int ID )
    {
        SQLReader.RunQuery( string.Format( @"UPDATE entities SET CellPosX = {0}, CellPosY = {1} WHERE entities.ID = {2};", x, y, ID )
                          , SQLReader.Stream.INPUT
                          , null
                          );
    }

Очевидно, я мог бы оставить себя открытым для SQL-инъекций, но я бы хотел заняться одной проблемой за раз.

Макет. Извините за пост полного класса, я постараюсь разбить его. HandleNetworkData (который вызывается клиентским потоком) сначала подписывается на SQLThread, передавая команду типа enum и делегат, который запускается, когда SQLThread обрабатывает событие. Оглядываясь назад, сказать особо нечего. Я мог бы заменить списки очередями, но все остальное довольно просто, я думаю. Я надеюсь. Не очень нравится выражение switch, очень уродливо.

Пожалуйста, дайте мне знать, если вы думаете, что я делаю гору из мухи слона, и если есть какое-то простое решение, которого я не вижу. Любая обратная связь будет принята с благодарностью.

class SQLThread
{
    public static Dictionary<CommonSQL.Commands, Action<object[]>> m_eventHandler = new Dictionary<CommonSQL.Commands, Action<object[]>>();

    private static List<CommonSQL.Commands> m_executingEvents = new List<CommonSQL.Commands>();
    private static List<object[]> m_executingData = new List<object[]>();

    private static List<CommonSQL.Commands> m_queuedEvents = new List<CommonSQL.Commands>();
    private static List<object[]> m_queuedData = new List<object[]>();

    private static System.Object m_queueLock = new System.Object();

    public static void AddEvent( CommonSQL.Commands packet, Action<object[]> e)
    {
        m_eventHandler.Add( packet, e );
    }
    public static void RemoveEvent(CommonSQL.Commands packet)
    {
        m_eventHandler.Remove(packet);
    }
    public static void QueueEvent( CommonSQL.Commands action, object[] sender )
    {
        lock ( m_queueLock )
        {
            m_queuedEvents.Add( action );
            m_queuedData.Add( sender );
        }
    }
    private static void MoveQueuedEventsToExecuting()
    {
        lock ( m_queueLock )
        {
            while ( m_queuedEvents.Count > 0 )
            {
                CommonSQL.Commands e = m_queuedEvents[0];
                object[] data = m_queuedData[0];

                m_executingEvents.Add( e );
                m_executingData.Add( data );

                m_queuedEvents.RemoveAt( 0 );
                m_queuedData.RemoveAt( 0 );

            }
        }
    }
    public void Update()
    {
        MoveQueuedEventsToExecuting();

        while ( m_executingEvents.Count > 0 )
        {
            CommonSQL.Commands action = m_executingEvents[0];
            object[] data = m_executingData[0];

            m_executingEvents.RemoveAt( 0 );
            m_executingData.RemoveAt( 0 );

            if ( m_eventHandler.ContainsKey(action) )
            {
                object[] returnedData = null;

                switch ( action )
                {
                    case CommonSQL.Commands.SetEntityCoordinates:
                        returnedData = CommonSQL.SetEntityCoodinates( parameterData );
                        break;
                    case CommonSQL.Commands.GetMapData:
                        returnedData = CommonSQL.GetMapData( parameterData );
                        break;
                    case CommonSQL.Commands.GetEntityCoordinates:
                        returnedData = CommonSQL.GetEntityCoordinates( parameterData );
                        break;
                    case CommonSQL.Commands.GetEntityID:
                        returnedData = CommonSQL.GetEntityID( parameterData );
                        break;
                    case CommonSQL.Commands.GetPlayerIDAndPassword:
                        returnedData = CommonSQL.GetPlayerIDAndPassword( parameterData );
                        break;
                    case CommonSQL.Commands.CreateAccount:
                        returnedData = CommonSQL.CreateAccount( parameterData );
                        break;
                    case CommonSQL.Commands.CreateEntity:
                        returnedData = CommonSQL.CreateEntity( parameterData );
                        break;
                }

                m_eventHandler[action].Invoke( returnedData );
            }
            else
            {
                Console.WriteLine( string.Format( "No instruction for {0}.", action ) );
            }
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...