Несколько асинхронных клиентских сокетов - PullRequest
0 голосов
/ 04 июля 2019

Я пытаюсь реализовать приложение C #, которое создает несколько асинхронных клиентских сокетов, которые подключаются к различным IP-адресам. Я использовал пример Microsoft для подключения к шлюзу Ethernet-Serial и отправки / получения команд. Однако я очень плохо знаком с C # и не могу не думать, что должен быть более структурированный метод выполнения того, что мне нужно:

  1. Создать объект, представляющий удаленное устройство (ESGateway)
  2. Реализация методов подключения, отправки и получения данных
  3. Создайте массив таких объектов и используйте их для запроса подключенных устройств каждые n секунд. Если устройство не отвечает после истечения времени ожидания, перейдите к отправке следующего запроса. Каждый объект может запрашиваться независимо от остальных (возможное решение может заключаться в использовании синхронных сокетов в отдельных потоках для каждого устройства).
  4. Данные, извлеченные из устройств, должны быть отправлены в базу данных

Моя основная проблема заключается в том, что асинхронный сокет не возвращает, пока не получен ответ, который я могу обойти, используя ManualResetEvent.WaitOne(timeout) в функции Receive(), что вызывает исключение в функции ReceiveCallback().

Класс AsyncSocketClient, который должен обрабатывать соединение:

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using NLog;

namespace PumpComm
{
    public class StateObject
    {
        // Client socket
        public Socket workSocket = null;

        // Size of receive buffer
        public const int BufferSize = 256;

        // Receive buffer
        public byte[] buffer = new byte[BufferSize];

        // Received data string
        public StringBuilder sb = new StringBuilder();
    }

    public class SocketEventArgs : EventArgs
    {
        public String remoteResponse { get; set; }
    }

    public class AsyncSocketClient
    {
        public int RemotePort { get; }
        public IPAddress RemoteIpAddress { get; }
        private Socket _client = null;
        private EndPoint _endPoint = null;
        private static Logger logger = LogManager.GetCurrentClassLogger();
        public event EventHandler<SocketEventArgs> ResponseReceived;

        private ManualResetEvent connectDone = new ManualResetEvent(false);
        private ManualResetEvent sendDone = new ManualResetEvent(false);
        private ManualResetEvent receiveDone = new ManualResetEvent(false);

        private static String response = String.Empty;

        public AsyncSocketClient(IPAddress remoteIpAddress, int remotePort)
        {
            logger.Info("AsyncSocketClient constructor.");
            this.RemoteIpAddress = remoteIpAddress;
            this.RemotePort = remotePort;
            _endPoint = new IPEndPoint(remoteIpAddress, remotePort);

            try
            {
                _client = new Socket(remoteIpAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
            }
            catch (SocketException se)
            {
                logger.Info("Fault in AsyncSocketClient constructor.");
                logger.Error(se.ToString());
            }
        }

        protected virtual void OnResponseReceived(String response)
        {
            ResponseReceived?.Invoke(this, new SocketEventArgs() {remoteResponse = response});
        }

        public bool StartClient()
        {
            // Open the socket and return true/false
            if (_client != null)
            {
                try
                {
                    _client.BeginConnect(_endPoint, new AsyncCallback(ConnectCallback), _client);
                    return (connectDone.WaitOne(5000));
                }
                catch (Exception e)
                {
                    logger.Error("Error when trying to connect to remote host.");
                    return false;
                }
            }
            else
            {
                logger.Error("Client socket is not initialized!");
                return false;
            }
        }

        public bool SendData(String data)
        {
            byte[] byteData = Encoding.ASCII.GetBytes(data);

            if (_client.Connected)
            {
                var res = _client.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), _client);

                if (sendDone.WaitOne(10000))
                {
                    // Wait for response
                    return (Receive(_client));
                }
                else
                {
                    return false;
                }
            }
            else
            {
                return false;
            }
        }

        private void SendCallback(IAsyncResult ar)
        {
            try
            {
                Socket client = (Socket) ar.AsyncState;

                int bytesSent = client.EndSend(ar);

                logger.Info($"Bytes transmitted: {bytesSent}");

                sendDone.Set();
            }
            catch (Exception e)
            {
                logger.Error("Could not send data: {0}", e.ToString());
            }
        }


        public void StopClient()
        {
            if (!_client.Connected) return;
            _client.Shutdown(SocketShutdown.Both);
            _client.Close();
        }

        private void ConnectCallback(IAsyncResult ar)
        {
            try
            {
                // Retrieve the socket from the state object
                Socket client = (Socket) ar.AsyncState;

                // Complete the connection
                client.EndConnect(ar);
                logger.Info("Socket connected to {0}", client.RemoteEndPoint.ToString());

                // Signal that the connection has been made
                connectDone.Set();
            }
            catch (Exception e)
            {
                logger.Error("Exception occurred when trying to connect.");
            }
        }

        private bool Receive(Socket client)
        {
            try
            {
                StateObject state = new StateObject();
                state.workSocket = client;

                client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback),
                    state);
                return (receiveDone.WaitOne(10000));
            }
            catch (Exception e)
            {
                logger.Error($"Error in Receive: {e}");
                return false;
            }
        }

        private void ReceiveCallback(IAsyncResult ar)
        {
            try
            {
                StateObject state = (StateObject) ar.AsyncState;
                Socket client = state.workSocket;

                int bytesRead = client.EndReceive(ar);
                if (bytesRead > 0)
                {
                    state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));

                    string temp = state.sb.ToString();
                    if (temp.Contains("\r"))
                    {
                        response = state.sb.ToString();
                        int responseLength = response.Length;
                        byte[] responseByteArray = Encoding.ASCII.GetBytes(response);
                        string responseHex = BitConverter.ToString(responseByteArray);
                        logger.Info($"Received {responseLength} bytes: {responseHex}");

                        OnResponseReceived(response);

                        receiveDone.Set();
                    }
                    else
                    {
                        client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                            new AsyncCallback(ReceiveCallback), state);
                    }
                }
                else
                {
                    if (state.sb.Length > 1)
                    {
                        response = state.sb.ToString();
                    }

                    receiveDone.Set();
                }
            }
            catch (Exception e)
            {
                logger.Info("Error in ReceiveCallback - nothing received");
            }
        }
    }
}

Ниже приведен тест, который я выполнил для подтверждения установления соединения:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using NLog;

namespace PumpComm
{
    class Program
    {
        // Event listener
        public static void OnResponseReceived(object source, SocketEventArgs e)
        {
            logger.Info($"Response received: {e.remoteResponse}");

        }

        public static Logger logger = LogManager.GetCurrentClassLogger();

        static void Main(string[] args)
        {
            IPAddress ipAddress = IPAddress.Parse("192.168.0.102");
            int port = 9003;

            var asyncSocket = new AsyncSocketClient(ipAddress, port);

            asyncSocket.ResponseReceived += OnResponseReceived;

            var result = asyncSocket.StartClient();
            logger.Info($"Connection established: {result}");

            asyncSocket.SendData("$P02Kn\r");

            asyncSocket.StopClient();
        }
    }
}

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...