【问题标题】:Manage TcpClient connection管理 TcpClient 连接
【发布时间】: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()。
  • 关于您的第二个问题,是否所有命令都会生成响应,并且传入的消息是否总是对命令的响应?

标签: c# tcp tcpclient


【解决方案1】:

在你的类中添加一个StreamReader 对象和一个消息队列:

private StreamReader _reader;
private ConcurrentQueue<string> _sentCommands;

如下初始化变量:

_stream = _client.GetStream();
_reader = new StreamReader(_stream, Encoding.ASCII);
_sentCommands = new ConcurrentQueue<string>();

在您的SendMessage 消息中,跟踪您发送的每条消息:

_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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-18
    • 2020-11-26
    • 1970-01-01
    相关资源
    最近更新 更多