网络客户端同时线程所有调用SQL命令

时间:2019-06-08 16:48:46

标签: c# sql multithreading sockets tcp

我有一堆客户端连接到服务器。当客户端将数据发送到服务器时,它会创建一个线程来处理消息。

        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,并在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 ) );
            }
        }
    }
}

0 个答案:

没有答案