2010-04-18 24 views

risposta

75

Come Paul Turner risposta Socket.Connected non può essere utilizzato in questa situazione. È necessario eseguire il polling della connessione ogni volta per verificare se la connessione è ancora attiva. Questo è il codice che ho usato:

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

Funziona così:

  • s.Poll restituisce vero se
    • connessione è chiusa, azzerato, terminati o in corso (che significa nessuna connessione attiva)
    • connessione attiva e sono disponibili dati per la lettura
  • s.Available restituisce il numero di byte disponibili per la lettura
  • se entrambe sono vere:
    • non vi sono dati disponibili per la lettura in modo connessione non è attiva
+0

Penso che non sia necessario controllare la proprietà 'Available'. Secondo la pagina [MSDN] del metodo 'Poll' (http://msdn.microsoft.com/en-us/library/system.net.sockets.spocket.poll.aspx), restituisce true anche ** se i dati sono disponibile per la lettura **. –

+3

Leggi di nuovo la documentazione, restituisce anche true se la connessione è chiusa o in attesa, quindi non sai se è attiva a meno che non controlli se un altro lato ti ha inviato alcuni dati. – zendar

+3

Come one-liner: 'return! (Socket.Poll (1, SelectMode.SelectRead) && socket.Available == 0)' – kasperhj

-11

Proprietà Socket.Connected.

+7

Basta notare che, anche se Socket.connected restituisce true, la presa potrebbe non essere collegata. Se è falso, non è sicuramente connesso. – nos

12

La proprietà Socket.Connected ti dirà se una presa è collegata allo. In realtà riflette lo stato dell'ultima operazione di invio/ricezione eseguita sul socket.

Se il socket è stato chiuso con le proprie azioni (smaltimento del socket, metodi di chiamata per la disconnessione), Socket.Connected restituirà false. Se la presa è stata disconnessa con altri mezzi, la proprietà restituirà true fino al prossimo tentativo di inviare o ricevere informazioni, a quel punto verrà lanciato uno SocketException o ObjectDisposedException.

È possibile controllare la proprietà dopo che si è verificata l'eccezione, ma non è affidabile prima.

1

La migliore il modo è semplicemente di avere il client che invia un PING ogni X secondi e che il server presume che sia disconnesso dopo non averne ricevuto uno per un po '.

Ho riscontrato lo stesso problema riscontrato quando si utilizzavano i socket, e questo era l'unico modo per farlo. La proprietà socket.connected non è mai stata corretta.

Alla fine, però, sono passato a utilizzare WCF perché era molto più affidabile delle prese.

21

Come zendar scritto, è bello utilizzare il Socket.Poll e Socket.Available, ma è necessario prendere in considerazione che la presa non avrebbe potuto essere inizializzato in primo luogo. Questa è l'ultima informazione (credo) ed è fornita dalla proprietà Socket.Connected.La versione rivista del metodo sarebbe simile a questa:

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; 

*/ 
    } 
3

ho fatto un metodo di estensione in base a this articolo di MSDN. Ecco come è possibile determinare se un socket è ancora connesso.

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

Seguendo i consigli da NibblyPig e zendar, mi si avvicinò con il codice qui sotto, che funziona su tutti i test che ho fatto. Alla fine ho avuto bisogno sia del ping che del sondaggio. Il ping mi consente di sapere se il cavo è stato disconnesso, altrimenti il ​​livello fisico è stato interrotto (router spento, ecc.). Ma a volte dopo la riconnessione ottengo un RST, il ping è ok, ma lo stato TCP non lo è.

#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 
4

La risposta accettata non sembra funzionare se si scollega il cavo di rete. O il server si blocca. O il tuo router si blocca. O se ti dimentichi di pagare la bolletta internet. Imposta le opzioni keep-alive TCP per una migliore affidabilità.

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

Il valore temporale imposta il timeout in quanto i dati dall'ultimo invio. Quindi tenta di inviare e ricevere un pacchetto keep-alive. Se fallisce, ripete 10 volte (numero hardcoded da Vista AFAIK) nell'intervallo specificato prima di decidere che la connessione è morta.

Quindi i valori precedenti comporterebbero 2 + 10 * 1 = 12 secondi di rilevamento. Dopo ciò, qualsiasi operazione di lettura/scrittura/sondaggio dovrebbe fallire sul socket.

+0

Scollego il socket con la forza (da uno strumento come la porta curr), ma non c'era la riconnessione automatica. – Eitan

+1

Non si riconnetterà automaticamente. È solo un modo "migliore" per rilevare quando una connessione si interrompe senza inviare alcun dato. –

1
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. Copia la classe SocketExtensions al progetto
  2. chiamata la SetKeepAlive sulla presa - socket.SetKeepAlive (1000, 2);
  3. aggiungere un timer per controllare la funzione IsConnected
Problemi correlati