我有一堆客户端连接到服务器。当客户端将数据发送到服务器时,它会创建一个线程来处理消息。
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命令。
我对我打算如何解决这个问题有一个模糊的想法,但是我对自己从来没有太大的信心,所以第二种意见确实会走很长一段路。
我的计划是创建一个线程,以循环浏览队列中的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 ) );
}
}
}
}