.net NET客户端-服务器应用程序被冻结,需要想法来修复!

.net NET客户端-服务器应用程序被冻结,需要想法来修复!,.net,client,.net,Client,他是堆栈溢出的好人, 我有一个.NET客户机服务器应用程序,它运行着几百个客户机。该项目大约在一年前从VB6迁移到.NET,是一个用于纸牌/棋盘游戏的平台。 尽管我将在下面尽可能多地提供细节,但问题是当有40-70名玩家在里面时,频道会被冻结 架构 1。服务器(.NET 4.0) 分为三个项目:ServerNET、Listener和Channel 侦听器的作用类似于一个登录服务器,客户端首先连接到该服务器。它负责检查版本和帐户信息等内容。 还允许客户端选择要连接的通道。这基本上是一段时间内的一个

他是堆栈溢出的好人,
我有一个.NET客户机服务器应用程序,它运行着几百个客户机。该项目大约在一年前从VB6迁移到.NET,是一个用于纸牌/棋盘游戏的平台。 尽管我将在下面尽可能多地提供细节,但问题是当有40-70名玩家在里面时,频道会被冻结

架构

1。服务器(.NET 4.0)

  • 分为三个项目:ServerNET、Listener和Channel
  • 侦听器的作用类似于一个登录服务器,客户端首先连接到该服务器。它负责检查版本和帐户信息等内容。 还允许客户端选择要连接的通道。这基本上是一段时间内的一个窃听者,倾听任何试图永远联系的人。这不是双方都被冻结的原因
  • 通道表示一个端口,客户端在使用侦听器后连接到通道。很像航天飞机,这是主要部分。与MIRC频道类似,它将所有用户绑定在同一频道内,大部分数据发送给同一频道内的人,如聊天和由服务器托管的其他玩家创建的游戏。这是一个控制台应用程序,是玩家的中心。玩家信息保存在“客户端”类中,该类包括一个TCPClient和一些其他属性。每个客户端都运行一个线程并进行异步调用,这些调用由服务器处理。此外,这些“客户端”对象保存在名为“ClientCollection”的集合类中。当里面大约有40-70名玩家时,这个频道就会被冻结。每个频道最多允许100名玩家
  • ServerNET是整个系统的主体,负责所有其他与整个系统相关的一般事务,而不是通道专用。这是一个表单应用程序,运行服务器选项之类的东西
2。客户端(.NET 2.0)

  • 使用TCPClient运行,主要是单线程,而服务器是多线程
  • 必须使用.NET2.0
  • 主要由视觉和其他不重要的东西组成
当有40多个客户端连接到一个通道时,它开始被完全随机冻结(或者这就是我们现在所拥有的,没有证据或足够的数据来指出什么是完全错误的)。我们真的不认为网络流量是问题所在(目前还不太确定),因为我们已经在不同的服务器机器上尝试了不同的设置。我们使用的所有服务器机器都能够以硬件方式处理那么多进程。因此,它是关于方法和代码方面发生了什么

我们之所以努力解决这个问题,是因为我们不确定是什么原因造成的。请查看以下示例:
系统A有55个人在他们的频道1上在线,而且它不会被冻结。系统A使用A1 IP,通道位于16xxx端口。
系统B有25个人在他们的频道4中在线,它会被冻结,就像一到两分钟一样。系统B使用B1 IP和18xxx作为通道端口。它与系统A在同一台机器上,不会冻结

作为结论,这看起来与在线人数无关,但当人数增加时,这种情况更为常见

我们尝试在通道项目中以无休止的do循环滚动应用程序.DoEvents(),认为某个X进程导致通道进入冻结状态几分钟,从而导致通道暂停。然后,它在几秒钟内执行冻结时排队的每个操作。每个通道的CPU使用率平均在7%-20%之间,看起来情况正在好转。然而,这并不是永久有效的解决办法

我们怀疑的事情:

  • 保存玩家和TCP客户端的ClientCollection从CollectionBase继承。也许这在同步过程中造成了一些混乱。这在当时是一个数组,我们遇到的这些问题较少。也许它不应该从CollectionBase继承,但是其他的东西呢
  • 我们正在使用SyncLock(C#中的锁)同步ClientCollection。(尽管我们在开始使用锁之前就遇到了这个问题)
服务器信息
英特尔至强X3460 2.80GHz
16 GB内存
64位Windows Server 2008企业版

我知道在没有看到全部代码的情况下是不可能解决这个问题的,但是我很遗憾我无法发布代码。相反,我在寻找一个想法,把我带到某个方向。但是,我们很乐意分享解决此问题的任何其他信息


谢谢大家的帮助

我们在一个非常类似的应用上遇到了一个非常类似的问题(我们的统计数据发布到了约1300个用户)

我最好的猜测是,在您的TCPClient上,您设置了一个无限超时。不幸的是,这是默认行为。因此,当TCPClient在读取时阻塞时,它有时会完全冻结

将超时设置为30秒(或者更适合您的情况)


对我来说,在服务器应用程序中使用同步套接字是一个很大的禁忌。不要为每个连接的客户端使用一个线程。不要使用
TcpClient.Read
/
TcpClient.Send

阅读
BeginRead
/
EndRead
+
BeginSend
/
EndSend
方法。它们的伸缩性比使用线程和同步方法要好得多

更新 异步读取并不意味着不能同步处理读取命令。读取async的原因是为了能够获得完整的命令,而不必为每个客户机使用自己的线程

为阅读做如下操作:

  • 开始
  • 在OnRead(起始回调)中。调用EndRead
  • 0字节=断开连接
  • 将接收到的数据追加到缓冲区(如果命令是字符串,请使用StringBuilder,不要将字符串用作缓冲区)
  • 检查缓冲区是否包含完整的包
  • 调用您的方法/ev
    TcpClient newClient = incoming.AcceptTcpClient();
    newClient.NoDelay = true;  // Send & receive immediately, even when the buffers aren't full
    newClient.ReceiveTimeout = 30000;
    newClient.SendTimeout = 30000;