【发布时间】:2016-04-08 03:07:17
【问题描述】:
我有一个设备(步进电机)通过TCP 连接到我的电脑。对发送到设备的消息的响应可能会在几秒钟后返回(通常为 3 到 15 秒)。
为了与设备通信,经过大量阅读,我想出了以下class:
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);
}
}
上面的代码有两个问题:
- 有时结果是串联的,不能一一读取(这真的是不一致)
- 我找不到将已发送消息与其响应相关联的可靠方法
如何改进上述代码以解决这些问题?
【问题讨论】:
-
在像您这样的实时系统中添加 3 秒等待会导致问题。任何超时都应该异步处理。您需要网络层才能使代码正常工作。顶层应用层和底层 TCP 传输层。对于步进电机,如果电机的速度明显偏离所需频率,您可能希望发送多个步骤而无需等待响应。通常这是在启动电机时完成的。
-
看看下面的网页。该代码使用套接字,但套接字可以替换为任何继承套接字的类,如 tcp msdn.microsoft.com/en-us/library/w89fhyex(v=vs.110).aspx
-
3 秒等待是我发现创建连接超时的唯一方法。尝试连接时只会发生一次。
-
你的字符串是如何分隔的?一般来说,NetworkStream(和 TCP)不保证在完成
Read()调用后将接收到完整的消息或仅接收单个消息。如果您的响应始终以换行符分隔,请使用阅读器类包装您的流并改为执行ReadLine()。 -
关于您的第二个问题,是否所有命令都会生成响应,并且传入的消息是否总是对命令的响应?