2010-04-16 5 views
0

J'essaye d'écrire un service qui écoute TCP Socket sur un port donné jusqu'à ce qu'une fin de ligne soit recived et puis basé sur la "ligne" qui a été reçue exécute une commande.Lecture d'une socket jusqu'à la fin de la ligne C#?

J'ai suivi un tutoriel de programmation socket de base pour C# et je suis venu avec le code ci-dessous pour écouter une prise:

public void StartListening() 
     { 
      _log.Debug("Creating Maing TCP Listen Socket"); 
      _mainSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 

      IPEndPoint ipLocal = new IPEndPoint(IPAddress.Any, _port); 
      _log.Debug("Binding to local IP Address"); 
      _mainSocket.Bind(ipLocal); 

      _log.DebugFormat("Listening to port {0}",_port); 
      _mainSocket.Listen(10); 

      _log.Debug("Creating Asynchronous callback for client connections"); 
      _mainSocket.BeginAccept(new AsyncCallback(OnClientConnect), null); 

     } 

     public void OnClientConnect(IAsyncResult asyn) 
     { 
      try 
      { 
       _log.Debug("OnClientConnect Creating worker socket"); 
       Socket workerSocket = _mainSocket.EndAccept(asyn); 
       _log.Debug("Adding worker socket to list"); 
       _workerSockets.Add(workerSocket); 
       _log.Debug("Waiting For Data"); 
       WaitForData(workerSocket); 

       _log.DebugFormat("Clients Connected [{0}]", _workerSockets.Count); 

       _mainSocket.BeginAccept(new AsyncCallback(OnClientConnect), null); 
      } 
      catch (ObjectDisposedException) 
      { 
       _log.Error("OnClientConnection: Socket has been closed\n"); 
      } 
      catch (SocketException se) 
      { 
       _log.Error("Socket Exception", se); 
      } 
     } 

     public class SocketPacket 
     { 
      private System.Net.Sockets.Socket _currentSocket; 

      public System.Net.Sockets.Socket CurrentSocket 
      { 
       get { return _currentSocket; } 
       set { _currentSocket = value; } 
      } 
      private byte[] _dataBuffer = new byte[1]; 

      public byte[] DataBuffer 
      { 
       get { return _dataBuffer; } 
       set { _dataBuffer = value; } 
      } 


     } 

     private void WaitForData(Socket workerSocket) 
     { 
      _log.Debug("Entering WaitForData"); 
      try 
      { 
       lock (this) 
       { 
        if (_workerCallback == null) 
        { 
         _log.Debug("Initializing worker callback to OnDataRecieved"); 
         _workerCallback = new AsyncCallback(OnDataRecieved); 
        } 
       } 
       SocketPacket socketPacket = new SocketPacket(); 
       socketPacket.CurrentSocket = workerSocket; 
       workerSocket.BeginReceive(socketPacket.DataBuffer, 0, socketPacket.DataBuffer.Length, SocketFlags.None, _workerCallback, socketPacket); 
      } 
      catch (SocketException se) 
      { 
       _log.Error("Socket Exception", se); 
      } 
     } 

     public void OnDataRecieved(IAsyncResult asyn) 
     { 
      SocketPacket socketData = (SocketPacket)asyn.AsyncState; 
      try 
      { 

       int iRx = socketData.CurrentSocket.EndReceive(asyn); 
       char[] chars = new char[iRx + 1]; 
       _log.DebugFormat("Created Char array to hold incomming data. [{0}]",iRx+1); 

       System.Text.Decoder decoder = System.Text.Encoding.UTF8.GetDecoder(); 
       int charLength = decoder.GetChars(socketData.DataBuffer, 0, iRx, chars, 0); 
       _log.DebugFormat("Read [{0}] characters",charLength); 

       String data = new String(chars); 
       _log.DebugFormat("Read in String \"{0}\"",data); 

       WaitForData(socketData.CurrentSocket); 
      } 
      catch (ObjectDisposedException) 
      { 
       _log.Error("OnDataReceived: Socket has been closed. Removing Socket"); 
       _workerSockets.Remove(socketData.CurrentSocket); 

      } 
      catch (SocketException se) 
      { 
       _log.Error("SocketException:",se); 
       _workerSockets.Remove(socketData.CurrentSocket); 

      } 
     } 

Ce que je pensais allait être une bonne base pour ce que je voulais faire, mais le code que j'ai ajouté les caractères entrants à une zone de texte un par un et n'a rien fait avec elle. Ce qui ne marche pas vraiment pour ce que je veux faire.

Mon problème principal est le découplage de la méthode OnDataReceived de la méthode Wait for data. ce qui signifie que j'ai des problèmes pour créer une chaîne (j'utiliserais un générateur de chaînes mais je peux accepter plusieurs connexions pour que cela ne fonctionne pas vraiment.)

Idéalement, je voudrais regarder en écoutant une prise jusqu'à ce que je vois et caractère de fin de ligne, puis appeler une méthode avec la chaîne résultante comme paramètre.

Quelle est la meilleure façon de s'y prendre pour le faire.

+1

Je suggère de supprimer le code de journalisation * incroyablement verbeux * dans cette question et les futures. –

Répondre

1

Essayez d'utiliser des prises de aSYNCH. le code ci-dessous l'écoute d'une prise, et si la nouvelle ligne char via telnet est reçue, elle sera renvoyée à la socket correspondante Il semblerait que vous deviez simplement rediriger cette entrée vers votre zone de texte

private string _hostName; 
    private const int _LISTENINGPORT = 23; 
    private Socket _incomingSocket; 
    byte[] _recievedData; 
    //todo: do we need 1024 byte? the asynch methods read the bytes as they come 
    //so when 1 byte typed == 1 byte read. Unless its new line then it is two. 
    private const int _DATASIZE = 1024; 

    public ConnectionServer() 
    { 
     IPAddress localAddr = IPAddress.Parse("127.0.0.1"); 
     _hostName = Dns.GetHostName(); 
     _recievedData = new byte[_DATASIZE]; 
     _incomingSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 
     IPEndPoint endPoint = new IPEndPoint(localAddr, _LISTENINGPORT); 
     _incomingSocket.Bind(endPoint); 
     _incomingSocket.Listen(10); 
    } 
    ~ConnectionServer() 
    { 
    } 
    public void StartListening() 
    { 
     _incomingSocket.BeginAccept(new AsyncCallback(OnAccept), _incomingSocket); 
    } 
    private void OnAccept(IAsyncResult result) 
    { 
     UserConnection connectionInfo = new UserConnection(); 
     Socket acceptedSocket = (Socket)result.AsyncState; 
     connectionInfo.userSocket = acceptedSocket.EndAccept(result); 
     connectionInfo.messageBuffer = new byte[_DATASIZE]; 
     //Begin acynch communication with target socket 
     connectionInfo.userSocket.BeginReceive(connectionInfo.messageBuffer, 0, _DATASIZE, SocketFlags.None, 
      new AsyncCallback(OnReceiveMessage), connectionInfo); 
     //reset the listnening socket to start accepting 
     _incomingSocket.BeginAccept(new AsyncCallback(OnAccept), result.AsyncState); 
    } 
    private void OnReceiveMessage(IAsyncResult result) 
     { 
      UserConnection connectionInfo = (UserConnection)result.AsyncState; 
      int bytesRead = connectionInfo.userSocket.EndReceive(result); 
      if (connectionInfo.messageBuffer[0] != 13 && connectionInfo.messageBuffer[1] != 10) 
      //ascii for newline and line feed 
      //todo dress this up 
      { 
       if (string.IsNullOrEmpty(connectionInfo.message)) 
       { 
        connectionInfo.message = ASCIIEncoding.ASCII.GetString(connectionInfo.messageBuffer); 
       } 
       else 
       { 
        connectionInfo.message += ASCIIEncoding.ASCII.GetString(connectionInfo.messageBuffer); 
       } 
      } 
      else 
      { 
       connectionInfo.userSocket.Send(ASCIIEncoding.ASCII.GetBytes(connectionInfo.message), SocketFlags.None); 
       connectionInfo.userSocket.Send(connectionInfo.messageBuffer, SocketFlags.None); 
       connectionInfo.message = string.Empty; 
       connectionInfo.messageBuffer = new byte[_DATASIZE]; 
      } 


{ 
    public class UserConnection 
    { 
     public Socket userSocket { get; set; } 
     public Byte[] messageBuffer { get; set; } 
     public string message { get; set; } 
    } 
} 
1

Vous semblez avoir plusieurs questions:

Vous avez une méthode asynchrone appelé WaitForData. C'est très déroutant, car les méthodes avec le mot Wait dans leurs noms bloquent généralement le thread en cours d'exécution jusqu'à ce que quelque chose se passe (ou, éventuellement, un délai expire). Cela fait exactement le contraire. Avez-vous l'intention que ce soit une opération synchrone ou asynchrone?

Il n'y a pas non plus besoin d'instancier l'objet Decoder, ni le tableau char pour (semble-t-il) quelque chose; Il suffit d'appeler System.Text.Encoding.UTF8.GetString(socketData.DataBuffer, 0, iRx).

Vous ne semblez pas non plus faire quoi que ce soit avec des lignes ... c'est pourquoi il ne fait rien avec des lignes.

Votre approche avec l'utilisation d'un StringBuilder est ce que je ferais. J'ajoute un StringBuilder à la classe SocketData et l'appelle Builder. Comme vous capturez les données de chaîne, faire quelque chose comme ceci:

string[] data = System.Text.Encoding.UTF8.GetString(
        socketData.DataBuffer, 0, iRx).Split(Environment.NewLine); 

socketData.Builder.Append(data[0]); 

for(int i = 1; i < data.Length; i++) 
{ 
    // the socketData.Builder variable now contains a single line, so do 
    // something with it here, like raise an event 
    OnLineReceived(builder.ToString()); 

    socketData.Builder = new StringBuilder(data[i]); 
} 

L'une mise en garde est que UTF8 est un codage multi-octets, ce qui signifie que vous pourriez potentiellement saisir un morceau de données qui coupe mi-caractère. C'est généralement une meilleure idée de faire ce genre de prétraitement de l'autre côté de la communication, puis d'envoyer les données dans un format de longueur appropriée.

Questions connexes