Cara mengkonfigurasi batas waktu koneksi soket

104

Ketika Klien mencoba untuk menyambung ke alamat IP yang terputus, ada waktu tunggu yang lama selama 15 detik ... Bagaimana kami dapat mengurangi batas waktu ini? Apa metode untuk mengkonfigurasinya?

Kode yang saya gunakan untuk menyiapkan koneksi soket adalah sebagai berikut:

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);
}
ninikin
sumber

Jawaban:

146

Aku menemukan ini. Lebih sederhana dari jawaban yang diterima, dan berfungsi dengan .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.");
}

//... 
FlappySocks
sumber
20
OK, sedikit masukan yang satu ini-- Saya suka ini dan kodenya jauh lebih sedikit .. namun! Sukses bukanlah kondisi yang benar. Sebagai gantinya, tambahkan if (! _Socket.Connected) dan itu bekerja jauh lebih baik. Saya akan memberikannya +1 untuk lebih sedikit lebih banyak aspek.
TravisWhidden
2
Tergantung pada dua titik akhir Anda. Jika keduanya berada di pusat data, maka 1 detik sudah cukup, 3 untuk ukuran yang baik, 10 untuk kebaikan. Jika salah satu ujungnya ada di perangkat seluler, seperti smartphone, maka Anda mungkin melihat ke 30 detik.
FlappySocks
3
Hal lain juga melihat keluar untuk ... Jika bukannya menempatkan nulldi untuk callbackdan Anda berencana untuk EndConnect(), jika soket telah closedmaka ini akan memberikan pengecualian. Jadi pastikan Anda memeriksa ...
poy
9
Bagaimana jika saya ingin MENINGKATKAN batas waktu daripada MENURUNKANnya? Saya pikir pendekatan async hanya memungkinkan Anda membuat kode tidak menunggu selama 20 detik (batas waktu internal diatur dalam soket terhubung). Tetapi jika koneksi membutuhkan waktu lebih lama, BeginConnect akan tetap berhenti. Atau, apakah BeginConnect menunggu secara internal selamanya? Saya memiliki koneksi yang sangat lambat ketika terkadang dibutuhkan hingga 30-40 detik untuk terhubung, dan waktu tunggu pada detik ke-21 sangat sering terjadi.
Alex
3
@TravisWhidden Dapat mengonfirmasi, ini sangat penting! Menurut pengalaman saya, jika titik akhir dapat dicapai, tetapi tidak ada server di titik akhir yang dapat menerima koneksi, maka AsyncWaitHandle.WaitOneakan diberi sinyal, tetapi soket akan tetap tidak tersambung.
Nicholas Miller
29

Saya ambil:

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.
        }
    }
}
bevacqua.dll
sumber
Saya mengambil kebebasan untuk menangani suatu kondisi. Harap Anda tidak keberatan.
Hemant
Per komentar pada jawaban peringkat teratas, yang tampaknya merupakan salinan kecuali dibuat menjadi SocketExtension, Anda masih belum terbiasa .Connectedmelihat apakah Anda, dan Anda tidak menggunakan socket.Connected = true;untuk mendefinisikan success.
vapcguy
22

Saya baru saja menulis kelas ekstensi untuk memungkinkan batas waktu dalam koneksi. Gunakan persis seperti Anda akan menggunakan Connect()metode standar , dengan parameter tambahan bernama 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)
            { }
        }
    }
picrap
sumber
8
Penggemar GhostDoc, ya? ;-) "Asyncs the connect" - GhostDoc WTFness klasik.
KeithS
1
:) ya, dan terkadang bahkan bukan pembaca dari apa yang telah dihasilkan.
picrap
Socket.EndConnect lebih baik daripada socket.Close?
Kiquenet
3
The socket.EndConnectmengambil ~ 10 detik untuk menutup sehingga fungsi kembali tidak setelah rentang waktu tetapi setelah waktu jangka waktu yang + endConnect
Royi Namir
8

Saya tidak memprogram di C # tetapi di C, kami menyelesaikan masalah yang sama dengan membuat soket non-pemblokiran dan kemudian meletakkan fd di loop pilih / polling dengan nilai batas waktu yang sama dengan jumlah waktu kami bersedia menunggu untuk terhubung untuk berhasil.

Saya menemukan ini untuk Visual C ++ dan penjelasannya di sana juga mengarah ke mekanisme pemilihan / jajak pendapat yang saya jelaskan sebelumnya.

Dalam pengalaman saya, Anda tidak dapat mengubah nilai batas waktu koneksi per soket. Anda mengubahnya untuk semua (dengan menyetel parameter OS).

Aditya Sehgal
sumber
7

mungkin sudah terlambat tetapi ada solusi tepat berdasarkan 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;
        }
Oleg Bondarenko
sumber
Apakah ada overload "ConnectAsync" yang menerima host dan port?
marsh-wiggle
@ marsh-wiggle, metode "ConnectAsync" memiliki 4 kelebihan docs.microsoft.com/en-us/dotnet/api/… Silakan lihat bagian Metode Ekstensi
Oleg Bondarenko
1
@OlegBondarenko ok, tidak tersedia untuk .net 4.5.1. Saya harus membungkusnya sendiri. Terima kasih!
marsh-wiggle
5

Saya memecahkan masalah dengan menggunakan Metode Socket.ConnectAsync daripada Metode Socket.Connect. Setelah menjalankan Socket.ConnectAsync (SocketAsyncEventArgs), mulai pengatur waktu (timer_connection), jika waktu habis, periksa apakah koneksi soket terhubung (jika (m_clientSocket.Connected)), jika tidak, muncul kesalahan batas waktu.

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();
        }
    }
ninikin
sumber
2
Ketika pengatur waktu berhenti, Anda menunjukkan pesan kesalahan bukan? Bagaimana ini menghentikan tumpukan TCP Anda benar-benar terhubung. Bayangkan sebuah skenario, di mana sebuah remote host berjarak lebih dari 2 detik yaitu rto> 2. Timer Anda akan berhenti dan Anda akan mencetak pesan kesalahan. Namun, TCP tidak dikontrol oleh pengatur waktu Anda. Itu masih akan mencoba untuk terhubung dan mungkin berhasil terhubung setelah 2 detik. Apakah C # menyediakan fasilitas untuk membatalkan permintaan "menghubungkan" atau menutup soket. Solusi pengatur waktu Anda sama dengan memeriksa setelah 2 detik apakah koneksi berhasil.
Aditya Sehgal
Saya menemukan ini: splinter.com.au/blog/?p=28 Sepertinya begini caranya. Ini mirip dengan milik Anda tetapi saya pikir itu melakukan apa yang saya jelaskan di atas.
Aditya Sehgal
Jika waktu habis, Anda harus memanggil m_clientSocket.Close ();
Vincent McNabb
Update, link blog saya yang direferensikan oleh aditya telah berubah: splinter.com.au/opening-a-tcp-connection-in-c-with-a-custom-t
Chris
Saya akan menulis ulang logika yang terkait dengan panggilan "timer_connection.Dispose ();". Referensi objek timer_connection mungkin digunakan setelah objek tersebut dibuang.
BoiseBaked
2

Lihat ini di MSDN . Tampaknya Anda tidak dapat melakukan ini dengan properti yang diimplementasikan di kelas Socket.

Poster di MSDN benar-benar memecahkan masalahnya dengan menggunakan threading. Dia memiliki utas utama yang memanggil utas lain yang menjalankan kode koneksi selama beberapa detik dan kemudian memeriksa properti Connected dari soket:

Saya membuat metode lain yang benar-benar menghubungkan soket ... membuat utas utama tidur selama 2 detik dan kemudian memeriksa metode penghubung (yang dijalankan di utas terpisah) jika soket terhubung dengan baik jika tidak, lemparkan pengecualian "Waktu habis" dan itu saja. Sekali lagi terima kasih atas tanggapannya.

Apa yang Anda coba lakukan, dan mengapa tidak bisa menunggu selama 15-30 detik sebelum waktunya habis?

eric.christensen
sumber
2

Saya memiliki masalah yang sama saat menghubungkan ke Socket dan saya menemukan solusi di bawah ini, Ini berfungsi dengan baik untuk saya. `

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)
           {

           }
       } 
Tyronne Thomas
sumber
1

Saya bekerja dengan Unity dan mengalami masalah dengan BeginConnect dan metode asinkron lainnya dari soket.

Ada sesuatu yang tidak saya mengerti tetapi contoh kode sebelumnya tidak berfungsi untuk saya.

Jadi saya menulis kode ini untuk membuatnya berfungsi. Saya mengujinya di jaringan adhoc dengan android dan pc, juga di lokal di komputer saya. Semoga bisa membantu.

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;
    }


}

dan ada pengawas yang sangat sederhana di C # untuk membuatnya bekerja:

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);
    }
}
Hugo Zevetel
sumber
1

Ini seperti jawaban FlappySock, tapi saya menambahkan callback karena saya tidak suka tata letaknya dan bagaimana Boolean dikembalikan. Di komentar jawaban dari Nick Miller:

Menurut pengalaman saya, jika titik akhir dapat dicapai, tetapi tidak ada server pada titik akhir yang dapat menerima koneksi, maka AsyncWaitHandle.WaitOne akan diberi tanda, tetapi soket akan tetap tidak tersambung

Jadi bagi saya, tampaknya mengandalkan apa yang dikembalikan bisa berbahaya - saya lebih suka menggunakan socket.Connected. Saya menetapkan Boolean nullable dan memperbaruinya dalam fungsi callback. Saya juga menemukan itu tidak selalu selesai melaporkan hasil sebelum kembali ke fungsi utama - saya menanganinya juga, dan membuatnya menunggu hasil menggunakan batas waktu:

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 
    }
}

Terkait: Bagaimana cara memeriksa apakah saya terhubung?

vapcguy
sumber
-8

Harus ada properti ReceiveTimeout di kelas Socket.

Socket.ReceiveTimeout Properti

Colin
sumber
1
Saya mencoba. Itu tidak berhasil. Saya menambahkan m_clientSocket.ReceiveTimeout = 1000; sebelum memanggil m_clientSocket.Connect (ipEnd). Namun, masih menunggu sekitar 15-20 detik sebelum memunculkan pesan pengecualian.
ninikin
2
Ini menetapkan batas waktu saat soket menerima data setelah koneksi dibuat.
eric.christensen
1
Tidak dapat digunakan ReceiveTimeout- ini hanya untuk saat menerima dengan BeginReceivedan EndReceive. Tidak ada padanan ketika Anda hanya melihat apakah Anda terhubung.
vapcguy