소켓 연결 시간 초과를 구성하는 방법


104

클라이언트가 연결이 끊긴 IP 주소에 연결을 시도 할 때 15 초 이상의 시간 초과가 있습니다.이 시간 초과를 어떻게 줄일 수 있습니까? 구성하는 방법은 무엇입니까?

소켓 연결을 설정하는 데 사용하는 코드는 다음과 같습니다.

try
{
    m_clientSocket = new Socket(
         AddressFamily.InterNetwork,
         SocketType.Stream,
         ProtocolType.Tcp);

    IPAddress ip = IPAddress.Parse(serverIp);
    int iPortNo = System.Convert.ToInt16(serverPort);
    IPEndPoint ipEnd = new IPEndPoint(ip, iPortNo);

    m_clientSocket.Connect(ipEnd);
    if (m_clientSocket.Connected)
    {
        lb_connectStatus.Text = "Connection Established";
        WaitForServerData();
    }
}
catch (SocketException se)
{
    lb_connectStatus.Text = "Connection Failed";
    MessageBox.Show(se.Message);
}

답변:


146

나는 이것을 찾았다. 허용되는 답변보다 간단하며 .NET v2에서 작동합니다.

Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

// Connect using a timeout (5 seconds)

IAsyncResult result = socket.BeginConnect( sIP, iPort, null, null );

bool success = result.AsyncWaitHandle.WaitOne( 5000, true );

if ( socket.Connected )
{
    socket.EndConnect( result );
}
else 
{
     // NOTE, MUST CLOSE THE SOCKET

     socket.Close();
     throw new ApplicationException("Failed to connect server.");
}

//... 

20
좋아, 이것에 대한 입력은 거의 없다. 나는 이것과 훨씬 적은 코드를 좋아한다. 그러나! 성공은 올바른 조건이 아니다. 대신 if (! _socket.Connected)를 추가하면 훨씬 더 잘 작동합니다. 나는 더 적은 것이 더 많은 측면에 대해 +1을 줄 것입니다.
TravisWhidden 2011 년

2
두 끝점에 따라 다릅니다. 둘 다 데이터 센터에 있다면 1 초이면 충분하고 3 초이면 충분하고 10 초이면 친절합니다. 한쪽 끝이 스마트 폰과 같은 모바일 장치에 있으면 30 초를 볼 수 있습니다.
FlappySocks

3
또 다른 것은 너무 만약 대신 넣는 ... 피려 null에 대한에 callback당신은에 계획 EndConnect()소켓이 있었는지, closed다음이 당신에게 예외를 제공 할 것입니다. 그러니 확인하십시오 ...
poy

9
제한 시간을 줄이는 대신 늘리려면 어떻게해야합니까? 비동기 방식을 사용하면 코드가 20 초 동안 기다리지 않도록 할 수 있다고 생각합니다 (소켓 연결에 설정된 내부 제한 시간). 그러나 연결이 더 오래 걸리면 BeginConnect가 중지됩니다. 아니면 BeginConnect는 내부적으로 영원히 대기합니까? 때때로 연결하는 데 최대 30-40 초가 필요할 때 연결 속도가 매우 느리고 21 초의 시간 초과가 매우 자주 발생합니다.
Alex

3
@TravisWhidden 확인할 수 있습니다, 이것은 매우 중요합니다! 내 경험상 끝점에 도달 할 수 있지만 연결을 수신 할 수있는 끝점에 서버가 없으면 AsyncWaitHandle.WaitOne신호를 보내지 만 소켓은 연결되지 않은 상태로 유지됩니다.
Nicholas Miller

29

내 테이크 :

public static class SocketExtensions
{
    /// <summary>
    /// Connects the specified socket.
    /// </summary>
    /// <param name="socket">The socket.</param>
    /// <param name="endpoint">The IP endpoint.</param>
    /// <param name="timeout">The timeout.</param>
    public static void Connect(this Socket socket, EndPoint endpoint, TimeSpan timeout)
    {
        var result = socket.BeginConnect(endpoint, null, null);

        bool success = result.AsyncWaitHandle.WaitOne(timeout, true);
        if (success)
        {
            socket.EndConnect(result);
        }
        else
        {
            socket.Close();
            throw new SocketException(10060); // Connection timed out.
        }
    }
}

나는 조건을 처리 할 자유를 얻었습니다. 괜찮 으시길 바랍니다.
인 Hemant

으로 만들어진 것을 제외하고는 사본으로 보이는 최고 등급 답변에 대한 의견에 따라 SocketExtension아직 사용 .Connected중인지 확인하지 않았 socket.Connected = true;으며을 정의하는 데 사용하지 않습니다 success.
vapcguy

22

연결 시간 초과를 허용하기 위해 확장 클래스를 작성했습니다. Connect()라는 추가 매개 변수와 함께 표준 메소드 를 사용하는 것과 똑같이 사용하십시오 timeout.

using System;
using System.Net;
using System.Net.Sockets;

/// <summary>
/// Extensions to Socket class
/// </summary>
public static class SocketExtensions
{
    /// <summary>
    /// Connects the specified socket.
    /// </summary>
    /// <param name="socket">The socket.</param>
    /// <param name="host">The host.</param>
    /// <param name="port">The port.</param>
    /// <param name="timeout">The timeout.</param>
    public static void Connect(this Socket socket, string host, int port, TimeSpan timeout)
    {
        AsyncConnect(socket, (s, a, o) => s.BeginConnect(host, port, a, o), timeout);
    }

    /// <summary>
    /// Connects the specified socket.
    /// </summary>
    /// <param name="socket">The socket.</param>
    /// <param name="addresses">The addresses.</param>
    /// <param name="port">The port.</param>
    /// <param name="timeout">The timeout.</param>
    public static void Connect(this Socket socket, IPAddress[] addresses, int port, TimeSpan timeout)
    {
        AsyncConnect(socket, (s, a, o) => s.BeginConnect(addresses, port, a, o), timeout);
    }

    /// <summary>
    /// Asyncs the connect.
    /// </summary>
    /// <param name="socket">The socket.</param>
    /// <param name="connect">The connect.</param>
    /// <param name="timeout">The timeout.</param>
    private static void AsyncConnect(Socket socket, Func<Socket, AsyncCallback, object, IAsyncResult> connect, TimeSpan timeout)
    {
        var asyncResult = connect(socket, null, null);
        if (!asyncResult.AsyncWaitHandle.WaitOne(timeout))
        {
            try
            {
                socket.EndConnect(asyncResult);
            }
            catch (SocketException)
            { }
            catch (ObjectDisposedException)
            { }
        }
    }

8
GhostDoc의 팬 이시죠? ;-) "연결 비동기"-고전적인 GhostDoc WTFness.
KeithS

1
:) 예, 때로는 생성 된 내용의 독자조차도 아닙니다.
picrap 2011-06-25

socket.Close보다 socket.EndConnect가 더 낫습니까?
Kiquenet

3
socket.EndConnect하지 시간 범위 이후하지만 시간 범위 + endConnect 시간 후에 함수가 반환되도록 닫 ~ 10 초 소요
Royi Namir에게

8

나는 C #으로 프로그램하지 않지만 C에서는 소켓을 비 블로킹으로 만든 다음 연결을 기다릴 시간과 같은 시간 제한 값으로 fd를 선택 / 폴 루프에 넣어 동일한 문제를 해결합니다. 성공하기 위해.

나는 이것을 Visual C ++ 에서 찾았고 설명은 내가 전에 설명한 선택 / 폴링 메커니즘으로 구부러져 있습니다.

내 경험상 소켓 당 연결 시간 제한 값을 변경할 수 없습니다. OS 매개 변수를 조정하여 모두에 대해 변경합니다.


7

너무 늦을 수도 있지만 Task.WaitAny (c # 5 +)를 기반으로 한 깔끔한 솔루션이 있습니다.

 public static bool ConnectWithTimeout(this Socket socket, string host, int port, int timeout)
        {
            bool connected = false;
            Task result = socket.ConnectAsync(host, port);               
            int index = Task.WaitAny(new[] { result }, timeout);
            connected = socket.Connected;
            if (!connected) {
              socket.Close();
            }

            return connected;
        }

"ConnectAsync"오버로드가 호스트 및 포트를 허용합니까?
marsh-wiggle

@ marsh-wiggle, "ConnectAsync"메서드에는 4 개의 오버로드가 있습니다. docs.microsoft.com/en-us/dotnet/api/… 확장 메서드 섹션을 참조하십시오
Oleg Bondarenko

1
@OlegBondarenko 좋아, .net 4.5.1에는 사용할 수 없습니다. 직접 포장해야합니다. 감사!
marsh-wiggle

5

Socket.Connect 메서드 대신 Socket.ConnectAsync 메서드를 사용하여 문제를 해결했습니다. Socket.ConnectAsync (SocketAsyncEventArgs)를 호출 한 후 타이머 (timer_connection)를 시작하고, 시간이 다되면 소켓 연결이 연결되었는지 확인하고 (if (m_clientSocket.Connected)) 그렇지 않은 경우 팝업 타임 아웃 오류가 발생합니다.

private void connect(string ipAdd,string port)
    {
        try
        {
            SocketAsyncEventArgs e=new SocketAsyncEventArgs();


            m_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            IPAddress ip = IPAddress.Parse(serverIp);
            int iPortNo = System.Convert.ToInt16(serverPort);
            IPEndPoint ipEnd = new IPEndPoint(ip, iPortNo);

            //m_clientSocket.
            e.RemoteEndPoint = ipEnd;
            e.UserToken = m_clientSocket;
            e.Completed+=new EventHandler<SocketAsyncEventArgs>(e_Completed);                
            m_clientSocket.ConnectAsync(e);

            if (timer_connection != null)
            {
                timer_connection.Dispose();
            }
            else
            {
                timer_connection = new Timer();
            }
            timer_connection.Interval = 2000;
            timer_connection.Tick+=new EventHandler(timer_connection_Tick);
            timer_connection.Start();
        }
        catch (SocketException se)
        {
            lb_connectStatus.Text = "Connection Failed";
            MessageBox.Show(se.Message);
        }
    }
private void e_Completed(object sender,SocketAsyncEventArgs e)
    {
        lb_connectStatus.Text = "Connection Established";
        WaitForServerData();
    }
    private void timer_connection_Tick(object sender, EventArgs e)
    {
        if (!m_clientSocket.Connected)
        {
            MessageBox.Show("Connection Timeout");
            //m_clientSocket = null;

            timer_connection.Stop();
        }
    }

2
타이머가 멈 추면 오류 메시지가 표시 되죠? 이것이 TCP 스택이 실제로 연결되는 것을 어떻게 막을까요? 원격 호스트가 2 초 이상 떨어져있는 시나리오 (예 : rto> 2)를 상상해보십시오. 타이머가 중지되고 오류 메시지가 인쇄됩니다. 그러나 TCP는 타이머에 의해 제어되지 않습니다. 여전히 연결을 시도하고 2 초 후에 성공적으로 연결할 수 있습니다. C #은 "연결"요청 또는 소켓 닫기를 취소하는 기능을 제공합니까? 타이머 솔루션은 2 초 후 연결 성공 여부를 확인하는 것과 같습니다.
Aditya Sehgal 2009-06-30

나는 이것을 발견했다 : splinter.com.au/blog/?p=28 이것이 방법 인 것 같다. 당신의 것과 비슷하지만 위에서 설명한 것처럼 생각합니다.
Aditya Sehgal

시간이 초과되면 m_clientSocket.Close ();
Vincent McNabb

업데이트, aditya가 참조한 내 블로그 링크가 변경되었습니다. splinter.com.au/opening-a-tcp-connection-in-c-with-a-custom-t
Chris

"timer_connection.Dispose ();"호출과 관련된 논리를 다시 작성합니다. timer_connection 개체 참조는 개체가 삭제 된 후에 사용될 수 있습니다.
BoiseBaked

2

MSDN에서 이것을 확인하십시오 . Socket 클래스에서 구현 된 속성으로이 작업을 수행 할 수없는 것 같습니다.

MSDN의 포스터는 실제로 스레딩을 사용하여 문제해결했습니다 . 그는 몇 초 동안 연결 코드를 실행하는 다른 스레드를 호출 한 다음 소켓의 Connected 속성을 확인하는 메인 스레드가 있습니다.

실제로 소켓에 연결된 다른 메서드를 만들었습니다. 주 스레드가 2 초 동안 잠자고 소켓이 잘 연결되어 있는지 연결 방법 (별도의 스레드에서 실행 됨)을 확인하고 그렇지 않으면 "Timed out"예외가 발생합니다. 그리고 그게 전부입니다. 보충 해 주셔서 다시 한 번 감사드립니다.

무엇을하려고하는데 왜 시간이 초과되기까지 15-30 초 동안 기다릴 수 없습니까?


2

나는 소켓에 연결할 때 같은 문제가 있었고 아래 해결책을 생각해 냈습니다. 잘 작동합니다. `

private bool CheckConnectivityForProxyHost(string hostName, int port)
       {
           if (string.IsNullOrEmpty(hostName))
               return false;

           bool isUp = false;
           Socket testSocket = null;

           try
           {

               testSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
               IPAddress ip = null;
               if (testSocket != null && NetworkingCollaboratorBase.GetResolvedConnecionIPAddress(hostName, out ip))//Use a method to resolve your IP
               {
                   IPEndPoint ipEndPoint = new IPEndPoint(ip, port);

                   isUp = false;
//time out 5 Sec
                  CallWithTimeout(ConnectToProxyServers, 5000, testSocket, ipEndPoint);

                       if (testSocket != null && testSocket.Connected)
                       {
                           isUp = true;
                       }
                   }

               }
           }
           catch (Exception ex)
           {
               isUp = false;
           }
           finally
           {
               try
               {
                   if (testSocket != null)
                   {
                       testSocket.Shutdown(SocketShutdown.Both);
                   }
               }
               catch (Exception ex)
               {

               }
               finally
               {
                   if (testSocket != null)
                       testSocket.Close();
               }

           }

           return isUp;
       }


 private void CallWithTimeout(Action<Socket, IPEndPoint> action, int timeoutMilliseconds, Socket socket, IPEndPoint ipendPoint)
       {
           try
           {
               Action wrappedAction = () =>
               {
                   action(socket, ipendPoint);
               };

               IAsyncResult result = wrappedAction.BeginInvoke(null, null);

               if (result.AsyncWaitHandle.WaitOne(timeoutMilliseconds))
               {
                   wrappedAction.EndInvoke(result);
               }

           }
           catch (Exception ex)
           {

           }
       }

  private void ConnectToProxyServers(Socket testSocket, IPEndPoint ipEndPoint)
       {
           try
           {
               if (testSocket == null || ipEndPoint == null)
                   return;

                   testSocket.Connect(ipEndPoint);

           }
           catch (Exception ex)
           {

           }
       } 

1

Unity와 함께 작업했고 BeginConnect 및 소켓의 다른 비동기 메서드에 문제가있었습니다.

내가 이해하지 못하는 것이 있지만 이전의 코드 샘플은 나를 위해 작동하지 않습니다.

그래서이 코드를 작성하여 작동하게했습니다. 나는 내 컴퓨터의 로컬에서도 android 및 pc로 임시 네트워크에서 테스트합니다. 도움이되기를 바랍니다.

using System.Net.Sockets;
using System.Threading;
using System.Net;
using System;
using System.Diagnostics;

class ConnexionParameter : Guardian
{
    public TcpClient client;
    public string address;
    public int port;
    public Thread principale;
    public Thread thisthread = null;
    public int timeout;

    private EventWaitHandle wh = new AutoResetEvent(false);

    public ConnexionParameter(TcpClient client, string address, int port, int timeout, Thread principale)
    {
        this.client = client;
        this.address = address;
        this.port = port;
        this.principale = principale;
        this.timeout = timeout;
        thisthread = new Thread(Connect);
    }


    public void Connect()
    {
        WatchDog.Start(timeout, this);
        try
        {
            client.Connect(IPAddress.Parse(address), port);

        }
        catch (Exception)
        {
            UnityEngine.Debug.LogWarning("Unable to connect service (Training mode? Or not running?)");
        }
        OnTimeOver();
        //principale.Resume();
    }

    public bool IsConnected = true;
    public void OnTimeOver()
    {
        try
        {
            if (!client.Connected)
            {
                    /*there is the trick. The abort method from thread doesn't
 make the connection stop immediately(I think it's because it rise an exception
 that make time to stop). Instead I close the socket while it's trying to
 connect , that make the connection method return faster*/
                IsConnected = false;

                client.Close();
            }
            wh.Set();

        }
        catch(Exception)
        {
            UnityEngine.Debug.LogWarning("Connexion already closed, or forcing connexion thread to end. Ignore.");
        }
    }


    public void Start()
    {

        thisthread.Start();
        wh.WaitOne();
        //principale.Suspend();
    }

    public bool Get()
    {
        Start();
        return IsConnected;
    }
}


public static class Connexion
{


    public static bool Connect(this TcpClient client, string address, int port, int timeout)
    {
        ConnexionParameter cp = new ConnexionParameter(client, address, port, timeout, Thread.CurrentThread);
        return cp.Get();
    }

//http://stackoverflow.com/questions/19653588/timeout-at-acceptsocket
    public static Socket AcceptSocket(this TcpListener tcpListener, int timeoutms, int pollInterval = 10)
    {
        TimeSpan timeout = TimeSpan.FromMilliseconds(timeoutms);
        var stopWatch = new Stopwatch();
        stopWatch.Start();
        while (stopWatch.Elapsed < timeout)
        {
            if (tcpListener.Pending())
                return tcpListener.AcceptSocket();

            Thread.Sleep(pollInterval);
        }
        return null;
    }


}

C #에 대한 매우 간단한 감시 장치가 있습니다.

using System.Threading;

public interface Guardian
{
    void OnTimeOver();
}

public class WatchDog {

    int m_iMs;
    Guardian m_guardian;

    public WatchDog(int a_iMs, Guardian a_guardian)
    {
        m_iMs = a_iMs;
        m_guardian = a_guardian;
        Thread thread = new Thread(body);
        thread.Start(this);
    }


    private void body(object o)
    {
        WatchDog watchdog = (WatchDog)o;
        Thread.Sleep(watchdog.m_iMs);
        watchdog.m_guardian.OnTimeOver();
    }

    public static void Start(int a_iMs, Guardian a_guardian)
    {
        new WatchDog(a_iMs, a_guardian);
    }
}

1

이것은 FlappySock의 대답과 비슷하지만 레이아웃과 Boolean이 반환되는 방식이 마음에 들지 않았기 때문에 콜백을 추가했습니다. Nick Miller의 답변에 대한 의견 :

내 경험상 끝점에 도달 할 수 있지만 연결을받을 수있는 끝점에 서버가 없으면 AsyncWaitHandle.WaitOne이 신호를 받지만 소켓은 연결되지 않은 상태로 유지됩니다.

그래서 나에게 반환되는 것에 의존하는 것은 위험 할 수있는 것처럼 보인다 socket.Connected. 나는 . nullable 부울을 설정하고 콜백 함수에서 업데이트합니다. 또한 주 함수로 돌아 가기 전에 항상 결과보고를 완료하는 것은 아니라는 것을 발견했습니다. 또한이를 처리하고 시간 제한을 사용하여 결과를 기다리도록합니다.

private static bool? areWeConnected = null;

private static bool checkSocket(string svrAddress, int port)
{
    IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse(svrAddress), port);
    Socket socket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

    int timeout = 5000; // int.Parse(ConfigurationManager.AppSettings["socketTimeout"].ToString());
    int ctr = 0;
    IAsyncResult ar = socket.BeginConnect(endPoint, Connect_Callback, socket);
    ar.AsyncWaitHandle.WaitOne( timeout, true );

    // Sometimes it returns here as null before it's done checking the connection
    // No idea why, since .WaitOne() should block that, but it does happen
    while (areWeConnected == null && ctr < timeout)
    {
        Thread.Sleep(100);
        ctr += 100;
    } // Given 100ms between checks, it allows 50 checks 
      // for a 5 second timeout before we give up and return false, below

    if (areWeConnected == true)
    {
        return true;
    }
    else
    {
        return false;
    }
}

private static void Connect_Callback(IAsyncResult ar)
{
    areWeConnected = null;
    try
    {
        Socket socket = (Socket)ar.AsyncState;
        areWeConnected = socket.Connected;
        socket.EndConnect(ar);
    }
    catch (Exception ex)
    {
      areWeConnected = false;
      // log exception 
    }
}

관련 : 연결되어 있는지 확인하는 방법은 무엇입니까?


-8

Socket 클래스에는 ReceiveTimeout 속성이 있어야합니다.

Socket.ReceiveTimeout 속성


1
나는 시도했다. 작동하지 않습니다. m_clientSocket.ReceiveTimeout = 1000을 추가했습니다. m_clientSocket.Connect (ipEnd)를 호출하기 전에. 그러나 예외 메시지가 팝업되기 전에 약 15-20 초 동안 대기합니다.
ninikin

2
연결이 이루어진 후 소켓이 데이터를 수신 할 때의 시간 제한을 설정합니다.
eric.christensen

1
사용할 수 없음 ReceiveTimeout- BeginReceive및로 수신하는 경우 에만 해당됩니다 EndReceive. 당신이 연결되어 있는지 볼 때에 상응하는 것은 없습니다.
vapcguy
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.