我试图理解TcpListener类的待定参数,但我很难同时实现最大数量的挂起连接,这样我就可以测试它了。
我有一个示例异步服务器和客户端代码。MSDN说待办事项是挂起的连接队列的最大长度。我让服务器一直监听连接,客户端连接30次。我所期望的是在第20个请求之后,在客户机中抛出一个SocketException,因为待办事项被设置为20,为什么它不阻止它呢?
我的第二个误解是,我真的需要将我接受的连接的逻辑放在一个新线程中吗?假设有一个缓慢的操作,大约需要10秒,例如,通过TCP发送一个文件?目前,我把我的逻辑放在一个new Thread中,我知道这不是最好的解决方案,我应该使用ThreadPool,但问题是主体。我通过将客户端的循环更改为1000个迭代来测试它,如果我的逻辑不在一个新线程中,那么在第200次连接之后,连接可能会被阻塞,这可能是因为Thread.Sleep每次使主线程慢10秒,并且主线程负责所有接受回调。因此,基本上,我自己解释如下:如果我想使用相同的概念,我必须把我的AcceptCallback逻辑放在一个新的线程中,就像我所做的那样,或者我必须做一些类似于这里被接受的答案:TcpListener排队连接比我清除它们的速度快。我说的对吗?
服务器代码:
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace Server
{
class Program
{
private static readonly ManualResetEvent _mre = new ManualResetEvent(false);
static void Main(string[] args)
{
TcpListener listener = new TcpListener(IPAddress.Any, 80);
try
{
listener.Start(20);
while (true)
{
_mre.Reset();
Console.WriteLine("Waiting for a connection...");
listener.BeginAcceptTcpClient(new AsyncCallback(AcceptCallback), listener);
_mre.WaitOne();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
private static void AcceptCallback(IAsyncResult ar)
{
_mre.Set();
TcpListener listener = (TcpListener)ar.AsyncState;
TcpClient client = listener.EndAcceptTcpClient(ar);
IPAddress ip = ((IPEndPoint)client.Client.RemoteEndPoint).Address;
Console.WriteLine($"{ip} has connected!");
// Actually I changed it to ThreadPool
//new Thread(() =>
//{
// Console.WriteLine("Sleeping 10 seconds...");
// Thread.Sleep(10000);
// Console.WriteLine("Done");
//}).Start();
ThreadPool.QueueUserWorkItem(new WaitCallback((obj) =>
{
Console.WriteLine("Sleeping 10 seconds...");
Thread.Sleep(10000);
Console.WriteLine("Done");
}));
// Close connection
client.Close();
}
}
}客户代码:
using System;
using System.Net.Sockets;
namespace Client
{
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 30; i++)
{
Console.WriteLine($"Connecting {i}");
using (TcpClient client = new TcpClient()) // because once we are done, we have to close the connection with close.Close() and in this way it will be executed automatically by the using statement
{
try
{
client.Connect("localhost", 80);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
Console.ReadKey();
}
}
}NetworkStream编辑:因为我的第二个问题可能有点混乱,所以我会发布我的代码,其中包括发送的消息,问题是我应该这样离开它,还是把放到一个新的线程中呢?。
服务器:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace Server
{
class Program
{
private static readonly ManualResetEvent _mre = new ManualResetEvent(false);
static void Main(string[] args)
{
// MSDN example: https://learn.microsoft.com/en-us/dotnet/framework/network-programming/asynchronous-server-socket-example
// A better solution is posted here: https://stackoverflow.com/questions/2745401/tcplistener-is-queuing-connections-faster-than-i-can-clear-them
TcpListener listener = new TcpListener(IPAddress.Any, 80);
try
{
// Backlog limit is 200 for Windows 10 consumer edition
listener.Start(5);
while (true)
{
// Set event to nonsignaled state
_mre.Reset();
Console.WriteLine("Waiting for a connection...");
listener.BeginAcceptTcpClient(new AsyncCallback(AcceptCallback), listener);
// Wait before a connection is made before continuing
_mre.WaitOne();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
private static void AcceptCallback(IAsyncResult ar)
{
// Signal the main thread to continue
_mre.Set();
TcpListener listener = (TcpListener)ar.AsyncState;
TcpClient client = listener.EndAcceptTcpClient(ar);
IPAddress ip = ((IPEndPoint)client.Client.RemoteEndPoint).Address;
Console.WriteLine($"{ip} has connected!");
using (NetworkStream ns = client.GetStream())
{
byte[] bytes = Encoding.Unicode.GetBytes("test");
ns.Write(bytes, 0, bytes.Length);
}
// Use this only with backlog 20 in order to test
Thread.Sleep(5000);
// Close connection
client.Close();
Console.WriteLine("Connection closed.");
}
}
}客户端:
using System;
using System.Net.Sockets;
using System.Text;
namespace Client
{
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 33; i++)
{
Console.WriteLine($"Connecting {i}");
using (TcpClient client = new TcpClient()) // once we are done, the using statement will do client.Close()
{
try
{
client.Connect("localhost", 80);
using (NetworkStream ns = client.GetStream())
{
byte[] bytes = new byte[100];
int readBytes = ns.Read(bytes, 0, bytes.Length);
string result = Encoding.Unicode.GetString(bytes, 0, readBytes);
Console.WriteLine(result);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
Console.ReadKey();
}
}
}发布于 2019-05-17 10:41:03
侦听待办事项在RFC 6458中定义并告诉操作系统accept queue中允许的最大套接字数。
传入连接由TCP/IP堆栈放置在此队列中,并在服务器调用Accept以处理新连接时删除。
在您的问题中,两个版本的服务器代码都会在主线程循环中调用Accept,并在进行另一个接受调用之前等待AcceptCallback启动。这导致队列很快耗尽。
要演示侦听队列溢出,最简单的方法是降低服务器的接受率--例如,将其降低到零:
var serverEp = new IPEndPoint(IPAddress.Loopback, 34567);
var serverSocket = new TcpListener(serverEp);
serverSocket.Start(3);
for (int i = 1; i <= 10; i++)
{
var clientSocket = new TcpClient();
clientSocket.Connect(serverEp);
Console.WriteLine($"Connected socket {i}");
} 在您的示例中,您只需在主线程中的Accept循环的末尾添加一个睡眠,并提高连接速率。
在现实世界中,最佳待办事项取决于:
我不建议直接使用Thread,以下是使用Task和套接字任务扩展的服务器外观
static async Task Main(string[] args)
{
var server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
server.Bind(new IPEndPoint(IPAddress.Any, 80));
server.Listen(5);
while (true)
{
var client = await server.AcceptAsync();
var backTask = ProcessClient(client);
}
}
private static async Task ProcessClient(Socket socket)
{
using (socket)
{
var ip = ((IPEndPoint)(socket.RemoteEndPoint)).Address;
Console.WriteLine($"{ip} has connected!");
var buffer = Encoding.Unicode.GetBytes("test");
await socket.SendAsync(buffer, SocketFlags.None);
}
Console.WriteLine("Connection closed.");
}https://stackoverflow.com/questions/56156342
复制相似问题