线程将数据发送到错误的客户端。这是线程安全问题吗?
Thread sending data to wrong client. Is this a thread safety issue?
我目前正在开发一个服务器,该服务器使用 consumer/producer 方法处理线程和阻塞集合,如下所示:
public class NetworkClient
{
private bool _started = false;
private int _clientId;
private readonly Socket _socket;
private static AutoResetEvent _lastMessageWasAckd = new AutoResetEvent(true);
private static BlockingCollection<byte[]> _messageQueue = new BlockingCollection<byte[]>();
public NetworkClient(Socket socket, int clientId)
{
this._socket = socket;
this._clientId = clientId;
}
public int getClientID() {
return _clientId;
}
public void SendMessage(string message)
{
Console.WriteLine("Adding to player's sending queue " + _clientId);
_messageQueue.Add(Encoding.ASCII.GetBytes(message));
}
public void Start()
{
Thread receiver = new Thread(new ThreadStart(ReceivingThreadProc));
Thread sender = new Thread(new ThreadStart(SendingThreadProc));
receiver.Start();
sender.Start();
this._started = true;
}
public void Stop()
{
this._started = false;
}
private void ReceivingThreadProc()
{
byte[] bytes = new byte[1024];
string data;
try
{
while (_started && _socket.Connected)
{
int numByte = this._socket.Receive(bytes);
data = Encoding.ASCII.GetString(bytes, 0, numByte);
if (numByte == 0)
{
break;
}
if (data == "ACK")
{
_lastMessageWasAckd.Set();
continue;
}
// Acknowledge the message
_socket.Send(Encoding.ASCII.GetBytes("ACK"));
ServerReceiver.onEvent(this._clientId, data);
}
}
catch (Exception e)
{
this._socket.Close();
}
}
private void SendingThreadProc()
{
while (_started && _socket.Connected)
{
_lastMessageWasAckd.WaitOne();
byte[] message = _messageQueue.Take();
Console.WriteLine("Sending the following message to client number: " + _clientId);
Console.WriteLine(System.Text.Encoding.ASCII.GetString(message));
_socket.Send(message);
_lastMessageWasAckd.Reset();
}
}
}
将为连接服务器的每个客户端创建一个 NetworkClient
实例。问题是有时一条消息排队等待发送到客户端 1(这由 SendMessage
方法中的 Console.Writeline
确认)但是该消息被发送到客户端 0(由控制台写入行显示SendingThreadProc
方法)代替。这是由于线程安全问题,还是我完全遗漏了什么?这通常发生在两条消息接连发送时。
如有任何帮助,我们将不胜感激。
编辑:
正如许多人正确指出的那样,我没有在我调用的地方添加 SendMessage
我将把这个 class 放在下面:
class NetworkServer
{
private int latestClient = 0;
private ServerReceiver _serverReceiver;
private static readonly Dictionary<int, NetworkClient> clients = new Dictionary<int, NetworkClient>();
public NetworkServer()
{
IPEndPoint endpoint = new IPEndPoint(IPAddress.Any, 5656);
Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
this._serverReceiver = new ServerReceiver();
this._serverReceiver.start();
try
{
listener.Bind(endpoint);
listener.Listen(10);
while (true)
{
Socket clientSocket = listener.Accept();
this.OnClientJoin(clientSocket);
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
public static void SendClientMessage(int clientId, string package, CommandType commandType,
Dictionary<string, object> data = null)
{
if (data == null)
{
data = new Dictionary<string, object>();
}
SendClientMessageRaw(clientId, new NetworkCommand(clientId, package, commandType, data).ToJson());
}
public static void SendClientMessageRaw(int id, string message)
{
Console.WriteLine("Supposed to send to client number " + clients[id].getClientID());
clients[id].SendMessage(message);
}
private void OnClientJoin(Socket socket)
{
// Add client to array, perform handshake?
NetworkClient networkClient = new NetworkClient(socket, latestClient);
clients.Add(latestClient, networkClient);
Console.WriteLine("player added :" + latestClient);
networkClient.Start();
latestClient++;
if (latestClient == 2)
{
SendClientMessage(1, "Test", CommandType.Ping, null);
}
}
会不会是因为您的消息队列是静态的,因此在所有 NetworkClient 之间共享?意味着客户端 A 可以为客户端 B 提取消息?
可能就像从属性中删除静态一样简单。
我目前正在开发一个服务器,该服务器使用 consumer/producer 方法处理线程和阻塞集合,如下所示:
public class NetworkClient
{
private bool _started = false;
private int _clientId;
private readonly Socket _socket;
private static AutoResetEvent _lastMessageWasAckd = new AutoResetEvent(true);
private static BlockingCollection<byte[]> _messageQueue = new BlockingCollection<byte[]>();
public NetworkClient(Socket socket, int clientId)
{
this._socket = socket;
this._clientId = clientId;
}
public int getClientID() {
return _clientId;
}
public void SendMessage(string message)
{
Console.WriteLine("Adding to player's sending queue " + _clientId);
_messageQueue.Add(Encoding.ASCII.GetBytes(message));
}
public void Start()
{
Thread receiver = new Thread(new ThreadStart(ReceivingThreadProc));
Thread sender = new Thread(new ThreadStart(SendingThreadProc));
receiver.Start();
sender.Start();
this._started = true;
}
public void Stop()
{
this._started = false;
}
private void ReceivingThreadProc()
{
byte[] bytes = new byte[1024];
string data;
try
{
while (_started && _socket.Connected)
{
int numByte = this._socket.Receive(bytes);
data = Encoding.ASCII.GetString(bytes, 0, numByte);
if (numByte == 0)
{
break;
}
if (data == "ACK")
{
_lastMessageWasAckd.Set();
continue;
}
// Acknowledge the message
_socket.Send(Encoding.ASCII.GetBytes("ACK"));
ServerReceiver.onEvent(this._clientId, data);
}
}
catch (Exception e)
{
this._socket.Close();
}
}
private void SendingThreadProc()
{
while (_started && _socket.Connected)
{
_lastMessageWasAckd.WaitOne();
byte[] message = _messageQueue.Take();
Console.WriteLine("Sending the following message to client number: " + _clientId);
Console.WriteLine(System.Text.Encoding.ASCII.GetString(message));
_socket.Send(message);
_lastMessageWasAckd.Reset();
}
}
}
将为连接服务器的每个客户端创建一个 NetworkClient
实例。问题是有时一条消息排队等待发送到客户端 1(这由 SendMessage
方法中的 Console.Writeline
确认)但是该消息被发送到客户端 0(由控制台写入行显示SendingThreadProc
方法)代替。这是由于线程安全问题,还是我完全遗漏了什么?这通常发生在两条消息接连发送时。
如有任何帮助,我们将不胜感激。
编辑:
正如许多人正确指出的那样,我没有在我调用的地方添加 SendMessage
我将把这个 class 放在下面:
class NetworkServer
{
private int latestClient = 0;
private ServerReceiver _serverReceiver;
private static readonly Dictionary<int, NetworkClient> clients = new Dictionary<int, NetworkClient>();
public NetworkServer()
{
IPEndPoint endpoint = new IPEndPoint(IPAddress.Any, 5656);
Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
this._serverReceiver = new ServerReceiver();
this._serverReceiver.start();
try
{
listener.Bind(endpoint);
listener.Listen(10);
while (true)
{
Socket clientSocket = listener.Accept();
this.OnClientJoin(clientSocket);
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
public static void SendClientMessage(int clientId, string package, CommandType commandType,
Dictionary<string, object> data = null)
{
if (data == null)
{
data = new Dictionary<string, object>();
}
SendClientMessageRaw(clientId, new NetworkCommand(clientId, package, commandType, data).ToJson());
}
public static void SendClientMessageRaw(int id, string message)
{
Console.WriteLine("Supposed to send to client number " + clients[id].getClientID());
clients[id].SendMessage(message);
}
private void OnClientJoin(Socket socket)
{
// Add client to array, perform handshake?
NetworkClient networkClient = new NetworkClient(socket, latestClient);
clients.Add(latestClient, networkClient);
Console.WriteLine("player added :" + latestClient);
networkClient.Start();
latestClient++;
if (latestClient == 2)
{
SendClientMessage(1, "Test", CommandType.Ping, null);
}
}
会不会是因为您的消息队列是静态的,因此在所有 NetworkClient 之间共享?意味着客户端 A 可以为客户端 B 提取消息?
可能就像从属性中删除静态一样简单。