TcpListener를 중지하는 올바른 방법
저는 현재 TcpListener를 사용하여 들어오는 연결을 처리하고 있으며 각 연결에는 통신을 처리하기위한 스레드가 제공되고 해당 단일 연결을 종료합니다. 코드는 다음과 같습니다.
TcpListener listener = new TcpListener(IPAddress.Any, Port);
System.Console.WriteLine("Server Initialized, listening for incoming connections");
listener.Start();
while (listen)
{
// Step 0: Client connection
TcpClient client = listener.AcceptTcpClient();
Thread clientThread = new Thread(new ParameterizedThreadStart(HandleConnection));
clientThread.Start(client.GetStream());
client.Close();
}
listen
변수는 클래스의 필드입니다 부울입니다. 이제 프로그램이 종료되면 클라이언트 수신을 중지하고 싶습니다. 수신 대기를 설정하면 false
더 많은 연결을 사용하지 못하지만 AcceptTcpClient
차단 호출이므로 최소한 다음 클라이언트를 사용하고 THEN을 종료합니다. 강제로 멈출 수있는 방법이 있습니까? 다른 차단 호출이 실행되는 동안 listener.Stop () 호출은 어떤 영향을 미칩니 까?
코드에 대해 제안 할 두 가지 제안이 있으며 귀하의 디자인이라고 생각합니다. 그러나 먼저 네트워크 또는 파일 시스템과 같은 I / O로 작업 할 때 비 차단 I / O 콜백을 사용해야한다는 점을 먼저 지적하고 싶습니다. 그것은 지금까지의 FAR 보다 효율적이고 그들이 프로그램에 단단하지만 응용 프로그램이 더 많이 작동합니다. 마지막에 제안 된 디자인 수정에 대해 간략하게 설명하겠습니다.
- TcpClient에 Using () {} 사용
- Thread.Abort ()
- TcpListener.Pending ()
- 비동기 재 작성
TcpClient에 Using () {} 사용
*** 예외가 발생한 경우에도 TcpClient.Dispose () 또는 TcpClient.Close () 메서드가 호출되도록하려면 TcpClient 호출을 using () {} 블록에 넣어야합니다. 또는 try {} finally {} 블록의 finally 블록에 넣을 수 있습니다.
Thread.Abort ()
당신이 할 수있는 일이 두 가지 있습니다. 1은이 TcpListener 스레드를 다른 스레드에서 시작한 경우 스레드에서 Thread.Abort 인스턴스 메서드를 호출하기 만하면 차단 호출 내에서 threadabortexception이 발생하고 스택 위로 올라갈 수 있다는 것입니다.
TcpListener.Pending ()
두 번째 저비용 수정은 listener.Pending () 메서드를 사용하여 폴링 모델을 구현하는 것입니다. 그런 다음 새로운 연결이 보류 중인지 확인하기 전에 Thread.Sleep을 사용하여 "대기"합니다. 보류중인 연결이 있으면 AcceptTcpClient를 호출하면 보류중인 연결이 해제됩니다. 코드는 다음과 같습니다.
while (listen){
// Step 0: Client connection
if (!listener.Pending())
{
Thread.Sleep(500); // choose a number (in milliseconds) that makes sense
continue; // skip to next iteration of loop
}
TcpClient client = listener.AcceptTcpClient();
Thread clientThread = new Thread(new ParameterizedThreadStart(HandleConnection));
clientThread.Start(client.GetStream());
client.Close();
}
비동기 재 작성
마지막으로 응용 프로그램에 대한 비 차단 방법론으로 이동하는 것이 좋습니다. 내부적으로 프레임 워크는 오버랩 된 I / O 및 I / O 완료 포트를 사용하여 비동기 호출에서 비 차단 I / O를 구현합니다. 그다지 어렵지는 않지만 코드에 대해 약간 다르게 생각하면됩니다.
기본적으로 BeginAcceptTcpClient 메서드로 코드를 시작하고 반환 된 IAsyncResult를 추적합니다. 당신은 누구의하여 TcpClient를 얻고 그것을 떨어져 통과에 대한 책임 방법에 있음을 지적 하지 당신이 회전 및 각 클라이언트 요청에 대한 새 스레드를 종료하지 않을 수 있도록 ThreadPool.QueueUserWorkerItem 떨어져 스레드 새 스레드로 이에 (를 참고 스레드 풀이 공유되고 모든 스레드를 독점하면 시스템에서 구현 한 애플리케이션의 다른 부분이 고갈 될 수 있기 때문에 특히 오래 지속 된 요청이있는 경우 자체 스레드 풀을 사용해야 할 수 있습니다. 리스너 메서드가 새 TcpClient를 자체 ThreadPool 요청으로 시작하면 BeginAcceptTcpClient를 다시 호출하고 델리게이트가 자신을 다시 가리 킵니다.
효과적으로 현재 메서드를 3 개의 다른 메서드로 나누면 다양한 부분에서 호출됩니다. 1. 모든 것을 부트 스트랩하려면 2. EndAcceptTcpClient를 호출 할 대상이 되려면 TcpClient를 자체 스레드로 시작한 다음 다시 자신을 호출합니다. 3. 클라이언트 요청을 처리하고 완료되면 닫습니다.
listener.Server.Close()
다른 스레드에서 차단 호출을 중단합니다.
A blocking operation was interrupted by a call to WSACancelBlockingCall
소켓은 강력한 비동기 기능을 제공합니다. 비동기 서버 소켓 사용 살펴보기
다음은 코드에 대한 몇 가지 참고 사항입니다.
이 경우 수동으로 생성 된 스레드를 사용하면 오버 헤드가 발생할 수 있습니다.
아래 코드는 경쟁 조건에 따라 달라집니다. TcpClient.Close ()는 TcpClient.GetStream ()을 통해 얻은 네트워크 스트림을 닫습니다. 더 이상 필요하지 않다고 확실히 말할 수있는 클라이언트를 닫는 것을 고려하십시오.
clientThread.Start(client.GetStream());
client.Close();
TcpClient.Stop() closes underlying socket. TcpCliet.AcceptTcpClient() uses Socket.Accept() method on underlying socket which will throw SocketException once it is closed. You can call it from a different thread.
Anyway I recommend asynchronous sockets.
Don't use a loop. Instead, call BeginAcceptTcpClient() without a loop. In the callback, just issue another call to BeginAcceptTcpClient(), if your listen flag is still set.
To stop the listener, since you've not blocked, your code can just call Close() on it.
See my answer here https://stackoverflow.com/a/17816763/2548170 TcpListener.Pending()
is not good solution
Just to add even more reason to use the asynchronous approach, I'm pretty sure Thread.Abort won't work because the call is blocked in the OS level TCP stack.
Also... if you are calling BeginAcceptTCPClient in the callback to listen for every connection but the first, be careful to make sure that the thread that executed the initial BeginAccept doesn't terminate or else the listener will automatically get disposed by the framework. I suppose that's a feature, but in practice it's very annoying. In desktop apps it's not usually a problem, but on the web you might want to use the thread pool since those threads don't ever really terminate.
Already mentioned above, use BeginAcceptTcpClient instead, it's much easier to manage asynchronously.
Here is some sample code :
ServerSocket = new TcpListener(endpoint);
try
{
ServerSocket.Start();
ServerSocket.BeginAcceptTcpClient(OnClientConnect, null);
ServerStarted = true;
Console.WriteLine("Server has successfully started.");
}
catch (Exception ex)
{
Console.WriteLine($"Server was unable to start : {ex.Message}");
return false;
}
Probably best to use the asynchronous BeginAcceptTcpClient function. Then you can just call Stop() on the listener as it won't be blocking.
Some changes to make the Peter Oehlert anwer perfect. Because before 500 miliseconds the listener bloking again. To correct this:
while (listen)
{
// Step 0: Client connection
if (!listener.Pending())
{
Thread.Sleep(500); // choose a number (in milliseconds) that makes sense
continue; // skip to next iteration of loop
}
else // Enter here only if have pending clients
{
TcpClient client = listener.AcceptTcpClient();
Thread clientThread = new Thread(new ParameterizedThreadStart(HandleConnection));
clientThread.Start(client.GetStream());
client.Close();
}
}
참고URL : https://stackoverflow.com/questions/365370/proper-way-to-stop-tcplistener
'Program Tip' 카테고리의 다른 글
MySQL 오류 # 1064를 어떻게 수정할 수 있습니까? (0) | 2020.12.04 |
---|---|
Jenkins의 다중 분기 파이프 라인으로 "주기적으로 구축" (0) | 2020.12.04 |
jquery로 html 문자열 구문 분석 (0) | 2020.12.04 |
ADO.NET | DataDirectory | (0) | 2020.12.04 |
Coq와 같은 비 튜링 완전한 언어의 실질적인 한계는 무엇입니까? (0) | 2020.12.04 |