Program Tip

소켓이 C #에서 연결 / 연결 해제되었는지 확인하는 방법은 무엇입니까?

programtip 2020. 12. 7. 20:35
반응형

소켓이 C #에서 연결 / 연결 해제되었는지 확인하는 방법은 무엇입니까?


네트워크 소켓 (System.Net.Sockets.Socket)이 연결 해제 될 때 패킷을 보내지 않는 경우 (예 : 비정상적으로 연결 해제 된 경우) 네트워크 소켓 (System.Net.Sockets.Socket)이 계속 연결되어 있는지 어떻게 확인할 수 있습니까?


폴 터너는 대답 Socket.Connected이 상황에서 사용할 수 없습니다. 연결이 여전히 활성 상태인지 확인하려면 매번 연결을 폴링해야합니다. 이것은 내가 사용한 코드입니다.

bool SocketConnected(Socket s)
{
    bool part1 = s.Poll(1000, SelectMode.SelectRead);
    bool part2 = (s.Available == 0);
    if (part1 && part2)
        return false;
    else
        return true;
}

다음과 같이 작동합니다.

  • s.Poll true를 반환하면
    • 연결이 닫힘, 재설정, 종료 또는 보류 중 (활성 연결이 없음을 의미)
    • 연결이 활성화되어 있고 읽을 수있는 데이터가 있습니다.
  • s.Available 읽을 수있는 바이트 수를 반환합니다.
  • 둘 다 사실 인 경우 :
    • 읽을 수있는 데이터가 없으므로 연결이 활성화되지 않았습니다.

으로 zendar가 쓴, 사용하기 좋은 Socket.Poll하고 Socket.Available,하지만 당신은 소켓이 처음에 초기화되지 않았을 수 있음을 고려해야 할 필요가있다. 이것은 정보의 마지막 부분이며 Socket.Connected부동산에서 제공 합니다. 수정 된 버전의 메서드는 다음과 같습니다.

 static bool IsSocketConnected(Socket s)
    {
        return !((s.Poll(1000, SelectMode.SelectRead) && (s.Available == 0)) || !s.Connected);

/* The long, but simpler-to-understand version:

        bool part1 = s.Poll(1000, SelectMode.SelectRead);
        bool part2 = (s.Available == 0);
        if ((part1 && part2 ) || !s.Connected)
            return false;
        else
            return true;

*/
    }

Socket.Connected속성은 소켓 여부를 알려줍니다 생각 이 연결되어. 실제로 소켓에서 수행 된 마지막 보내기 / 받기 작업의 상태를 반영합니다.

소켓이 자신의 작업 (소켓 폐기, 연결 해제 메서드 호출)에 의해 닫혔다면를 Socket.Connected반환 false합니다. 소켓이 다른 방법으로 연결이 끊어진 경우 true다음 번에 정보를 보내거나 받으려고 시도 할 때까지 속성이 반환 됩니다. 이때 a SocketException또는 ObjectDisposedException이 발생합니다.

예외가 발생한 후 속성을 확인할 수 있지만 이전에는 신뢰할 수 없습니다.


MSDN 기사를 기반으로 확장 방법을 만들었습니다 . 이것은 소켓이 여전히 연결되어 있는지 확인할 수있는 방법입니다.

public static bool IsConnected(this Socket client)
{
    bool blockingState = client.Blocking;

    try
    {
        byte[] tmp = new byte[1];

        client.Blocking = false;
        client.Send(tmp, 0, 0);
        return true;
    }
    catch (SocketException e)
    {
        // 10035 == WSAEWOULDBLOCK
        if (e.NativeErrorCode.Equals(10035))
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    finally
    {
        client.Blocking = blockingState;
    }
}

네트워크 케이블을 뽑으면 허용되는 대답이 작동하지 않는 것 같습니다. 또는 서버가 충돌합니다. 또는 라우터가 충돌합니다. 또는 인터넷 요금 지불을 잊은 경우. 안정성 향상을 위해 TCP 연결 유지 옵션을 설정합니다.

public static class SocketExtensions
{
    public static void SetSocketKeepAliveValues(this Socket instance, int KeepAliveTime, int KeepAliveInterval)
    {
        //KeepAliveTime: default value is 2hr
        //KeepAliveInterval: default value is 1s and Detect 5 times

        //the native structure
        //struct tcp_keepalive {
        //ULONG onoff;
        //ULONG keepalivetime;
        //ULONG keepaliveinterval;
        //};

        int size = Marshal.SizeOf(new uint());
        byte[] inOptionValues = new byte[size * 3]; // 4 * 3 = 12
        bool OnOff = true;

        BitConverter.GetBytes((uint)(OnOff ? 1 : 0)).CopyTo(inOptionValues, 0);
        BitConverter.GetBytes((uint)KeepAliveTime).CopyTo(inOptionValues, size);
        BitConverter.GetBytes((uint)KeepAliveInterval).CopyTo(inOptionValues, size * 2);

        instance.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);
    }
}



// ...
Socket sock;
sock.SetSocketKeepAliveValues(2000, 1000);

시간 값은 데이터가 마지막으로 전송 된 이후 시간 초과를 설정합니다. 그런 다음 Keep-alive 패킷을 보내고받습니다. 실패하면 연결이 끊어 졌다고 결정하기 전에 지정된 간격으로 10 번 (Vista AFAIK 이후 하드 코딩 된 번호) 재 시도합니다.

따라서 위의 값은 2 + 10 * 1 = 12 초 감지가됩니다. 그 후 읽기 / 쓰기 / 폴 작업이 소켓에서 실패해야합니다.


가장 좋은 방법은 단순히 클라이언트가 X 초마다 PING을 보내도록하고 서버가 잠시 동안 하나를받지 못한 후 연결이 끊어 졌다고 가정하는 것입니다.

소켓을 사용할 때와 동일한 문제가 발생했으며 이것이 내가 할 수있는 유일한 방법이었습니다. socket.connected 속성이 올바르지 않습니다.

결국 소켓보다 훨씬 더 안정적 이었기 때문에 WCF를 사용하기로 전환했습니다.


NibblyPigzendar 의 조언에 따라 아래 코드를 작성했습니다.이 코드는 제가 만든 모든 테스트에서 작동합니다. 나는 결국 핑과 설문 조사가 모두 필요했습니다. 핑은 케이블 연결이 끊어 졌는지 또는 그렇지 않으면 물리적 계층이 중단되었는지 (라우터 전원이 꺼진 경우 등) 알려줍니다. 그러나 때로는 다시 연결 한 후 RST를 얻고 ping은 정상이지만 tcp 상태는 그렇지 않습니다.

#region CHECKS THE SOCKET'S HEALTH
    if (_tcpClient.Client.Connected)
    {
            //Do a ping test to see if the server is reachable
            try
            {
                Ping pingTest = new Ping()
                PingReply reply = pingTest.Send(ServeripAddress);
                if (reply.Status != IPStatus.Success) ConnectionState = false;
            } catch (PingException) { ConnectionState = false; }

            //See if the tcp state is ok
            if (_tcpClient.Client.Poll(5000, SelectMode.SelectRead) && (_tcpClient.Client.Available == 0))
            {
                ConnectionState = false;
            }
        }
    }
    else { ConnectionState = false; }
#endregion

public static class SocketExtensions
{
    private const int BytesPerLong = 4; // 32 / 8
    private const int BitsPerByte = 8;

    public static bool IsConnected(this Socket socket)
    {
        try
        {
            return !(socket.Poll(1000, SelectMode.SelectRead) && socket.Available == 0);
        }
        catch (SocketException)
        {
            return false;
        }
    }


    /// <summary>
    /// Sets the keep-alive interval for the socket.
    /// </summary>
    /// <param name="socket">The socket.</param>
    /// <param name="time">Time between two keep alive "pings".</param>
    /// <param name="interval">Time between two keep alive "pings" when first one fails.</param>
    /// <returns>If the keep alive infos were succefully modified.</returns>
    public static bool SetKeepAlive(this Socket socket, ulong time, ulong interval)
    {
        try
        {
            // Array to hold input values.
            var input = new[]
            {
                (time == 0 || interval == 0) ? 0UL : 1UL, // on or off
                time,
                interval
            };

            // Pack input into byte struct.
            byte[] inValue = new byte[3 * BytesPerLong];
            for (int i = 0; i < input.Length; i++)
            {
                inValue[i * BytesPerLong + 3] = (byte)(input[i] >> ((BytesPerLong - 1) * BitsPerByte) & 0xff);
                inValue[i * BytesPerLong + 2] = (byte)(input[i] >> ((BytesPerLong - 2) * BitsPerByte) & 0xff);
                inValue[i * BytesPerLong + 1] = (byte)(input[i] >> ((BytesPerLong - 3) * BitsPerByte) & 0xff);
                inValue[i * BytesPerLong + 0] = (byte)(input[i] >> ((BytesPerLong - 4) * BitsPerByte) & 0xff);
            }

            // Create bytestruct for result (bytes pending on server socket).
            byte[] outValue = BitConverter.GetBytes(0);

            // Write SIO_VALS to Socket IOControl.
            socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
            socket.IOControl(IOControlCode.KeepAliveValues, inValue, outValue);
        }
        catch (SocketException)
        {
            return false;
        }

        return true;
    }
}
  1. SocketExtensions 클래스를 프로젝트에 복사합니다.
  2. 소켓에서 SetKeepAlive를 호출하십시오-socket.SetKeepAlive (1000, 2);
  3. IsConnected 기능을 확인하는 타이머 추가

으로 알렉산더 로거는 지적 zendar 의 대답, 완전히 확인하기 위해 뭔가를 보낼 수 있습니다. 연결된 파트너가이 소켓을 전혀 읽지 않는 경우 다음 코드를 사용할 수 있습니다.

bool SocketConnected(Socket s)
{
  // Exit if socket is null
  if (s == null)
    return false;
  bool part1 = s.Poll(1000, SelectMode.SelectRead);
  bool part2 = (s.Available == 0);
  if (part1 && part2)
    return false;
  else
  {
    try
    {
      int sentBytesCount = s.Send(new byte[1], 1, 0);
      return sentBytesCount == 1;
    }
    catch
    {
      return false;
    }
  }
}

But even then it might take a few seconds until a broken network cable or something similar is detected.


Just use the KeepAlive like @toster-cx says and then use the Socket Connected status to check if the Socket is still connected. Set your receive timeout at the same timeout of the keepalive. If you have more questions i am always happy to help!


Use Socket.Connected Property.

참고URL : https://stackoverflow.com/questions/2661764/how-to-check-if-a-socket-is-connected-disconnected-in-c

반응형