Я пытаюсь реализовать приложение C #, которое создает несколько асинхронных клиентских сокетов, которые подключаются к различным IP-адресам. Я использовал пример Microsoft для подключения к шлюзу Ethernet-Serial и отправки / получения команд. Однако я очень плохо знаком с C # и не могу не думать, что должен быть более структурированный метод выполнения того, что мне нужно:
- Создать объект, представляющий удаленное устройство (ESGateway)
- Реализация методов подключения, отправки и получения данных
- Создайте массив таких объектов и используйте их для запроса подключенных устройств каждые n секунд. Если устройство не отвечает после истечения времени ожидания, перейдите к отправке следующего запроса. Каждый объект может запрашиваться независимо от остальных (возможное решение может заключаться в использовании синхронных сокетов в отдельных потоках для каждого устройства).
- Данные, извлеченные из устройств, должны быть отправлены в базу данных
Моя основная проблема заключается в том, что асинхронный сокет не возвращает, пока не получен ответ, который я могу обойти, используя 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();
}
}
}
Я понимаю, что приведенный выше код может быть совершенно бесполезным, и я ищу рекомендации по улучшению структуры и функциональности.