서버 소켓에서 클라이언트 연결 끊김을 즉시 감지
클라이언트와 내 서버의 연결이 끊어 졌음을 어떻게 감지 할 수 있습니까?
내 AcceptCallBack
방법에 다음 코드가 있습니다.
static Socket handler = null;
public static void AcceptCallback(IAsyncResult ar)
{
//Accept incoming connection
Socket listener = (Socket)ar.AsyncState;
handler = listener.EndAccept(ar);
}
클라이언트가 handler
소켓 에서 연결이 끊어진 것을 가능한 한 빨리 발견 할 방법을 찾아야합니다 .
난 노력 했어:
handler.Available;
handler.Send(new byte[1], 0, SocketFlags.None);
handler.Receive(new byte[1], 0, SocketFlags.None);
위의 접근 방식은 서버에 연결 중이고 서버 연결이 끊어지는시기를 감지하려고 할 때 작동하지만 서버 인 경우에는 작동하지 않고 클라이언트 연결 끊김을 감지하려고 할 때 작동 합니다.
도움을 주시면 감사하겠습니다.
소켓 연결이 끊어졌을 때 신호를 보낼 수있는 이벤트가 없으므로 허용 가능한 주파수로 폴링해야합니다.
이 확장 방법을 사용하면 소켓 연결이 끊어 졌는지 감지하는 신뢰할 수있는 방법을 가질 수 있습니다.
static class SocketExtensions
{
public static bool IsConnected(this Socket socket)
{
try
{
return !(socket.Poll(1, SelectMode.SelectRead) && socket.Available == 0);
}
catch (SocketException) { return false; }
}
}
누군가 TCP 소켓의 keepAlive 기능을 언급했습니다. 여기에 잘 설명되어 있습니다.
http://tldp.org/HOWTO/TCP-Keepalive-HOWTO/overview.html
이 방법으로 사용하고 있습니다 : 소켓이 연결된 후이 함수를 호출하여 keepAlive를 설정합니다. 이 keepAliveTime
매개 변수는 첫 번째 연결 유지 패킷이 전송 될 때까지 활동이없는 제한 시간 (밀리 초)을 지정합니다. 이 keepAliveInterval
매개 변수는 승인이 수신되지 않은 경우 연속 연결 유지 패킷이 전송되는 간격 (밀리 초)을 지정합니다.
void SetKeepAlive(bool on, uint keepAliveTime , uint keepAliveInterval )
{
int size = Marshal.SizeOf(new uint());
var inOptionValues = new byte[size * 3];
BitConverter.GetBytes((uint)(on ? 1 : 0)).CopyTo(inOptionValues, 0);
BitConverter.GetBytes((uint)time).CopyTo(inOptionValues, size);
BitConverter.GetBytes((uint)interval).CopyTo(inOptionValues, size * 2);
socket.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);
}
또한 동기 읽기를 사용하고 있습니다.
socket.BeginReceive(packet.dataBuffer, 0, 128,
SocketFlags.None, new AsyncCallback(OnDataReceived), packet);
그리고 콜백에서 여기 timeout을 잡았 SocketException
는데, 이는 연결 유지 패킷 이후 소켓이 ACK 신호를받지 못할 때 발생합니다.
public void OnDataReceived(IAsyncResult asyn)
{
try
{
SocketPacket theSockId = (SocketPacket)asyn.AsyncState;
int iRx = socket.EndReceive(asyn);
catch (SocketException ex)
{
SocketExceptionCaught(ex);
}
}
이렇게하면 TCP 클라이언트와 서버 간의 연결 끊김을 안전하게 감지 할 수 있습니다.
이것은 단순히 불가능합니다. 사용자와 서버간에 물리적 연결이 없습니다 (루프백 케이블로 두 컴퓨터간에 연결하는 매우 드문 경우 제외).
연결이 정상적으로 종료되면 상대방에게 알림이 전송됩니다. 그러나 다른 방법으로 연결이 끊어지면 (예 : 사용자 연결이 끊어진 경우) 서버는 시간이 초과 될 때까지 알 수 없습니다 (또는 연결에 쓰기를 시도하고 ack 시간이 초과 됨). 이것이 바로 TCP가 작동하는 방식이며 TCP와 함께 살아야합니다.
따라서 "즉시"는 비현실적입니다. 최선의 방법은 코드가 실행되는 플랫폼에 따라 제한 시간 내에하는 것입니다.
편집 : 정상적인 연결 만 찾고 있다면 클라이언트에서 "DISCONNECT"명령을 서버로 보내지 않는 이유는 무엇입니까?
"그것이 TCP가 작동하는 방식이며 TCP와 함께 살아야합니다."
네, 맞아요. 내가 깨닫게 된 삶의 사실입니다. 이 프로토콜 (및 기타)을 사용하는 전문 응용 프로그램에서도 동일한 동작이 나타납니다. 나는 심지어 온라인 게임에서 발생하는 것을 보았다. 친구가 "안녕"이라고 말하고 서버가 "집을 청소"할 때까지 1 ~ 2 분 더 온라인 상태 인 것처럼 보입니다.
여기에서 제안 된 방법을 사용하거나 제안 된대로 "하트 비트"를 구현할 수 있습니다. 나는 전자를 선택합니다. 그러나 후자를 선택했다면 서버가 각 클라이언트를 매번 단일 바이트로 "핑"하고 타임 아웃이 있는지 또는 응답이 없는지 확인합니다. 정확한 타이밍으로이를 달성하기 위해 백그라운드 스레드를 사용할 수도 있습니다. 정말 걱정된다면 옵션 목록 (열거 형 플래그 등)에서 조합을 구현할 수도 있습니다. 그러나 업데이트하는 한 서버 업데이트에 약간의 지연이있는 것은 그리 큰 문제가 아닙니다. 그것은 인터넷이고 아무도 그것이 마법이 될 것이라고 기대하지 않습니다! :)
시스템에 하트 비트를 구현하는 것이 해결책이 될 수 있습니다. 이것은 클라이언트와 서버가 모두 귀하의 통제하에있는 경우에만 가능합니다. 소켓에서 마지막 바이트가 수신 된 시간을 추적하는 DateTime 객체를 가질 수 있습니다. 그리고 특정 간격 동안 응답하지 않은 소켓이 손실되었다고 가정합니다. 이는 하트 비트 / 사용자 정의 연결 유지가 구현 된 경우에만 작동합니다.
나는 매우 유용하고 그것에 대한 또 다른 해결 방법을 찾았습니다!
연결이 종료 될 때마다 네트워크 소켓에서 데이터를 읽기 위해 비동기 메서드를 사용하는 경우 (즉, BeginReceive
- EndReceive
메서드 사용 ) 다음 상황 중 하나가 나타납니다. 메시지가 데이터없이 전송 되거나 (트리거 된 Socket.Available
경우에도 BeginReceive
값이 0이 됨) Socket.Connected
이 호출에서 값이 거짓이됩니다 ( EndReceive
그때 사용하지 마십시오 ).
내가 사용한 기능을 게시하고 있는데 내가 의미하는 바를 더 잘 알 수 있다고 생각합니다.
private void OnRecieve(IAsyncResult parameter)
{
Socket sock = (Socket)parameter.AsyncState;
if(!sock.Connected || sock.Available == 0)
{
// Connection is terminated, either by force or willingly
return;
}
sock.EndReceive(parameter);
sock.BeginReceive(..., ... , ... , ..., new AsyncCallback(OnRecieve), sock);
// To handle further commands sent by client.
// "..." zones might change in your code.
}
이것은 나를 위해 일했습니다. 핵심은 폴링으로 소켓 상태를 분석하기 위해 별도의 스레드가 필요하다는 것입니다. 소켓과 동일한 스레드에서 수행하면 감지에 실패합니다.
//open or receive a server socket - TODO your code here
socket = new Socket(....);
//enable the keep alive so we can detect closure
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
//create a thread that checks every 5 seconds if the socket is still connected. TODO add your thread starting code
void MonitorSocketsForClosureWorker() {
DateTime nextCheckTime = DateTime.Now.AddSeconds(5);
while (!exitSystem) {
if (nextCheckTime < DateTime.Now) {
try {
if (socket!=null) {
if(socket.Poll(5000, SelectMode.SelectRead) && socket.Available == 0) {
//socket not connected, close it if it's still running
socket.Close();
socket = null;
} else {
//socket still connected
}
}
} catch {
socket.Close();
} finally {
nextCheckTime = DateTime.Now.AddSeconds(5);
}
}
Thread.Sleep(1000);
}
}
The example code here http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.connected.aspx shows how to determine whether the Socket is still connected without sending any data.
If you called Socket.BeginReceive() on the server program and then the client closed the connection "gracefully", your receive callback will be called and EndReceive() will return 0 bytes. These 0 bytes mean that the client "may" have disconnected. You can then use the technique shown in the MSDN example code to determine for sure whether the connection was closed.
Can't you just use Select?
Use select on a connected socket. If the select returns with your socket as Ready but the subsequent Receive returns 0 bytes that means the client disconnected the connection. AFAIK, that is the fastest way to determine if the client disconnected.
I do not know C# so just ignore if my solution does not fit in C# (C# does provide select though) or if I had misunderstood the context.
Using the method SetSocketOption, you will be able to set KeepAlive that will let you know whenever a Socket gets disconnected
Socket _connectedSocket = this._sSocketEscucha.EndAccept(asyn);
_connectedSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, 1);
http://msdn.microsoft.com/en-us/library/1011kecd(v=VS.90).aspx
Hope it helps! Ramiro Rinaldi
i had same problem , try this :
void client_handler(Socket client) // set 'KeepAlive' true
{
while (true)
{
try
{
if (client.Connected)
{
}
else
{ // client disconnected
break;
}
}
catch (Exception)
{
client.Poll(4000, SelectMode.SelectRead);// try to get state
}
}
}
Expanding on comments by mbargiel and mycelo on the accepted answer, the following can be used with a non-blocking socket on the server end to inform whether the client has shut down.
This approach does not suffer the race condition that affects the Poll method in the accepted answer.
// Determines whether the remote end has called Shutdown
public bool HasRemoteEndShutDown
{
get
{
try
{
int bytesRead = socket.Receive(new byte[1], SocketFlags.Peek);
if (bytesRead == 0)
return true;
}
catch
{
// For a non-blocking socket, a SocketException with
// code 10035 (WSAEWOULDBLOCK) indicates no data available.
}
return false;
}
}
The approach is based on the fact that the Socket.Receive
method returns zero immediately after the remote end shuts down its socket and we've read all of the data from it. From Socket.Receive documentation:
If the remote host shuts down the Socket connection with the Shutdown method, and all available data has been received, the Receive method will complete immediately and return zero bytes.
If you are in non-blocking mode, and there is no data available in the protocol stack buffer, the Receive method will complete immediately and throw a SocketException.
The second point explains the need for the try-catch.
Use of the SocketFlags.Peek
flag leaves any received data untouched for a separate receive mechanism to read.
The above will work with a blocking socket as well, but be aware that the code will block on the Receive call (until data is received or the receive timeout elapses, again resulting in a SocketException
).
You can also check the .IsConnected property of the socket if you were to poll.
참고URL : https://stackoverflow.com/questions/722240/instantly-detect-client-disconnection-from-server-socket
'developer tip' 카테고리의 다른 글
Xcode4에서 * goto line * 명령은 어디에 있습니까? (0) | 2020.10.29 |
---|---|
Node.js에서 CTRL + C 감지 (0) | 2020.10.29 |
MATLAB 플로팅 그림의 창 제목을 변경하는 방법은 무엇입니까? (0) | 2020.10.29 |
지도에 대한 나만의 비교기를 어떻게 만들 수 있습니까? (0) | 2020.10.29 |
PHP로 PDF 파일 병합 (0) | 2020.10.28 |