zoukankan      html  css  js  c++  java
  • UDP广播 与 TCP客户端 --服务端

           随着倒计时的响声,自觉无心工作,只想为祖国庆生。

           最近有遇到过这样一个问题,将摄像头识别的行人,车辆实时显示在客户端中。有提供接口,会以Json的数据的形式将实时将识别的对象进行Post提交。所以我们这边先写一个web服务来持续接收数据,再将数据进行解析存入数据库。到这里为止,数据没有问题,都全部存入数据库中,这样还剩下一个实时刷新识别图片的问题。之前的处理方法是每隔5秒左右去读取数据库最新消息,用Timer计时器来解决,这样的话确实能解决问题,但是感觉不是最好的方法,因为摄像头识别的对象有时效性,比如半夜几乎没人通过,这个时候后台还在去捞取最新数据进行判断就比较消耗资源。所以我先想到的是用 TCP方法来处理, web在解析图片的时候, 进行发送,winform客户端进行接收。貌似可以解决问题,先上代码

            private void button1_Click(object sender, EventArgs e)
            {
                //创建TCP客户端对象
                TcpClient tcpClient = new TcpClient();
                //添加目标(服务器)主机的IP、端口号
                tcpClient.Connect(IPAddress.Parse("172.16.0.217"), 8056);
                //网络流 数据的形式
                NetworkStream networkStream = tcpClient.GetStream();
                if (networkStream.CanWrite)
                {
                    //待发送数据转Byte[]
                    Byte[] bytSend = Encoding.UTF8.GetBytes(textBox1.Text);
                    networkStream.Write(bytSend, 0, bytSend.Length);
                }
                else
                {
                    MessageBox.Show("无法写入数据流");
    
                    networkStream.Close();
                    tcpClient.Close();
    
                    return;
                }
                //流创建完需要及时关闭
                networkStream.Close();
                tcpClient.Close();
    
            }  

    这里是TCP的客户端,指定了接收端的IP和端口号,以及发送的数据流,同样在服务端选择接受这些流数据。服务端要持续接收数据,就必须开启一个线程进行监听

            private void Form1_Load(object sender, EventArgs e)  
            {
                //初始化加载数据
                Thread thread = new Thread(new ThreadStart(Listen));
                thread.Start();
            }
    
    
            //线程内向文本框txtRecvMssg中添加字符串及委托
            private delegate void serverRecDeg(string s);
            private void ReceiveMsg(string mes)
            {
                textBox1.Text = "Time:" + DateTime.Now.ToLongTimeString() + "Data:" + mes;
            }
    
            //监听数据
            private void Listen()
            {
                //socket 对象
                Socket sock = new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp);
                sock.Bind(new IPEndPoint(IPAddress.Any, 8056));
    
                //不断监听端口
                while (true)
                {
                    sock.Listen(0);
                    Socket socket = sock.Accept();
                    NetworkStream ntwStream = new NetworkStream(socket);
                    StreamReader strmReader = new StreamReader(ntwStream);
                    //winform UI控件赋值,如果有线程,则需要用Invoke 来赋值,则否报错
                    Invoke(new serverRecDeg(ReceiveMsg),new object[] { strmReader.ReadToEnd() });
                    socket.Close();
                }            
            }

    以上是TCP客户端发送数据,服务端持续接收。刚开始自认没问题,但是后来一想确实不可行。我web服务端是发送数据的,客户端是用来接收数据。TCP是客户端指定服务端唯一的IP 和端口号进行数据的传输,这里明显就冲突了,如果在Web服务端用TCP发送数据,

    就只能有一个客户端进行接收,需求是winform最后要部署到不同机器上,这种思路肯定不行。思来想去,最后在同事的指点下,想到了广播这一思路。在web服务端每次解析到图片的时候,发一个广播,然后隐藏在局域网内的各个客户端就可以进行收听,解析后将图片 

            //定义对象
            private static Socket sock;
            private static IPEndPoint ipendPoint;
            private static byte[] data;
    
            //发送广播
            public static void UdpSend(byte[] bytes1)
            {
                //sock 对象,指定UDP协议
                sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                ipendPoint = new IPEndPoint(IPAddress.Broadcast, 9050);
                //赋值
                data = bytes1;
                sock.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
                //发送
                sock.SendTo(data, ipendPoint);
    
            }

    这里使用的是UDP广播,代码量很少,发送端已经完成,剩下的就是客户端持续接收广播

            public void Receive()
            {
                try
                {
                    //Scok 对象,设置UDP协议
                    Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                    //超时时间
                    sock.ReceiveTimeout = 4000;
                    //广播的端口号指定
                    IPEndPoint iep = new IPEndPoint(IPAddress.Any, 9050);
                    sock.Bind(iep);
                    EndPoint ep = (EndPoint)iep;
                    //数据接收(指定异步接收的方法)
                    sock.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), sock);
    
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
            }
    
            //指定单次接收图片流缓冲区的大小
            static byte[] buffer = new byte[102400];
    
            //异步接收
            public void ReceiveMessage(IAsyncResult ar)
            {
                try
                {
                    var socket = ar.AsyncState as Socket;              
                    var length = socket.EndReceive(ar);
                    //读取出来消息内容
                    //var message = Encoding.UTF8.GetString(buffer, 0, length);               
                    //前三个长度为对象类型 车辆/行人 ,后面识别图片
                    //解析图片类型
                    string objType = System.Text.Encoding.Default.GetString(buffer.Skip(0).Take(3).ToArray());
                    //解析图片
                    Image img = convertImg(buffer.Skip(3).ToArray());                              
                    //显示消息
                    this.Invoke(new Action(() => { SetPicBoxImg(objType, img); }));                                              
                    //接收下一个消息(递归)
                    socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), socket);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
            }

    这些做完,剩下的就是将接收广播的方法写入到后台线程中,在窗体运行的时候启动即可

     Thread td = new Thread(new ThreadStart(Receive));
     td.IsBackground = true;
     td.Start();

    好了,事情到这里基本就结束了,可以看出来,代码量很少,却能解决实时刷新的需求。以后遇到事情一定要思路明确,不然一开始钻进TCP的死胡同里,怎么都出不来,白白浪费时间不说,把人也搞得很疲惫。这里多谢同事的指点,以后多像技术高手学习,毕竟阅历有时候真的就是硬实力。

  • 相关阅读:
    LeetCode: LRU Cache
    LeetCode: Reorder List
    LeetCode: Linked List Cycle I && II
    LeetCode: Word Break I && II
    LeetCode: Single Number I && II
    太坑了,mybatis注解一对多,id没了
    ajax请求参数的格式
    查询结果拼接
    id拼接保存到单个字段后作为表连接的查询条件
    seam的定时轮巡
  • 原文地址:https://www.cnblogs.com/Sientuo/p/9733041.html
Copyright © 2011-2022 走看看