C# TcpClient 通过持久连接读取多条消息 [英] C# TcpClient reading multiple messages over persistent connection

查看:36
本文介绍了C# TcpClient 通过持久连接读取多条消息的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建一个 TCP 服务器 &具有持久连接的客户端,以便服务器和客户端在任何时间点都可以相互通知某些事件"(因此使用推送而不是轮询).

I'm trying to create a TCP Server & Client that will have a persistent connection so that the server and client at any point in time can notify each other of certain 'events'(so push instead of poll).

我几乎一切正常,客户端可以连接,连接保持打开状态,客户端和服务器都可以编写 &从 tcp 流中读取.

I almost have everything working, clients can connect, connection is kept open and client and server can both write & read from the tcp stream.

问题在于读取,我通过首先发送包含消息长度的 8 个字节来定义消息边界.一旦我收到它,就会读取 lenght x 的消息并引发一个事件.

The problem is with the read, I've defined the message boundary by first sending 8 bytes that contain the length of the message. Once I've received it, the messages of lenght x is read and an event is raised.

这一切正常,但是一旦消息被读取,我希望await stream.ReadAsync"等待新的传入数据,但它会一直循环(并返回 0 数据)而不是等待导致 100% 的 CPU 使用率.

This all works fine but once the message has been read I want the "await stream.ReadAsync" to wait for new incoming data, but it keeps looping(and returns 0 data) instead of waiting causing 100% cpu usage.

有没有办法对流说重置",以便它开始像原来一样再次等待.

Is there a way to say 'Reset' to the stream so that it begins to wait again like it did originally.

这是我的tcpclient的代码(用于发送和接收),你可以跳到RunListener方法,其余的我认为不重要.

This is the code for my tcpclient(used for both sending and receiving), you can skip to the RunListener method, I don't think the rest matters.

    public class SSLTcpClient : IDisposable {
        /**
         * Public fields
         */
        public SslStream SslStream { get; private set; }

        /**
         * Events
         */
        public ConnectionHandler connected;
        public ConnectionHandler disconnected;
        public DataTransfer dataReceived;

        /**
         * Constructors
         */
        public SSLTcpClient() { }
        public SSLTcpClient(TcpClient pClient, X509Certificate2 pCert) {
            SslStream = new SslStream(
                pClient.GetStream(),
                false,
                new RemoteCertificateValidationCallback(
                    delegate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) {
                        return true;
                    }
                ),
                new LocalCertificateSelectionCallback(
                    delegate(object sender, string targetHost, X509CertificateCollection localCertificates, X509Certificate remoteCertificate, string[] acceptableIssuers) {
                        return new X509Certificate2(pCert);
                    }
                )
            );

            try {
                SslStream.AuthenticateAsServer(pCert, true, SslProtocols.Tls, true);
            } catch (AuthenticationException) {
                pClient.Close();
                return;
            }

            Thread objThread = new Thread(new ThreadStart(RunListener));
            objThread.Start();

            if (connected != null) {
                connected(this);
            }
        }

        /**
         * Connect the TcpClient
         */
        public bool ConnectAsync(IPAddress pIP, int pPort, string pX509CertificatePath, string pX509CertificatePassword) {
            TcpClient objClient = new TcpClient();
            try {
                if(!objClient.ConnectAsync(pIP, pPort).Wait(1000)) {
                    throw new Exception("Connect failed");
                };
            } catch (Exception) {
                return false;
            }
            X509Certificate2 clientCertificate;
            X509Certificate2Collection clientCertificatecollection = new X509Certificate2Collection();
            try {
                clientCertificate = new X509Certificate2(pX509CertificatePath, pX509CertificatePassword);
                clientCertificatecollection.Add(clientCertificate);
             } catch(CryptographicException) {
                objClient.Close();
                return false;
            }

            SslStream = new SslStream(
                objClient.GetStream(), 
                false, 
                new RemoteCertificateValidationCallback(
                    delegate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { 
                        return true;
                    }
                ), 
                new LocalCertificateSelectionCallback(
                    delegate(object sender, string targetHost, X509CertificateCollection localCertificates, X509Certificate remoteCertificate, string[] acceptableIssuers) {
                        var cert = new X509Certificate2(pX509CertificatePath, pX509CertificatePassword);
                        return cert;
                    }
                )
            );

            try {
                SslStream.AuthenticateAsClient(pIP.ToString(), clientCertificatecollection, SslProtocols.Tls, false);
            } catch (AuthenticationException) {
                objClient.Close();
                return false;
            }

            Thread objThread = new Thread(new ThreadStart(RunListener));
            objThread.Start();

            if (connected != null) {
                connected(this);
            }
            return true;
        }

        /**
         * Reading
         */
        private async void RunListener() {
            try {
                while (true) {
                    byte[] bytes = new byte[8];
                    await SslStream.ReadAsync(bytes, 0, (int)bytes.Length);

                    int bufLenght = BitConverter.ToInt32(bytes, 0);
                    if (bufLenght > 0) {
                        byte[] buffer = new byte[bufLenght];
                        await SslStream.ReadAsync(buffer, 0, bufLenght);

                        if (dataReceived != null) {
                            dataReceived(this, buffer);
                        }
                    }
                }
            } catch (Exception) {
                Dispose();
            }
        }

        /**
         * Writing 
         */
        public bool Send(byte[] pData) {
            try {
                byte[] lenght = BitConverter.GetBytes(pData.Length);
                Array.Resize(ref lenght, 8);

                SslStream.Write(lenght);
                if (!SslStream.WriteAsync(pData, 0, pData.Length).Wait(1000)) {
                    throw new Exception("Send timed out");
                }
            } catch (Exception) {
                Dispose();
                return false;
            }
            return true;
        }

        public bool Send(string pData) {
            byte[] bytes = System.Text.Encoding.UTF8.GetBytes(pData);
            return Send(bytes);
        }

        /**
         * Shutdown
         */
        public void Dispose() {
            SslStream.Close();
            if (disconnected != null) {
                disconnected(this);
            }
        }
    }

推荐答案

你读取 4 或 8 个字节的方式是错误的.你需要循环直到你真正得到它们.你可能会得到 1.

The way you read 4 or 8 bytes is wrong. You need to loop until you actually got them. You might get 1.

您在这里和其他地方假设您将读取您想要的数量.您将至少读取一个字节,如果连接被远程端关闭,则读取为零.

You are assuming here and in other places that you will read the amount that you wanted. You will read at least one byte, or zero if the connection was shut down by the remote side.

也许,您应该使用 BinaryReader 来抽象掉循环.

Probably, you should use BinaryReader to abstract away the looping.

另外,你需要清理资源.为什么不将它们包装在 using 中?所有 Close 调用都是不安全的,不需要.

Also, you need to clean up resources. Why aren't you wrapping them in using? All the Close calls are unsafe and not needed.

我也不明白为什么这里需要控制流的例外.重构它.

Also I don't see why exceptions for control flow would be necessary here. Refactor that away.

这篇关于C# TcpClient 通过持久连接读取多条消息的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆