Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/282.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 管理TCP客户端连接_C#_Tcp_Tcpclient - Fatal编程技术网

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