2013-01-10 22 views
5

Ho scritto questo codice per il mio server:Come utilizzare il client/listener TCP in C# multithread?

using System; 
    using System.Collections.Generic; 
    using System.Linq; 
    using System.Text; 
    using System.Threading.Tasks; 
    using System.Net; 
    using System.Threading; 
    using System.Net.Sockets; 
    using System.IO; 


namespace ConsoleApplication1 
{ 
    class Program 
    { 
     private static bool terminate; 

     public static bool Terminate 
     { 
      get { return terminate; } 
     } 

     private static int clientNumber = 0; 
     private static TcpListener tcpListener; 

     static void Main(string[] args) 
     { 
      StartServer(); 
      Console.Read(); 
     } 

     private static void StartServer() 
     { 
      try 
      { 
       Console.WriteLine("Server starting..."); 
       tcpListener = new TcpListener(IPAddress.Parse("127.0.0.1"), 8000); 
       terminate = false; 
       tcpListener.Start(); 
       tcpListener.BeginAcceptTcpClient(ConnectionHandler, null); 
       Console.ReadLine(); 

      } 
      catch (Exception ex) 
      { 
       Console.WriteLine(ex.Message); 
      } 
      finally 
      { 
       Console.WriteLine("Server stopping..."); 
       terminate = true; 
       if (tcpListener != null) 
       { 
        tcpListener.Stop(); 
       } 
      } 
     } 

     private static void ConnectionHandler(IAsyncResult result) 
     { 
      TcpClient client = null; 

      try 
      { 
       client = tcpListener.EndAcceptTcpClient(result); 
      } 
      catch (Exception) 
      { 
       return; 
      } 

      tcpListener.BeginAcceptTcpClient(ConnectionHandler, null); 

      if (client!=null) 
      { 
       Interlocked.Increment(ref clientNumber); 
       string clientName = clientNumber.ToString(); 
       new ClientHandler(client, clientName); 
      } 
     } 
    } 
} 


    class ClientHandler 
    { 
     private TcpClient client; 
     private string ID; 

     internal ClientHandler(TcpClient client, string ID) 
     { 
      this.client = client; 
      this.ID = ID; 
      Thread thread = new Thread(ProcessConnection); 
      thread.IsBackground = true; 
      thread.Start(); 
     } 

     private void ProcessConnection() 
     { 
      using (client) 
      { 
       using (BinaryReader reader=new BinaryReader(client.GetStream())) 
       { 
        if (reader.ReadString()==Responses.RequestConnect) 
        { 
          using (BinaryWriter writer=new BinaryWriter(client.GetStream())) 
          { 
           writer.Write(Responses.AcknowledgeOK); 
           Console.WriteLine("Client: "+ID); 
           string message = string.Empty; 
           while (message!=Responses.Disconnect) 
           { 
            try 
            { 
             message = reader.ReadString(); 
            } 
            catch 
            { 
             continue; 
            } 
            if (message==Responses.RequestData) 
            { 
             writer.Write("Data Command Received"); 
            } 
            else if (message==Responses.Disconnect) 
            { 
             Console.WriteLine("Client disconnected: "+ID); 
            } 
            else 
            { 
             Console.WriteLine("Unknown Command"); 
            } 
           } 
          } 
        } 
        else 
        { 
         Console.WriteLine("Unable to connect client: "+ID); 
        } 
       } 
      } 
     } 
    } 

    class Responses 
    { 
     public const string AcknowledgeOK = "OK"; 
     public const string AcknowledgeCancel = "Cancel"; 
     public const string Disconnect = "Bye"; 
     public const string RequestConnect = "Hello"; 
     public const string RequestData = "Data"; 
    } 

questo codice ascolto per le richieste dei client in modo multi-threaded. Non riesco a capire come posso distinguere tra diversi client collegati al mio server e quale client sta disconnettendo e richiedendo comandi diversi.

il mio codice cliente è:

private static void clietnRequest(string message,ref string response) 
{ 
    using (TcpClient client = new TcpClient()) 
    { 
     if (!client.Connected) 
     { 
      client.Connect(IPAddress.Parse("127.0.0.1"), 8000); 
      using (NetworkStream networkStream = client.GetStream()) 
      { 
       using (BinaryWriter writer = new BinaryWriter(networkStream)) 
       { 
        writer.Write(Responses.RequestConnect); 
        using (BinaryReader reader = new BinaryReader(networkStream)) 
        { 
         if (reader.ReadString() == Responses.AcknowledgeOK) 
         { 
          response = Responses.AcknowledgeOK; 

         } 
        } 
       } 
      } 
     } 
    } 
} 

questo pezzo di codice connette il client al server, ma non sono in grado di inviare più messaggi. Voglio nella mia app se il client è connesso, quindi può inviare comandi al server. invece di farlo, ogni volta si comporta come un nuovo client sul server. Mi manca qualcosa qui, gentilmente guidami nella giusta direzione. Sono totalmente nuovo alla programmazione di networking C#. Gentilmente aiutami a migliorare il mio codice. Listener Tcp e client Tcp è valido per questo scenario o devo usare Socket?

+0

Non dovresti solo invertire le eccezioni con un 'return', non avresti alcuna idea se qualcosa è andato storto, almeno inserire una qualche forma di registrazione. –

+0

è solo per scopi dimostrativi, – user1941098

+0

È ancora una cattiva abitudine che è buono per rompere presto. In una nota a parte, perché stai usando un 'ref' invece di passare la risposta come valore di ritorno? –

risposta

3

Si sta chiudendo la connessione ogni volta che il lato client dopo aver inviato un messaggio, se si vuole farlo non c'è niente di sbagliato con esso, ma sarà necessario inviare qualche forma di identificazione al server in modo che possa dire che questo non è una nuova connessione ma una vecchia connessione che si connette per una seconda volta. Questo è ESATTAMENTE quello che sta facendo il protocollo HTTP e che "identificazione" sono i cookie di internet.

Questa prima opzione va bene se si trasmettono dati molto di rado, ma se lo si fa più spesso è necessario mantenere la connessione aperta.

In linea di principio è necessario prendere atto della connessione e disconnessione dalla funzione di richiesta del client e passare la connessione aperta come argomento.

private void MainCode() 
{ 
    using (TcpClient client = new TcpClient()) 
    { 
     client.Connect(IPAddress.Parse("127.0.0.1"), 8000); 

     while(variableThatRepresentsRunning) 
     { 
      //(Snip logic that gererates the message) 

      clietnRequest(message, ref response, client); 

      //(Snip more logic) 
     } 
    } 
} 

private static void clietnRequest(string message,ref string response, TcpClient client) 
{ 
    if (client.Connected) 
    { 
     using (NetworkStream networkStream = client.GetStream()) 
     { 
      using (BinaryWriter writer = new BinaryWriter(networkStream)) 
      { 
       writer.Write(Responses.RequestConnect); 
       using (BinaryReader reader = new BinaryReader(networkStream)) 
       { 
        if (reader.ReadString() == Responses.AcknowledgeOK) 
        { 
         response = Responses.AcknowledgeOK; 

        } 
       } 
      } 
     } 
    } 
    else 
    { 
     //Show some error because the client was not connected 
    } 
} 

Facendo in questo modo il lato server client oggetto rappresenta il cliente, si avrà un'istanza di esso per ogni client connesso e rimarrà associata a quel cliente finché non si disconnette. Se si desidera tenere traccia di tutti i client collegati, è necessario inserirli tutti in una raccolta come List<TcpClient> (utilizzare uno Concurrent Collection o utilizzare il blocco perché si è multithreading) e quindi si avrà un elenco di tutti i client (sarà necessario fare in modo che i client si ripuliscano da soli in modo che si rimuovano dall'elenco dopo una disconnessione).

+0

mi chiedo, da dove passare il cliente parametro al metodo clientRequest? – user1941098

+0

Suppongo che 'clientRequest' venga chiamato dall'interno di una qualche forma di ciclo. Dovresti creare la connessione prima del ciclo e chiuderla dopo la fine del ciclo. –