在C#中编写高性能服务器,通常需要结合异步编程、IO操作优化、线程管理等多种技术手段,以下是一些关键步骤和代码示例,帮助你构建一个高效的C#服务器。
使用Socket
类并结合异步方法来处理客户端连接和数据传输,下面是一个基本的异步TCP服务器示例:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
class AsyncTcpServer
{
private static readonly int Port = 12345;
private static Socket listener;
static void Main()
{
listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, Port);
listener.Bind(localEndPoint);
listener.Listen(100);
Console.WriteLine($"Server is listening on port {Port}...");
AcceptConnections();
}
private static async void AcceptConnections()
{
while (true)
{
Socket handler = await listener.AcceptAsync();
Console.WriteLine("Client connected!");
// Handle the client connection in a separate task
_ = Task.Run(() => HandleClient(handler));
}
}
private static async void HandleClient(Socket handler)
{
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = await handler.ReceiveAsync(new ArraySegment<byte>(buffer), SocketFlags.None)) > 0)
{
string receivedData = Encoding.UTF8.GetString(buffer, 0, bytesRead);
Console.WriteLine($"Received: {receivedData}");
// Echo back the received data to the client
await handler.SendAsync(new ArraySegment<byte>(buffer, 0, bytesRead), SocketFlags.None);
}
handler.Shutdown(SocketShutdown.Both);
handler.Close();
}
}
这个示例展示了如何使用Socket
类的异步方法来接受客户端连接并处理数据传输,通过AcceptAsync
方法非阻塞地接受新的客户端连接,并为每个连接创建一个新任务来处理数据接收和发送。
二、使用SocketAsyncEventArgs
优化性能
为了进一步提高性能,可以使用SocketAsyncEventArgs
类,它提供了更高效的事件驱动模型,避免了为每个I/O操作分配和释放资源,以下是一个使用SocketAsyncEventArgs
的示例:
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
class AsyncTcpServerWithSAEA
{
private static readonly int Port = 12345;
private static Socket listener;
private static List<SocketAsyncEventArgs> pool;
private static SemaphoreSlim semaphore;
static void Main()
{
listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, Port);
listener.Bind(localEndPoint);
listener.Listen(100);
Console.WriteLine($"Server is listening on port {Port}...");
pool = new List<SocketAsyncEventArgs>();
semaphore = new SemaphoreSlim(0, 1);
StartAccepting();
}
private static void StartAccepting()
{
if (pool.Count > 0)
{
SocketAsyncEventArgs args = null;
lock (pool)
{
args = pool[0];
pool.RemoveAt(0);
}
args.AcceptSocket = args.UserToken = listener;
bool willRaiseEvent = listener.AcceptAsync(args);
if (!willRaiseEvent)
{
ProcessAccept(args);
}
}
else
{
var args = new SocketAsyncEventArgs();
args.Completed += OnAcceptCompleted;
args.AcceptSocket = args.UserToken = listener;
bool willRaiseEvent = listener.AcceptAsync(args);
if (!willRaiseEvent)
{
ProcessAccept(args);
}
}
}
private static void OnAcceptCompleted(object sender, SocketAsyncEventArgs e)
{
ProcessAccept(e);
}
private static void ProcessAccept(SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
Console.WriteLine("Client connected!");
e.AcceptSocket.BeginReceive(e.Buffer, 0, e.Buffer.Length, SocketFlags.None, OnReceiveCompleted, e);
}
else
{
Console.WriteLine($"Accept failed: {e.SocketError}");
}
StartAccepting();
}
private static void OnReceiveCompleted(Socket socket, SocketAsyncEventArgs e)
{
if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
{
string receivedData = Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred);
Console.WriteLine($"Received: {receivedData}");
socket.BeginSend(e.Buffer, e.Offset, e.BytesTransferred, SocketFlags.None, OnSendCompleted, e);
}
else if (e.BytesTransferred == 0 || e.SocketError != SocketError.Success)
{
Console.WriteLine($"Receive failed: {e.SocketError}");
socket.Close();
}
StartAccepting();
}
private static void OnSendCompleted(Socket socket, SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
e.SetBuffer(new byte[1024], 0, 1024);
socket.BeginReceive(e.Buffer, 0, e.Buffer.Length, SocketFlags.None, OnReceiveCompleted, e);
}
else
{
Console.WriteLine($"Send failed: {e.SocketError}");
socket.Close();
}
StartAccepting();
}
}
在这个示例中,我们使用了一个对象池(List<SocketAsyncEventArgs>
)来重用SocketAsyncEventArgs
实例,减少了内存分配和垃圾回收的开销,通过事件驱动的方式处理I/O操作,提高了服务器的响应能力和吞吐量。
除了使用原生的Socket
类外,还可以考虑使用一些成熟的第三方网络库来简化开发过程并提高性能,SuperSocket是一个基于C#的高性能Socket框架,支持TCP、UDP、HTTP等多种协议,提供了丰富的功能和良好的扩展性,以下是一个简单的SuperSocket使用示例:
安装SuperSocket包:
Install-Package SuperSocket.Common
Install-Package SuperSocket.SocketBase
创建一个简单的TCP服务器:
using System;
using System.Text;
using SuperSocket.SocketBase;
using SuperSocket.SocketBase.CmdParser;
using SuperSocket.SocketBase.Config;
using SuperSocket.SocketEngine;
using SuperSocket.SocketEngine.Configuration;
using SuperSocket.SocketEngine.Core;
using SuperSocket.SocketEngine.Protocol;
using SuperSocket.SocketEngine.Session;
using SuperSocket.SocketEngine.Transport;
using SuperSocket.SocketEngine.Exception;
using SuperSocket.ProtoBase;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.Linq;
using System.Reflection;
using System.Diagnostics;
using System.Security;
using System.Security.Permissions;
using System.Runtime.CompilerServices;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.IO;
using System.Collections;
using System.Collections.Specialized;
using System.Collections.ObjectModel;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Discovery;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.MsmqIntegration;
using System.ServiceModel.PeerNames;
using System.ServiceModel.Security;
using System.ServiceModel.Web;
using System.ServiceModel.Activation;
using System.ServiceModel.Hosting;
using System.ServiceModel.Configuration;
using System.ServiceModel.Diagnostics;
using System.ServiceModel.Routing;
using System.ServiceModel.Channels.Framing;
using System.ServiceModel.Channels.Transport;
using System.ServiceModel.Channels.CommunicationPatterns;
using System.ServiceModel.Channels.Bindings;
using System.ServiceModel.Channels.Bindings.CustomBinding;
using System.ServiceModel.Channels.Bindings.BasicHttpBinding;
using System.ServiceModel.Channels.Bindings.NetTcpBinding;
using System.ServiceModel.Channels.Bindings.WSDualHttpBinding;
using System.ServiceModel.Channels.Bindings.WSHttpBinding;
using System.ServiceModel.Channels.Bindings.NetNamedPipeBinding;
using System.ServiceModel.Channels.Bindings.MsmqBinding;
using System.ServiceModel.Channels.Bindings.HttpBinding;
using System.ServiceModel.Channels.Bindings.SecureConversationBinding;
using System.ServiceModel.Channels.Bindings.FederationBinding;
using System.ServiceModel.Channels.Bindings.DualHttpBinding;
using System.ServiceModel.Channels.Bindings.CustomBinding;
using System.ServiceModel.Channels.Bindings.BasicHttpBinding;
using System.ServiceModel.Channels.Bindings.NetTcpBinding;
using System.ServiceModel.Channels.Bindings.WSDualHttpBinding;
using System.ServiceModel.Channels.Bindings.WSHttpBinding;
using System.ServiceModel.Channels.Bindings.NetNamedPipeBinding;
using System.ServiceModel.Channels.Bindings.MsmqBinding;
using System.ServiceModel.Channels.Bindings.HttpBinding;
using System.ServiceModel.Channels.Bindings.SecureConversationBinding;
using System.ServiceModel.Channels.Bindings.FederationBinding;
using System.ServiceModel.Channels.Bindings.DualHttpBinding;