上位机通信组件-2

因为TCP是面向流通信,则会出现几个网络问题。比如断线重连,丢包粘包等情况。

断线重连:

        设备会有自己的心跳检查,一般是通过心跳检查去发现是否已断线。这里不多叙述,简单的写一个自己实现的断线检查和重新连接。

private static void Connect() 
{
    Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    socket.Connect(new IPEndPoint(IPAddress.Parse("192.168.2.1"), 5001));

    // socket.Connected保留最后一次操作时的状态,不是实时的状态 
    Console.WriteLine(socket.Connected);

    //此处最好是通过开线程去执行。这样就能随时检测是否断线
    while (true)
    {
         返回True   :连接正常     Poll:检查连接状态
        bool state = !(!socket.Connected || (socket.Poll(1, SelectMode.SelectRead) && socket.Poll(1, SelectMode.SelectWrite) && (socket.Available == 0)));
        if (state)
        {
            Console.WriteLine("连接正常");
            socket.Shutdown(SocketShutdown.Both);
            socket.Dispose();
        }
        else
        {
            Console.WriteLine("连接断开");
            socket = new Socket(
                   AddressFamily.InterNetwork,
                   SocketType.Stream,
                   ProtocolType.Tcp);

            #region 方法一: 同步连接

            try
            {
                socket.Connect(new IPEndPoint(IPAddress.Parse("192.168.2.1"), port: 5001));
            }
            catch (Exception ex)
            {
                Console.WriteLine("重新连接失败");
                Console.WriteLine(ex.Message);
            }

            #endregion

            #region 方法二:异步执行

            var result = socket.BeginConnect(new IPEndPoint(IPAddress.Parse("192.168.2.1"), 5001), new AsyncCallback(r =>
            {
                Console.WriteLine(r.AsyncState);
                if (!r.IsCompleted) 
                {
                    //这里不是关闭连接,而是关闭当前委托,和BeginConnect对应。
                    socket.EndConnect(r);
                }
                Console.WriteLine("回调执行");
            }), null);
            bool s = result.AsyncWaitHandle.WaitOne(1000);
            if (s)// 连接成功后
            {
                Console.WriteLine("重新连接");
            }
            else
            {
                // 连接的时候  异步,在1秒钟时间内   没有连接成功   跳出
                try
                {
                    //socket.EndConnect(result); // 销毁之前的连接请求    卡线程
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
                Console.WriteLine("重连失败");
            }

            #endregion
        }
        //重连  使用    发送业务请求  执行连接判断    
    }
}

丢包情况一般是网络通讯不稳定,或者数据缓冲区读取的字节数量不对造成的。前一种情况可通过心跳检查,断线重连等方案解决。后一种情况可将未读的字节补充上来,这样包依然完整。

粘包情况:设备发送数据太快,导致上一个包的缓冲区还未清空,下一个包的数据就进入了缓冲区,读取缓冲区时把数据包粘连在一起了。这是TCP特有的问题,如下:

Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
server.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9009));
server.Listen();
Task.Run(() =>
{
    Socket c = server.Accept();
    while (true)
    {
        byte[] data = new byte[1024];
        c.Receive(data);
        Console.WriteLine(Encoding.UTF8.GetString(data));
    }
});

Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
client.Connect(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9009));

for (int i = 0; i < 10; i++)
{
    // 发送端
    string msg = "Hello";
    byte[] data = Encoding.UTF8.GetBytes(msg);

    client.Send(data);
}
Console.WriteLine("发送完成");

那怎么解决呢?

方法也很简单,和服务端定义一个协议,比如发送时将包的长度,名称,id等一起传送过来,这样接收时,按照协议去解析。

Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
server.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9009));
server.Listen();
Task.Run(() =>
{
    Socket c = server.Accept();
    while (true)
    {
        byte[] data = new byte[2];
        c.Receive(data);// 接收数据字节长度
        short len = BitConverter.ToInt16(data, 0);

        //根据字节长度去获取正文内容
        data = new byte[len];
        c.Receive(data);// 接收实际数据
        Console.WriteLine(Encoding.UTF8.GetString(data));
    }
});

Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
client.Connect(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9009));

for (int i = 0; i < 10; i++)
{
    // 发送端
    string msg = "Hello";
    byte[] data = Encoding.UTF8.GetBytes(msg);

    List<byte> bytes = new List<byte>();
    short len = (short)data.Length;// 正文字节长度
    bytes.AddRange(BitConverter.GetBytes(len));// 数据字节长度
    bytes.AddRange(data);// 数据有效字节

    client.Send(bytes.ToArray());
}
Console.WriteLine("发送完成");

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值