[英]C# TCP Connection saving clients and broadcasting to them
为了练习,我想创建客户端和服务器应用程序以模拟大厅。
因此,在服务器应用程序中,我接受传入连接,创建一个包含TcpClient对象,用户名,ID等以及发送和接收数据的方法的ClientInfo对象,并将该ClientInfo对象存储在我的大厅类的List中。 当用户执行诸如聊天之类的操作时,该消息将被发送到服务器并广播到所有可用的客户端。
我遇到的问题是:第一个客户端连接。 广播转到DefaultUser1。 第二个客户端连接。 广播转到DefaultUser2 + DefaultUser2。
如您所见,第一个客户端不再接收数据,服务器也不再从他那里接收数据。 列表中的数据以某种方式必须被破坏。 这是相关的代码位:
接受传入的对接并创建ClientInfo对象并将其存储到大厅:
while (mWorking)
{
TcpClient client = mListener.AcceptTcpClient();
mNumberOfClients++;
Console.WriteLine("New Tcp-Connection with client: " + client.Client.LocalEndPoint.ToString());
ClientInfo newInfo = new ClientInfo(client, mNumberOfClients);
mLobby.AddClient(newInfo);
}
ClientInfo构造函数:
public ClientInfo(TcpClient client, int clientNumber)
{
mClient = client;
mClientNumber = clientNumber;
mUsername = "DefaultUser" + mClientNumber.ToString();
mStream = client.GetStream();
mEncoding = new ASCIIEncoding();
}
ClientInfo中的发送方法:
public void Send(String message)
{
mCurrentMessage = message;
Thread sendThread = new Thread(this.WriteTask);
sendThread.Start();
}
private void WriteTask()
{
byte[] data = mEncoding.GetBytes(mCurrentMessage);
byte[] sizeinfo = new byte[4];
sizeinfo[0] = (byte)data.Length;
sizeinfo[1] = (byte)(data.Length >> 8);
sizeinfo[2] = (byte)(data.Length >> 16);
sizeinfo[3] = (byte)(data.Length >> 24);
mStream.Write(sizeinfo, 0, sizeinfo.Length);
mStream.Write(data, 0, data.Length);
}
大堂类的相关代码:
private static List<ClientInfo> mClients;
private static processDel mProcessDel;
public Lobby(processDel del)
{
mProcessDel = del;
mClients = new List<ClientInfo>();
}
public void AddClient(ClientInfo client)
{
mClients.Add(client);
client.Listen(mProcessDel);
Broadcast("UJOIN§" + client.username + "$");
}
public void Broadcast(String message)
{
for (int i = 0; i < mClients.Count; i++)
{
Console.WriteLine("Broadcasting to " + mClients[i].username);
mClients[i].Send(message);
}
}
我也尝试过用foreach进行广播,结果相同。 processDel是我处理接收到的数据所需的委托方法。 接收由每个客户端的单独线程处理。
猜测似乎是您误解了C#中static
含义。
static
表示方法或字段是类型的一部分,而不是类型的实例。 因此,如果所有字段都是静态的,则实际上没有任何实例数据,并且所有状态在类的所有实例之间共享-因此,第二个客户端也将覆盖与第一个客户端关联的所有数据。 解决方案很简单-只需删除static
,就可以了。
除此之外,您的代码还有一些线程安全问题。 默认情况下,.NET中的大多数类型都不是线程安全的,因此您需要添加适当的锁定以确保保持一致性。 也许,这更多是CodeReview的主题,所以我只想起想到的第一件事:
Send
总是启动一个新线程来发送消息。 但是,这也意味着,如果在正确的条件下连续两次调用它,则它可能会完全破坏您的TCP流-例如,第一个线程可能会写入长度数据,然后第二个线程会在写入第一个线程之前写入其长度数据。实际数据,您遇到了麻烦。 您也可能只发送了第二条消息两次,因为您正在传递文本以通过字段发送。 List<T>
不是线程安全的。 这意味着您只能在单个线程中安全地使用它-从代码中还不能完全清楚,但是似乎您可能会遇到麻烦。 使用类似ConcurrentDictionary<IPEndPoint, ClientInfo>
可能是更好的主意,但这实际上取决于您在做什么。 您还可以探索一些替代选项,例如使用异步I / O代替垃圾邮件线程,但这是更高级的选项(请注意,多线程甚至更糟:))。 无论如何,线程安全的一个好的开始应该是http://www.albahari.com/threading/,虽然有些长,但是多线程是一个非常复杂和危险的主题,它往往会产生难以解决的错误查找并复制,尤其是在调试器中运行时。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.