我之前使用的是BeginAccept()
和BeginRead()
,但是在Visual Studio 2012中,我希望在我的中使用新的异步(async
,await
)功能套接字服务器程序。
如何完成AcceptAsync
和ReceiveAsync
功能?
using System.Net;
using System.Net.Sockets;
namespace OfficialServer.Core.Server
{
public abstract class CoreServer
{
private const int ListenLength = 500;
private const int ReceiveTimeOut = 30000;
private const int SendTimeOut = 30000;
private readonly Socket _socket;
protected CoreServer(int port, string ip = "0.0.0.0")
{
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_socket.Bind(new IPEndPoint(IPAddress.Parse(ip), port));
_socket.Listen(ListenLength);
_socket.ReceiveTimeout = ReceiveTimeOut;
_socket.SendTimeout = SendTimeOut;
_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true);
}
public void Start()
{
}
}
}
答案 0 :(得分:55)
...因为你如此坚定,我把一个非常简单的例子说明如何写一个echo服务器来帮你上路。收到的任何内容都会回复给客户端。服务器将保持运行60秒。尝试在localhost端口6666上telnet到它。花些时间来了解这里发生了什么。
void Main()
{
CancellationTokenSource cts = new CancellationTokenSource();
TcpListener listener = new TcpListener(IPAddress.Any, 6666);
try
{
listener.Start();
//just fire and forget. We break from the "forgotten" async loops
//in AcceptClientsAsync using a CancellationToken from `cts`
AcceptClientsAsync(listener, cts.Token);
Thread.Sleep(60000); //block here to hold open the server
}
finally
{
cts.Cancel();
listener.Stop();
}
}
async Task AcceptClientsAsync(TcpListener listener, CancellationToken ct)
{
var clientCounter = 0;
while (!ct.IsCancellationRequested)
{
TcpClient client = await listener.AcceptTcpClientAsync()
.ConfigureAwait(false);
clientCounter++;
//once again, just fire and forget, and use the CancellationToken
//to signal to the "forgotten" async invocation.
EchoAsync(client, clientCounter, ct);
}
}
async Task EchoAsync(TcpClient client,
int clientIndex,
CancellationToken ct)
{
Console.WriteLine("New client ({0}) connected", clientIndex);
using (client)
{
var buf = new byte[4096];
var stream = client.GetStream();
while (!ct.IsCancellationRequested)
{
//under some circumstances, it's not possible to detect
//a client disconnecting if there's no data being sent
//so it's a good idea to give them a timeout to ensure that
//we clean them up.
var timeoutTask = Task.Delay(TimeSpan.FromSeconds(15));
var amountReadTask = stream.ReadAsync(buf, 0, buf.Length, ct);
var completedTask = await Task.WhenAny(timeoutTask, amountReadTask)
.ConfigureAwait(false);
if (completedTask == timeoutTask)
{
var msg = Encoding.ASCII.GetBytes("Client timed out");
await stream.WriteAsync(msg, 0, msg.Length);
break;
}
//now we know that the amountTask is complete so
//we can ask for its Result without blocking
var amountRead = amountReadTask.Result;
if (amountRead == 0) break; //end of stream.
await stream.WriteAsync(buf, 0, amountRead, ct)
.ConfigureAwait(false);
}
}
Console.WriteLine("Client ({0}) disconnected", clientIndex);
}
答案 1 :(得分:13)
您可以使用TaskFactory.FromAsync
将Begin
/ End
对包装到async
- 准备好的操作中。
Stephen Toub在他的博客上有一个awaitable Socket
,它包含了更高效的*Async
端点。我建议将其与TPL Dataflow结合使用,以创建完全async
- 兼容的Socket
组件。