C# 异步/等待Tcp套接字实现只接受一个连接
我习惯于开始/结束APM模式,我想将套接字服务器更新为.NET4.5/async/await。我从互联网资源中编写了一个示例代码,但它不起作用 我希望在所有已连接的客户端被接受连接(尚未实现)后,将它们分离到自己的类中。。接受所有传入连接的循环在自己的线程上运行 基本上Main.cs是我接受客户机、创建新类(client.cs/Session.cs)进行连接并将接受的客户机指向该类的地方。好吧,这就是我计划做的,它不在代码中,目前的主要问题是我知道如何处理这个接受序列,以及为什么我不能同时连接多个客户?我希望你能给我指出正确的答案 先谢谢你 代码 Form1.csC# 异步/等待Tcp套接字实现只接受一个连接,c#,sockets,asynchronous,tcp,async-await,C#,Sockets,Asynchronous,Tcp,Async Await,我习惯于开始/结束APM模式,我想将套接字服务器更新为.NET4.5/async/await。我从互联网资源中编写了一个示例代码,但它不起作用 我希望在所有已连接的客户端被接受连接(尚未实现)后,将它们分离到自己的类中。。接受所有传入连接的循环在自己的线程上运行 基本上Main.cs是我接受客户机、创建新类(client.cs/Session.cs)进行连接并将接受的客户机指向该类的地方。好吧,这就是我计划做的,它不在代码中,目前的主要问题是我知道如何处理这个接受序列,以及为什么我不能同时连接多
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Server
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
// Own thread for accepting connections
Main ConnectionLoop = new Main(10);
Thread thread = new Thread(new ThreadStart(ConnectionLoop.PrepareThread));
thread.IsBackground = true;
thread.Start();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Server
{
public class Main
{
private int m_backLog = 0;
// constructor
public Main(int Backlog)
{
m_backLog = Backlog;
Console.WriteLine("Main class created, backlog: {0}", Backlog);
}
public void PrepareThread()
{
Console.WriteLine("Thread created");
StartAccepting(CancellationToken.None).Wait();
}
private async Task StartAccepting(CancellationToken token)
{
Console.WriteLine("Started listening..");
CancellationTokenSource cts = new CancellationTokenSource();
TcpListener listener = new TcpListener(IPAddress.Any, 6112);
listener.Start();
await AcceptClientsAsync(listener, cts.Token);
// Thread.Sleep(600000);
}
private async Task AcceptClientsAsync(TcpListener listener, CancellationToken token)
{
var clientCounter = 0;
while (!token.IsCancellationRequested)
{
TcpClient client = await listener.AcceptTcpClientAsync().ConfigureAwait(false);
clientCounter++;
Console.WriteLine("Client {0} accepted!", clientCounter);
await EchoAsync(client, clientCounter, token);
}
}
private async Task EchoAsync(TcpClient client, int clientCounter, CancellationToken token)
{
using (client)
{
var buf = new byte[4096]; // buffer for stream
var stream = client.GetStream(); // stream itself
while (!token.IsCancellationRequested)
{
// some conditions we don't know is client connected, lets have timeout
var timeoutTask = Task.Delay(TimeSpan.FromSeconds(15));
var amountReadTask = stream.ReadAsync(buf, 0, buf.Length, token);
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;
}
var amountRead = amountReadTask.Result;
if (amountRead == 0) break; // end of stream
await stream.WriteAsync(buf, 0, amountRead, token).ConfigureAwait(false);
}
}
Console.WriteLine("Client {0} disconnected", clientCounter);
}
}
}
Main.cs
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Server
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
// Own thread for accepting connections
Main ConnectionLoop = new Main(10);
Thread thread = new Thread(new ThreadStart(ConnectionLoop.PrepareThread));
thread.IsBackground = true;
thread.Start();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Server
{
public class Main
{
private int m_backLog = 0;
// constructor
public Main(int Backlog)
{
m_backLog = Backlog;
Console.WriteLine("Main class created, backlog: {0}", Backlog);
}
public void PrepareThread()
{
Console.WriteLine("Thread created");
StartAccepting(CancellationToken.None).Wait();
}
private async Task StartAccepting(CancellationToken token)
{
Console.WriteLine("Started listening..");
CancellationTokenSource cts = new CancellationTokenSource();
TcpListener listener = new TcpListener(IPAddress.Any, 6112);
listener.Start();
await AcceptClientsAsync(listener, cts.Token);
// Thread.Sleep(600000);
}
private async Task AcceptClientsAsync(TcpListener listener, CancellationToken token)
{
var clientCounter = 0;
while (!token.IsCancellationRequested)
{
TcpClient client = await listener.AcceptTcpClientAsync().ConfigureAwait(false);
clientCounter++;
Console.WriteLine("Client {0} accepted!", clientCounter);
await EchoAsync(client, clientCounter, token);
}
}
private async Task EchoAsync(TcpClient client, int clientCounter, CancellationToken token)
{
using (client)
{
var buf = new byte[4096]; // buffer for stream
var stream = client.GetStream(); // stream itself
while (!token.IsCancellationRequested)
{
// some conditions we don't know is client connected, lets have timeout
var timeoutTask = Task.Delay(TimeSpan.FromSeconds(15));
var amountReadTask = stream.ReadAsync(buf, 0, buf.Length, token);
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;
}
var amountRead = amountReadTask.Result;
if (amountRead == 0) break; // end of stream
await stream.WriteAsync(buf, 0, amountRead, token).ConfigureAwait(false);
}
}
Console.WriteLine("Client {0} disconnected", clientCounter);
}
}
}
Async用于长时间运行的进程,并防止在长时间运行的I/O绑定进程上进行不必要的等待。它不提供任何类型的并发性。它只是释放了CPU,所以它不会坐在那里等待 因此,您需要利用TPL(Task Parallel Library,任务并行库)的其余部分来提供允许并发客户端所需的并发性。这可能意味着一旦连接发生,就为客户机剥离一个任务,并使用该任务来管理客户机 Async可以通过确保每个客户机不阻塞整个线程来补充这一点,但Async本身只帮助您不阻塞,它不提供并发性 首先,我强烈建议您阅读MSDN上关于第三方物流的所有信息。虽然有很多,但这是一本很好的书,会有很大的帮助。 至于一个更具体的例子,我可以试试。 在接受客户机的循环中,一旦有了连接的客户机,就需要获得并行性。这将使您的运行循环返回到接受客户机,并让客户机仍然进行交互。例如:
private async Task AcceptClientsAsync(TcpListener listener, CancellationToken token)
{
var clientCounter = 0;
while (!token.IsCancellationRequested)
{
TcpClient client = await listener.AcceptTcpClientAsync().ConfigureAwait(false);
clientCounter++;
Console.WriteLine("Client {0} accepted!", clientCounter);
Task.Run(async () => await EchoAsync(client, clientCounter, token), token);
}
}
注意:这是psuedo代码。我没有尝试编译它。但这个概念是牢固的
如果您有大量的客户机,您需要更加具体,而不仅仅是使用Task.Run,但对于一个示例来说,它工作得很好。它将利用线程池线程来实现并行性。它至少可以在100分钟内正常工作,但之后性能会下降。异步适用于长时间运行的进程,并防止在长时间运行的I/O绑定进程上进行不必要的等待。它不提供任何类型的并发性。它只是释放了CPU,所以它不会坐在那里等待 因此,您需要利用TPL(Task Parallel Library,任务并行库)的其余部分来提供允许并发客户端所需的并发性。这可能意味着一旦连接发生,就为客户机剥离一个任务,并使用该任务来管理客户机 Async可以通过确保每个客户机不阻塞整个线程来补充这一点,但Async本身只帮助您不阻塞,它不提供并发性 首先,我强烈建议您阅读MSDN上关于第三方物流的所有信息。虽然有很多,但这是一本很好的书,会有很大的帮助。 至于一个更具体的例子,我可以试试。 在接受客户机的循环中,一旦有了连接的客户机,就需要获得并行性。这将使您的运行循环返回到接受客户机,并让客户机仍然进行交互。例如:
private async Task AcceptClientsAsync(TcpListener listener, CancellationToken token)
{
var clientCounter = 0;
while (!token.IsCancellationRequested)
{
TcpClient client = await listener.AcceptTcpClientAsync().ConfigureAwait(false);
clientCounter++;
Console.WriteLine("Client {0} accepted!", clientCounter);
Task.Run(async () => await EchoAsync(client, clientCounter, token), token);
}
}
注意:这是psuedo代码。我没有尝试编译它。但这个概念是牢固的
如果您有大量的客户机,您需要更加具体,而不仅仅是使用Task.Run,但对于一个示例来说,它工作得很好。它将利用线程池线程来实现并行性。该函数至少可以运行100次,但之后性能会下降。异步函数返回一个任务,该任务在该函数完成时完成 在您的
AcceptClientsAsync
函数中,您正在等待echoancy
函数。这意味着在EchoAsync
完成之前(即,在发出取消令牌信号之后),不会再次调用AcceptCpclientAsync
对于异步套接字服务器,您应该有一个只在循环中接受的任务,对于每个连接,您应该有一个“处理”任务。这些必须是独立的-您不能等待接受任务中的处理任务
更新:根据请求添加示例:
private async Task AcceptClientsAsync(TcpListener listener, CancellationToken token)
{
var clientCounter = 0;
while (!token.IsCancellationRequested)
{
TcpClient client = await listener.AcceptTcpClientAsync().ConfigureAwait(false);
clientCounter++;
Console.WriteLine("Client {0} accepted!", clientCounter);
var echoTask = EchoAsync(client, clientCounter, token);
// TODO: save the echoTask in some kind of per-client data structure.
}
}
async
函数返回该函数完成时已完成的任务
在您的AcceptClientsAsync
函数中,您正在等待echoancy
函数。这意味着在EchoAsync
完成之前(即,在发出取消令牌信号之后),不会再次调用AcceptCpclientAsync
对于异步套接字服务器,您应该有一个只在循环中接受的任务,对于每个连接,您应该有一个“处理”任务。这些必须是独立的-您不能等待接受任务中的处理任务
更新:根据请求添加示例:
private async Task AcceptClientsAsync(TcpListener listener, CancellationToken token)
{
var clientCounter = 0;
while (!token.IsCancellationRequested)
{
TcpClient client = await listener.AcceptTcpClientAsync().ConfigureAwait(false);
clientCounter++;
Console.WriteLine("Client {0} accepted!", clientCounter);
var echoTask = EchoAsync(client, clientCounter, token);
// TODO: save the echoTask in some kind of per-client data structure.
}
}
谢谢你的回复,你能给我举个例子吗?这个任务&async/await让我很困惑谢谢你的回复,你能给我举个例子吗?这个任务&async/await让我很困惑,谢谢你的回复!你们愿意给我一个粗略的例子,甚至是并发异步套接字的伪代码吗?谢谢你们的回复!您愿意给我一个粗略的例子,甚至是并发异步套接字的伪代码吗?