C# 管理TCP客户端连接
我有一个设备(步进电机)通过C# 管理TCP客户端连接,c#,tcp,tcpclient,C#,Tcp,Tcpclient,我有一个设备(步进电机)通过TCP连接到我的电脑。对发送到设备的消息的响应可在数秒后返回(通常为3到15秒)。 为了与设备通信,经过大量阅读,我想出了以下类: public class TcpUtil : IDisposable { public event Action<TcpResponse> OnTcpMessage; private int _port; private string _ip; private TcpClient _clie
TCP
连接到我的电脑。对发送到设备的消息的响应可在数秒后返回(通常为3到15秒)。为了与设备通信,经过大量阅读,我想出了以下
类
:
public class TcpUtil : IDisposable
{
public event Action<TcpResponse> OnTcpMessage;
private int _port;
private string _ip;
private TcpClient _client;
private NetworkStream _stream;
public TcpUtil(string ip, int port)
{
_ip = ip;
_port = port;
_client = new TcpClient();
//connect with timeout
var connectResult = _client.BeginConnect(ip, port, null, null);
var success = connectResult.AsyncWaitHandle.WaitOne(3000);
if (!success)
{
throw new Exception("Connection timeout at " + ip);
}
// _stream is used for the duration of the object and cannot be used in a using block
_stream = _client.GetStream();
//start listening to incoming messages
Task.Factory.StartNew(() => Listen(_client));
}
private void Listen(TcpClient clientToUse)
{
while (clientToUse.Connected)
{
try
{
byte[] bytes = new byte[1024];
int bytesRead = _stream.Read(bytes, 0, bytes.Length);
string response = Encoding.ASCII.GetString(bytes, 0, bytesRead)
.Replace(Environment.NewLine, "");
if (OnTcpMessage != null && !string.IsNullOrWhiteSpace(response))
{
var message = new TcpResponse(response, _ip);
OnTcpMessage(message);
}
}
catch (Exception ex)
{
if (_client.Connected)
{
Debug.WriteLine("Listener error: " + ex.Message);
throw;
}
}
}
}
public void SendCommand(string command)
{
//device requirement - add a newline at the end of a message
if (!command.EndsWith(Environment.NewLine))
command = command + Environment.NewLine;
byte[] msg = Encoding.ASCII.GetBytes(command);
_stream.Write(msg, 0, msg.Length);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (_client != null)
{
_stream.Dispose();
_client.Close();
}
}
}
public void Dispose()
{
Dispose(true);
}
}
公共类TcpUtil:IDisposable
{
公共事件行动信息;
私人国际港口;
私有字符串ip;
私人TCP客户(u客户);
专用网络流;
公共TcpUtil(字符串ip,int端口)
{
_ip=ip;
_端口=端口;
_client=新的TcpClient();
//连接超时
var connectResult=_client.BeginConnect(ip、端口、null、null);
var success=connectResult.AsyncWaitHandle.WaitOne(3000);
如果(!成功)
{
抛出新异常(“连接超时在”+ip);
}
//_流在对象的持续时间内使用,不能在使用块中使用
_stream=_client.GetStream();
//开始收听传入的消息
Task.Factory.StartNew(()=>Listen(_client));
}
私有void侦听(TcpClient客户端)
{
while(clientouse.Connected)
{
尝试
{
字节[]字节=新字节[1024];
int bytesRead=_stream.Read(字节,0,字节.长度);
字符串响应=Encoding.ASCII.GetString(字节,0,字节读取)
.替换(Environment.NewLine,“”);
if(OnTcpMessage!=null&&!string.IsNullOrWhiteSpace(响应))
{
var消息=新的TCPREponse(响应,_ip);
OnTcpMessage(消息);
}
}
捕获(例外情况除外)
{
如果(_client.Connected)
{
Debug.WriteLine(“侦听器错误:+ex.Message”);
投掷;
}
}
}
}
公共void SendCommand(字符串命令)
{
//设备要求-在消息末尾添加换行符
if(!command.EndsWith(Environment.NewLine))
command=command+Environment.NewLine;
byte[]msg=Encoding.ASCII.GetBytes(命令);
_stream.Write(msg,0,msg.Length);
}
受保护的虚拟void Dispose(bool disposing)
{
如果(处置)
{
如果(_client!=null)
{
_stream.Dispose();
_client.Close();
}
}
}
公共空间处置()
{
处置(真实);
}
}
我对上述代码有两个问题:
- 有时结果是串联的,而不是逐个读取(这确实不一致)
- 我找不到可靠的方法将发送的消息与其响应关联起来
如何改进上述代码以解决这些问题?向类中添加一个
StreamReader
对象和一个消息队列:
private StreamReader _reader;
private ConcurrentQueue<string> _sentCommands;
然后,在接收消息时,退出队列,您将拥有一个命令/响应对:
try
{
var response = _reader.ReadLine();
string command;
var result = _sentCommands.TryDequeue(out command);
if (OnTcpMessage != null && !string.IsNullOrWhiteSpace(response))
{
// Do something here to take advantage of the command variable
}
}
//... Rest of code here
在像您这样的实时系统中,添加3秒等待将导致问题。任何超时都应异步处理。您需要创建网络层以使代码正常工作。顶层应用层和底层TCP传输层。对于步进电机,如果电机速度明显低于所需频率,您可能希望在不等待响应的情况下发送多个步骤。通常这是在启动电机时完成的。请查看以下网页。代码使用套接字,但是套接字可以替换为继承套接字的任何类,如tcp。3秒等待是创建连接超时的唯一方法。在尝试连接时,它只发生一次。字符串的分隔方式是什么?通常,NetworkStream(和TCP)不能保证在完成
Read()
调用后,会收到完整的消息,或者只收到一条消息。如果您的响应始终以换行符分隔,请使用reader类包装您的流,然后执行ReadLine()
。关于第二个问题,是否所有命令都会生成响应,并且传入的消息是否始终是对命令的响应?
_sentCommands.Enqueue(command);
var msg = Encoding.ASCII.GetBytes(command);
_stream.Write(msg, 0, msg.Length);
try
{
var response = _reader.ReadLine();
string command;
var result = _sentCommands.TryDequeue(out command);
if (OnTcpMessage != null && !string.IsNullOrWhiteSpace(response))
{
// Do something here to take advantage of the command variable
}
}
//... Rest of code here