Считывание сокета до конца строки C #? - PullRequest
0 голосов
/ 16 апреля 2010

Я пытаюсь написать службу, которая прослушивает сокет TCP на заданном порту, пока не будет получен конец строки, а затем на основе полученной "строки" выполнит команду.

Я следовал базовому руководству по программированию сокетов для c # и придумал следующий код для прослушивания сокетов:

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

            }
        }

Я подумал, что это послужит хорошей основой для того, что я хочу сделать, но код, который я добавил поочередно входящим символам в текстовое поле, ничего не сделал с ним. Что на самом деле не работает для того, что я хочу сделать.

Моя главная проблема - это разделение метода OnDataReceived от метода Ожидание данных. это означает, что у меня возникают проблемы при построении строки (я бы использовал построитель строк, но я могу принять несколько соединений, так что это на самом деле не работает.

В идеале я хотел бы смотреть во время прослушивания сокета, пока не увижу символ конца строки, а затем вызвать метод с результирующей строкой в ​​качестве параметра.

Какой лучший способ сделать это.

Ответы [ 2 ]

1 голос
/ 16 апреля 2010

У вас, кажется, есть несколько проблем:

У вас есть асинхронный метод с именем WaitForData. Это очень запутанно, поскольку методы со словом Wait в их именах обычно блокируют текущий выполняющийся поток, пока что-то не произойдет (или, по желанию, не истечет время ожидания). Это делает с точностью до наоборот. Вы планируете, чтобы это была синхронная или асинхронная операция?

Также нет необходимости создавать экземпляр объекта Decoder, и вам не нужен массив char для (кажется) чего-либо; просто позвоните System.Text.Encoding.UTF8.GetString(socketData.DataBuffer, 0, iRx).

Вы также, похоже, ничего не делаете со строками ... вот почему он ничего не делает со строками.

Ваш подход с использованием StringBuilder - это то, что я бы сделал. Я бы добавил StringBuilder к классу SocketData и назвал бы его Builder. Захватив строковые данные, сделайте что-то вроде этого:

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

Единственное предостережение в том, что UTF8 является многобайтовой кодировкой, что означает, что вы потенциально можете получить фрагмент данных, который обрезает средний символ. Как правило, лучше выполнить подобную предварительную обработку на другой стороне сообщения, а затем отправить данные в формате с соответствующим префиксом длины.

1 голос
/ 16 апреля 2010

Попробуйте использовать асинхронные сокеты. Приведенный ниже код будет прослушивать сокет, и если будет получен новый символ строки через telnet, он вернет его обратно в входящий сокет. Похоже, вам просто нужно перенаправить этот ввод в текстовое поле.

    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; }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...