如何设置 TcpListener 始终监听并接受多个连接?
Posted
技术标签:
【中文标题】如何设置 TcpListener 始终监听并接受多个连接?【英文标题】:How to set up TcpListener to always listen and accept multiple connections? 【发布时间】:2013-10-23 14:29:06 【问题描述】:这是我的服务器应用程序:
public static void Main()
try
IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
Console.WriteLine("Starting TCP listener...");
TcpListener listener = new TcpListener(ipAddress, 500);
listener.Start();
while (true)
Console.WriteLine("Server is listening on " + listener.LocalEndpoint);
Console.WriteLine("Waiting for a connection...");
Socket client = listener.AcceptSocket();
Console.WriteLine("Connection accepted.");
Console.WriteLine("Reading data...");
byte[] data = new byte[100];
int size = client.Receive(data);
Console.WriteLine("Recieved data: ");
for (int i = 0; i < size; i++)
Console.Write(Convert.ToChar(data[i]));
Console.WriteLine();
client.Close();
listener.Stop();
catch (Exception e)
Console.WriteLine("Error: " + e.StackTrace);
Console.ReadLine();
如您所见,它总是在工作时收听,但我想指定我希望应用能够同时收听并支持多个连接。
如何修改它以在接受多个连接的同时不断收听?
【问题讨论】:
是否指定127.0.0.1
限制监听套接字只能在本地机器上工作?公共网络上的设备是否能够访问此侦听套接字?
为什么要用new byte[100];
数组?这是从客户端发送的数据大小吗?
@barteloma 这是很久以前的事了,我不记得了,但它可能只是一个神奇的数字。
【参考方案1】:
您要侦听传入连接的套接字通常称为侦听套接字。
当监听套接字确认传入连接时,通常称为子套接字的套接字创建有效地表示远程端点。
为了同时处理多个客户端连接,您需要为服务器接收和处理数据的每个子套接字生成一个新线程。 这样做将允许 侦听套接字 接受和处理多个连接,因为您正在侦听的线程在您等待传入数据时将不再阻塞或等待.
while (true)
Socket client = listener.AcceptSocket();
Console.WriteLine("Connection accepted.");
var childSocketThread = new Thread(() =>
byte[] data = new byte[100];
int size = client.Receive(data);
Console.WriteLine("Recieved data: ");
for (int i = 0; i < size; i++)
Console.Write(Convert.ToChar(data[i]));
Console.WriteLine();
client.Close();
);
childSocketThread.Start();
【讨论】:
如何异步设置(不为每个客户端启动一个新线程?) @MrDysprosium 可扩展性明显。 你说的很明显,所以我对这个话题的理解程度可能很低......但是为什么异步比线程更具可扩展性? @MrDysprosium 因为为操作系统创建和管理线程的成本很高,因此为每个客户端启动一个线程很昂贵。除此之外,您为每个客户端创建的线程大部分都不会做任何事情,而是等待从客户端获取一些东西。 @SHM 不是 2 年后回复的那个人,但是为了让异步进程继续进行,在某些时候必须建立一个线程,因此,必须有人假脱机.即使通过来自另一台机器的线路,也会打开一个线程来等待响应。我不明白您将如何建立异步进程(例如,任务仍然是线程)。【参考方案2】:我今天遇到了类似的问题,这样解决了:
while (listen) // <--- boolean flag to exit loop
if (listener.Pending())
Thread tmp_thread = new Thread(new ThreadStart(() =>
string msg = null;
TcpClient clt = listener.AcceptTcpClient();
using (NetworkStream ns = clt.GetStream())
using (StreamReader sr = new StreamReader(ns))
msg = sr.ReadToEnd();
Console.WriteLine("Received new message (" + msg.Length + " bytes):\n" + msg);
tmp_thread.Start();
else
Thread.Sleep(100); //<--- timeout
我的循环在等待连接时没有卡住,它确实接受了多个连接。
编辑: 以下代码 sn-p 是使用任务而不是线程的 async
等效项。请注意,代码包含 C#-8 结构。
private static TcpListener listener = .....;
private static bool listen = true; // <--- boolean flag to exit loop
private static async Task HandleClient(TcpClient clt)
using NetworkStream ns = clt.GetStream();
using StreamReader sr = new StreamReader(ns);
string msg = await sr.ReadToEndAsync();
Console.WriteLine($"Received new message (msg.Length bytes):\nmsg");
public static async void Main()
while (listen)
if (listener.Pending())
await HandleClient(await listener.AcceptTcpClientAsync());
else
await Task.Delay(100); //<--- timeout
【讨论】:
一般来说,当我想管理来自外部的监听时,我也会使用这个解决方案,并有可能正确地停止它。我面临的唯一问题是,如果设置为高,使用 sleep 命令会产生过多的延迟,如果设置为较低,则会消耗 cpu。有没有办法解决这个问题? 我相信最好的解决方案是结合使用 async/await 和“Task.Delay(...)”。当我可以访问我的计算机时,我会尝试进一步详细说明。 太好了,让我们看看它是如何工作的。我的另一个想法是继续使用 AcceptTcpClient 而不嵌套到睡眠等待循环中,并在另一个单独的线程中启动它,然后在想要完成收听时强行终止它。然后它抛出一个必须处理的异常。丑陋,但在速度方面似乎是最有效的,只是想知道是否有任何类似但更好的解决方案。 我使用“Task.Delay(...)”更新了我的答案以包含异步代码。使用任务的异步编程被认为是基于线程的代码结构的“继承者”。 最好的方法就是在一个while循环中等待listener.AcceptTcpClientAsync。如果您希望能够从外部关闭侦听套接字,则只需使用取消令牌关闭套接字并捕获 AcceptTcpClientAsync() 抛出的套接字异常【参考方案3】:基本思想是,监听器套接字总是监听给定的 IP 和端口号。当有连接请求时,侦听器接受连接并使用 tcpclient 对象获取远程端点,直到连接关闭或丢失。
【讨论】:
以上是关于如何设置 TcpListener 始终监听并接受多个连接?的主要内容,如果未能解决你的问题,请参考以下文章
使用 async/await 的异步 TcpListener 的正确方法