多个异步客户端套接字

时间:2019-07-04 07:04:33

标签: c# sockets asynchronous

我正在尝试实现一个C#应用程序,该应用程序创建了多个异步客户端套接字,这些套接字连接到不同的IP。我已经使用Microsoft的example连接到以太网到串行网关并发送/接收命令。但是我对C#还是很陌生,我不禁认为必须有一种更加结构化的方法来完成我需要做的事情:

  1. 创建一个代表远程设备的对象(ESGateway)
  2. 连接,发送和接收数据的实现方法
  3. 创建此类对象的数组,并每隔n秒使用它们查询连接的设备。如果设备在超时后没有响应,请转到发送下一个查询。可以独立于其余对象查询每个对象(可能的解决方案可能是在每个设备的单独线程上使用同步套接字)。
  4. 从设备提取的数据需要发送到数据库

我主要担心的是,除非收到响应,否则异步套接字不会返回,我可以通过在ManualResetEvent.WaitOne(timeout)函数中使用Receive()来规避,这会在{{1}中引起异常}功能。

应该处理连接的AsyncSocketClient类:

ReceiveCallback()

以下是我为确认建立连接而进行的测试:

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

我意识到上面的代码可能完全没用,我正在寻找有关如何改善结构和功能的建议。

0 个答案:

没有答案