在网络编程中,异步Socket模型主要用于处理高并发连接或避免阻塞线程。C#中有几种常见的异步Socket模型,每种模型都有其特定的用例和适用场景。以下是几种常见的异步Socket模型:
1. 基于回调的异步Socket模型
C#中的Socket
类提供了一组异步方法,如BeginAccept
、BeginConnect
、BeginReceive
和BeginSend
,这些方法使用回调函数来处理异步操作。
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
public class AsynchronousSocketListener
{
private static ManualResetEvent allDone = new ManualResetEvent(false);
public static void StartListening()
{
byte[] bytes = new Byte[1024];
IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 11000);
Socket listener = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
try
{
listener.Bind(localEndPoint);
listener.Listen(100);
while (true)
{
allDone.Reset();
Console.WriteLine("Waiting for a connection...");
listener.BeginAccept(new AsyncCallback(AcceptCallback), listener);
allDone.WaitOne();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
public static void AcceptCallback(IAsyncResult ar)
{
allDone.Set();
Socket listener = (Socket)ar.AsyncState;
Socket handler = listener.EndAccept(ar);
StateObject state = new StateObject();
state.workSocket = handler;
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state);
}
public static void ReadCallback(IAsyncResult ar)
{
String content = String.Empty;
StateObject state = (StateObject)ar.AsyncState;
Socket handler = state.workSocket;
int bytesRead = handler.EndReceive(ar);
if (bytesRead > 0)
{
state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
content = state.sb.ToString();
if (content.IndexOf("<EOF>") > -1)
{
Console.WriteLine("Read {0} bytes from socket. \n Data : {1}", content.Length, content);
}
else
{
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state);
}
}
}
public class StateObject
{
public Socket workSocket = null;
public const int BufferSize = 1024;
public byte[] buffer = new byte[BufferSize];
public StringBuilder sb = new StringBuilder();
}
public static void Main(String[] args)
{
StartListening();
}
}
2. 基于async
/await
的异步Socket模型
使用C#的异步编程模型,Socket
类提供了一组Task
返回的异步方法,如AcceptAsync
、ConnectAsync
、ReceiveAsync
和SendAsync
。
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
public class AsyncSocketServer
{
public static async Task StartServerAsync()
{
IPAddress ipAddress = IPAddress.Loopback;
int port = 11000;
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, port);
Socket listener = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
try
{
listener.Bind(localEndPoint);
listener.Listen(100);
while (true)
{
Console.WriteLine("Waiting for a connection...");
Socket handler = await listener.AcceptAsync();
_ = Task.Run(() => HandleClientAsync(handler));
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static async Task HandleClientAsync(Socket handler)
{
byte[] buffer = new byte[1024];
try
{
while (true)
{
int bytesRead = await handler.ReceiveAsync(buffer, SocketFlags.None);
if (bytesRead == 0)
{
break;
}
string data = Encoding.ASCII.GetString(buffer, 0, bytesRead);
Console.WriteLine("Received : {0}", data);
byte[] msg = Encoding.ASCII.GetBytes("Server Echo: " + data);
await handler.SendAsync(msg, SocketFlags.None);
}
handler.Shutdown(SocketShutdown.Both);
handler.Close();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
public static void Main(String[] args)
{
StartServerAsync().GetAwaiter().GetResult();
}
}
3. 基于事件的异步Socket模型(适用于高级需求)
有时,你可能会使用SocketAsyncEventArgs
类来处理更复杂的异步I/O操作。这个模型通常用于需要高度可扩展性的服务器。
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
public class SocketAsyncEventArgsServer
{
private static Socket _listenerSocket;
private const int Port = 11000;
public static void Main()
{
IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Loopback, Port);
_listenerSocket = new Socket(IPAddress.Loopback.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
_listenerSocket.Bind(localEndPoint);
_listenerSocket.Listen(100);
StartAccept(null);
Console.WriteLine("Press Enter to exit...");
Console.ReadLine();
}
private static void StartAccept(SocketAsyncEventArgs acceptEventArg)
{
if (acceptEventArg == null)
{
acceptEventArg = new SocketAsyncEventArgs();
acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(AcceptEventArg_Completed);
}
else
{
acceptEventArg.AcceptSocket = null;
}
bool willRaiseEvent = _listenerSocket.AcceptAsync(acceptEventArg);
if (!willRaiseEvent)
{
ProcessAccept(acceptEventArg);
}
}
private static void AcceptEventArg_Completed(object sender, SocketAsyncEventArgs e)
{
ProcessAccept(e);
}
private static void ProcessAccept(SocketAsyncEventArgs e)
{
Socket s = e.AcceptSocket;
Console.WriteLine("Client connected.");
// Set up to receive data from the client.
SocketAsyncEventArgs readEventArgs = new SocketAsyncEventArgs();
readEventArgs.SetBuffer(new byte[1024], 0, 1024);
readEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
readEventArgs.UserToken = s;
bool willRaiseEvent = s.ReceiveAsync(readEventArgs);
if (!willRaiseEvent)
{
ProcessReceive(readEventArgs);
}
// Accept the next connection request.
StartAccept(e);
}
private static void IO_Completed(object sender, SocketAsyncEventArgs e)
{
switch (e.LastOperation)
{
case SocketAsyncOperation.Receive:
ProcessReceive(e);
break;
case SocketAsyncOperation.Send:
ProcessSend(e);
break;
}
}
private static void ProcessReceive(SocketAsyncEventArgs e)
{
Socket s = (Socket)e.UserToken;
if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
{
byte[] receivedData = new byte[e.BytesTransferred];
Array.Copy(e.Buffer, receivedData, e.BytesTransferred);
string receivedText = Encoding.ASCII.GetString(receivedData);
Console.WriteLine("Received : {0}", receivedText);
// Echo the data back to the client.
e.SetBuffer(receivedData, 0, receivedData.Length);
bool willRaiseEvent = s.SendAsync(e);
if (!willRaiseEvent)
{
ProcessSend(e);
}
}
else
{
s.Close();
}
}
private static void ProcessSend(SocketAsyncEventArgs e)
{
Socket s = (Socket)e.UserToken;
bool willRaiseEvent = s.ReceiveAsync(e);
if (!willRaiseEvent)
{
ProcessReceive(e);
}
}
}
选择适合的异步Socket模型
基于回调的异步Socket模型:适用于需要较高控制的场景,但代码可能会变得复杂且难以维护。
基于
async
/await
的异步Socket模型:推荐用于现代C#编程,代码简洁且易于维护。基于事件的异步Socket模型:适用于高性能和高并发的服务器应用,需要复杂的连接和I/O管理。
根据你的具体需求和应用场景,选择适合的异步Socket模型来实现高效的网络编程。