我是一名经验丰富的C#开发人员,但到目前为止我还没有开发过TCP服务器应用程序.现在我必须开发一个高度可扩展的高性能服务器,它可以处理至少5-10万个并发连接:通过GPRS从GPS设备获取字节数据.
常见的通信过程应如下所示:
所以,在我的服务器中我需要
我开始通过互联网阅读这个主题,但对我来说这似乎是一场噩梦.有很多方法,但我找不出哪个是最好的.
异步套接字方法对我来说似乎是最好的,但是以这种异步方式编写代码非常糟糕且不易调试.
所以我的问题是:您认为在C#中实现高性能TCP服务器的最佳方式是哪种?你知道任何好的开源组件吗?(我尝试了几个,但我找不到一个好的.)
Rem*_*anu 40
它必须是异步的,没有办法解决这个问题.高性能和可扩展性不会与每个插槽的单线程混合使用.您可以查看StackExchange自己正在做什么,请参阅异步Redis等待BookSleeve,它利用了下一个C#版本中的CTP功能(因此处于边缘并且可能会发生变化,但很酷).为了获得更多的优势,解决方案围绕着利用SocketAsyncEventArgs类进行了演变,通过消除与"经典"C#异步处理相关联的异步处理程序的频繁分配,使得事情更进一步:
SocketAsyncEventArgs类是System.Net.Sockets.Socket类的一组增强的一部分,它们提供了可供专用高性能套接字应用程序使用的备用异步模式.此类专为需要高性能的网络服务器应用程序而设计.应用程序可以仅使用增强型异步模式,也可以仅在目标热区域中使用(例如,在接收大量数据时).
长话短说:学习异步或死于尝试......
顺便说一下,如果你问为什么是异步,那么请阅读这篇文章中链接的三篇文章:高性能Windows程序.最终的答案是:底层操作系统设计需要它.
小智 11
正如Remus上面所说,你必须使用异步来保持高性能.那是.NET中的Begin .../End ...方法.
在套接字的引擎盖下,这些方法使用IO完成端口,这似乎是在Windows操作系统上处理许多套接字的最高性能方式.
正如Jim所说,TcpClient类可以在这里提供帮助并且非常易于使用.下面是使用TcpListener监听传入连接和TcpClient处理它们的示例,初始BeginAccept和BeginRead调用是异步的.
这个例子假设在套接字上使用了基于消息的协议,除了每个传输的前4个字节是长度之外,它被省略,但是这允许你在流上使用同步Read来获取其余的数据已经缓冲了.
这是代码:
class ClientContext
{
public TcpClient Client;
public Stream Stream;
public byte[] Buffer = new byte[4];
public MemoryStream Message = new MemoryStream();
}
class Program
{
static void OnMessageReceived(ClientContext context)
{
// process the message here
}
static void OnClientRead(IAsyncResult ar)
{
ClientContext context = ar.AsyncState as ClientContext;
if (context == null)
return;
try
{
int read = context.Stream.EndRead(ar);
context.Message.Write(context.Buffer, 0, read);
int length = BitConverter.ToInt32(context.Buffer, 0);
byte[] buffer = new byte[1024];
while (length > 0)
{
read = context.Stream.Read(buffer, 0, Math.Min(buffer.Length, length));
context.Message.Write(buffer, 0, read);
length -= read;
}
OnMessageReceived(context);
}
catch (System.Exception)
{
context.Client.Close();
context.Stream.Dispose();
context.Message.Dispose();
context = null;
}
finally
{
if (context != null)
context.Stream.BeginRead(context.Buffer, 0, context.Buffer.Length, OnClientRead, context);
}
}
static void OnClientAccepted(IAsyncResult ar)
{
TcpListener listener = ar.AsyncState as TcpListener;
if (listener == null)
return;
try
{
ClientContext context = new ClientContext();
context.Client = listener.EndAcceptTcpClient(ar);
context.Stream = context.Client.GetStream();
context.Stream.BeginRead(context.Buffer, 0, context.Buffer.Length, OnClientRead, context);
}
finally
{
listener.BeginAcceptTcpClient(OnClientAccepted, listener);
}
}
static void Main(string[] args)
{
TcpListener listener = new TcpListener(new IPEndPoint(IPAddress.Any, 20000));
listener.Start();
listener.BeginAcceptTcpClient(OnClientAccepted, listener);
Console.Write("Press enter to exit...");
Console.ReadLine();
listener.Stop();
}
}
Run Code Online (Sandbox Code Playgroud)
它演示了如何处理异步调用,但它需要添加错误处理,以确保TcpListener始终接受新连接,并在客户端意外断开连接时进行更多错误处理.此外,确实存在一些情况,其中并非所有数据都一次性到达,也需要处理.