管理TcpClient连接

本文关键字:连接 TcpClient 管理 | 更新日期: 2023-09-27 18:28:50

我有一个设备(步进电机)通过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);
    }
}

我对上面的代码有2个问题:

  • 有时结果被连接起来,而不是一个接一个地读取(这确实不一致)
  • 我找不到一种可靠的方法来将发送的消息与其响应关联起来

我如何改进上面的代码以使其解决这些问题?

管理TcpClient连接

向类中添加一个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