gpt4 book ai didi

c# - async/await Tcp socket 实现只接受一个连接

转载 作者:可可西里 更新时间:2023-11-01 02:56:41 27 4
gpt4 key购买 nike

我习惯了开始/结束 APM 模式,我想将我的套接字服务器更新为 .Net 4.5/async/await。我从 Internet 来源编写了示例代码,但无法正常工作。

我希望所有已连接的客户端在被接受连接(尚未实现..)后被分离到自己的类中。接受所有传入连接的循环在自己的线程中运行。

基本上,Main.cs 是我接受客户端、创建新类 (Client.cs/Session.cs) 进行连接并将接受客户端指向该类的地方。好吧,这就是我打算做的,它不在代码中,目前的主要问题是我对如何处理这个接受序列的了解以及为什么我不能同时连接多个客户端?我希望你能指出我正确的答案。

提前谢谢你。

Picture of server logic

代码

Form1.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();
}
}
}

Main.cs

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 绑定(bind)进程进行不必要的等待。它提供任何类型的并发。它只是释放了 CPU,所以它不会坐等。

因此,您需要利用 TPL(任务并行库)的其余部分来提供允许并发客户端所需的并发性。这可能意味着一旦连接发生,就为客户端分离出一个任务,并使用该任务来管理客户端。

异步可以通过确保每个客户端不会阻塞一个完整的线程来补充这一点,但异步本身只能帮助您不阻塞,它不提供并发性。

首先,我强烈建议您阅读 MSDN 上有关 TPL 的所有信息。它很多,但它是很好的阅读 Material ,并且会有很大帮助。 https://msdn.microsoft.com/en-us/library/dd460717(v=vs.110).aspx

至于更具体的例子,我可以试试。在您接受客户端的循环中,您将希望在连接客户端后立即获得并行性。这将使您的运行循环返回到接受客户端,并让客户端仍然交互。例如:

    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);
}
}

注意这是伪代码。我没有尝试编译它。但这个概念很扎实。

如果您有大量客户端,您将需要比仅使用 Task.Run 更具体,但对于示例来说它工作正常。它将利用线程池线程来实现并行性。这至少在 100 时工作正常,但之后性能可能会下降。

关于c# - async/await Tcp socket 实现只接受一个连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39150510/

27 4 0
文章推荐: javascript - 将 shell 通配符转换为正则表达式
文章推荐: java - 根据每个对象内的 List 过滤 List