2010-01-19 2 views
5

Qualcuno può spiegarmi perché il seguente codice non funziona?Problema di socket durante l'utilizzo della filettatura

using System; 
using System.Collections.Generic; 
using System.Text; 
using System.Net.Sockets; 
using System.Net; 
using System.Threading; 

namespace SocketThreadingTest 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      Thread t = new Thread(delegate() 
      { 
       BeginConnect(new IPEndPoint("some address")); 
      }); 
      t.Start(); 

      Console.ReadKey(); 
     } 

     public static void BeginConnect(IPEndPoint address) 
     { 
      try 
      { 
       Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 
       socket.BeginConnect(address, ConnectCallback, socket); 
      } 
      catch (Exception ex) 
      { 
       Console.WriteLine(ex); 
      } 
     } 

     private static void ConnectCallback(IAsyncResult ar) 
     { 
      Socket sock = (Socket)ar.AsyncState; 
      try 
      { 
       sock.EndConnect(ar); 
       Console.WriteLine("Connected {0}", sock.LocalEndPoint); 

       sock.Send(Encoding.UTF8.GetBytes("Hello")); 

       Console.WriteLine("success"); 
       sock.Close(); 
      } 
      catch (Exception ex) 
      { 
       Console.WriteLine("send ex " + ex); 

       if (sock != null) 
        sock.Close(); 
      } 
     } 
    } 
} 

L'uscita è (notare il punto finale locale del socket):

Connected 0.0.0.0:28142 
send ex System.Net.Sockets.SocketException: A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram 
socket using a sendto call) no address was supplied 
    at System.Net.Sockets.Socket.Send(Byte[] buffer, Int32 offset, Int32 size, So 
cketFlags socketFlags) 
    at System.Net.Sockets.Socket.Send(Byte[] buffer) 
    at SocketThreadingTest.Program.ConnectCallback(IAsyncResult ar) in Program.cs:line 44 

Naturalmente, quando non uso un filo e chiamo BeginConnect direttamente funziona bene. Ciò che è ancora più sconcertante è che l'aggiunta di un Thread.Sleep che è abbastanza lungo (1 secondo) funziona anche bene. Qualche idea? Grazie.

+1

Il filo si crea che chiama BeginConnect esce dopo BeginConnect si chiama. Vedete un comportamento diverso se non lasciate che quel thread finisca (le note dei documenti API ci sono problemi se il thread che chiama sock.BeginConnect termina anche se dovrebbe essere un problema su un socket precedentemente connesso) – nos

+0

Sì, c'è una differenza. Come ho detto, anche aggiungere un po 'di sonno dopo aver chiamato Program.BeginConnect fa questo lavoro. Il documento API dichiara: "Se questo socket è stato disconnesso in precedenza, BeginConnect deve essere chiamato su un thread che non verrà chiuso fino al completamento dell'operazione. Si tratta di una limitazione del provider sottostante." Ma come hai detto questo è un nuovo socket e non è stato precedentemente disconnesso ... – Zvika

risposta

0

L'IPEndPoint deve contenere una porta: non sono nemmeno sicuro di come verrà compilato l'endpoint, in quanto è necessario. È possibile specificare la porta come secondo parametro al IPEndAddress o modificare il metodo di BeginConnect come segue:

socket.BeginConnect(address, [port], ConnectCallback, socket); 

... dove [port] rappresenta la porta di ascolto sul server.

+1

non essere sciocco George ... ovviamente il codice così com'è non viene compilato. cosa ti aspetti che incolli qui un vero indirizzo del server ?! Ho pensato che scrivere "qualche indirizzo" fosse un suggerimento abbastanza forte che questo è un posto che devi riempire prima di eseguire questo codice. – Zvika

+0

Hai pensato male!;) –

1

Quale senso utilizzare thread separati e BeginConnect? Se si crea un thread separato (preferibilmente con il pool di thread) perché si utilizza la connessione asincrona (in questo caso il thread thread verrà prelevato dal pool di thread)?

Ci sono diverse opzioni: Usa ThreadPool e Socket.connect

class Program { 

    static void Connect(object o) 
    { 
     IPEndPoint address = (IPEndPoint)o; 
     Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 
     socket.Connect(address); 
     Console.WriteLine("Connected {0}", socket.LocalEndPoint); 
     socket.Send(Encoding.UTF8.GetBytes("Hello")); 
     Console.WriteLine("success"); 
     socket.Close(); 
    } 

    static void Main(string[] args) 
    { 
     IPEndPoint endPoint = new IPEndPoint(IPAddress.Loopback, 5111); 
     ThreadPool.QueueUserWorkItem(Connect, endPoint); 
     Console.ReadKey(); 
    } 
} 

Usa BeginConnect senza thread separato.

class Program { 

static void Main(string[] args) 
{ 
    BeginConnect(new IPEndPoint(IPAddress.Loopback, 5111)); 
    Console.ReadKey(); 
} 

public static void BeginConnect(IPEndPoint address) 
{ 
    try 
    { 
     Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 
     socket.BeginConnect(address, ConnectCallback, socket); 
    } 
    catch (Exception ex) 
    { 
     Console.WriteLine(ex); 
    } 
} 

private static void ConnectCallback(IAsyncResult ar) 
{ 
    Socket sock = (Socket)ar.AsyncState; 
    try 
    { 
     sock.EndConnect(ar); 
     Console.WriteLine("Connected {0}", sock.LocalEndPoint); 
     sock.Send(Encoding.UTF8.GetBytes("Hello")); 
     Console.WriteLine("success"); 
     sock.Close(); 
    } 
    catch (Exception ex) 
    { 
     Console.WriteLine("send ex " + ex); 
     if (sock != null) 
      sock.Close(); 
    } 
} 
} 

Usa BeginConnect con thread separato:

class Program 
{ 

    static void Main(string[] args) 
    { 
     Thread t = new Thread(delegate() 
     { 
      BeginConnect(new IPEndPoint(IPAddress.Loopback, 5111)); 
     }); 
     t.Start(); 
     Console.ReadKey(); 
    } 

    public static void BeginConnect(IPEndPoint address) 
    { 
     try 
     { 
      Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 
      socket.BeginConnect(address, ConnectCallback, socket); 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine(ex); 
     } 
    } 

    private static void ConnectCallback(IAsyncResult ar) 
    { 
     Socket sock = (Socket)ar.AsyncState; 
     try 
     { 
      sock.EndConnect(ar); 
      Console.WriteLine("Connected {0}", sock.LocalEndPoint); 
      sock.Send(Encoding.UTF8.GetBytes("Hello")); 
      Console.WriteLine("success"); 
      sock.Close(); 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine("send ex " + ex); 
      if (sock != null) 
       sock.Close(); 
     } 
    } 
} 
+0

Sono a conoscenza di queste opzioni, ma BeginConnect viene chiamato da un thread separato. Questo è il design dell'applicazione. Sto chiedendo perché in questa situazione ottengo un errore. – Zvika

+0

Il mio secondo esempio funziona bene ... È possibile modificare questo codice per utilizzare invece la classe Thread. –

+0

Ho modificato il mio esempio per utilizzare la classe Thread. E funziona bene. Penso che il problema non sia affatto nella classe Thread. –

0

è possibile che perché non è in attesa per il thread iniziale, il sistema operativo sta annullando la richiesta di I/O? Windows annullerà la richiesta I/O se il thread originale che ha avviato l'I/O async muore.

In questo caso, si chiama BeginConnect da un thread e si lascia morire il thread, quindi l'I/O viene annullato. Ora, potrebbe esserci qualche situazione in cui l'I/O potrebbe non essere cancellato se il thread che hai avviato non è effettivamente morto dal momento in cui hai chiamato Send() sul socket.

Se davvero si vuole far funzionare tutto questo, si potrebbe provare la seguente variazione:

 static void Main(string[] args) 
    { 
     Thread t = new Thread(delegate() 
     { 
      IAsyncResult ar = BeginConnect(new IPEndPoint("some address")); 
      // wait for the async connect to finish. 
      ar.WaitOne(); 
     }); 
     t.Start(); 

     Console.ReadKey(); 
    } 

    public static IAsyncResult BeginConnect(IPEndPoint address) 
    { 
     try 
     { 
      Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 
      return socket.BeginConnect(address, ConnectCallback, socket); 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine(ex); 
     } 
     return null; 
    }