Я пытаюсь понять параметр backlog класса 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://docs.microsoft.com/en-us/dotnet/framework/network-programming/asynchronous-server-socket-example
// A better solution is posted here: /1815428/tcplistener-stavit-v-ochered-soedineniya-bystree-chem-ya-mogu-ih-ochistit
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();
}
}
}