我正在尝试实现一个C#应用程序,该应用程序创建了多个异步客户端套接字,这些套接字连接到不同的IP。我已经使用Microsoft的example连接到以太网到串行网关并发送/接收命令。但是我对C#还是很陌生,我不禁认为必须有一种更加结构化的方法来完成我需要做的事情:
我主要担心的是,除非收到响应,否则异步套接字不会返回,我可以通过在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");
}
}
}
}
我意识到上面的代码可能完全没用,我正在寻找有关如何改善结构和功能的建议。