本篇继上一篇:Silverlight+WCF
实战-互联网象棋最后篇之对战摄像-上篇[客户端开启录制/注册编号/接收看电视机频](五)

 

 一:对阵摄像 不难原理

略,内容见上篇。

 

二:对战录像 步骤解析:

略,内容见上篇。

 

三:对战录制 具体实施

1:怎么着打开摄像

略,内容见上篇。

 

2:Silverlight怎样使用Socket进行电视发表

2.1:与长途建立链接:

2.2:注册编号[此间的平整是“房间号+棋手颜色值”]

2.3:开新线程,等待接受对方录制

2.4:将录制展现出来,须求用主线程来操作

略,以上内容见上篇。小编:路过孟秋 博客:http://cyq1162.cnblogs.com/ 秋色园http://www.cyqdata.com/

 

3:图片压缩与摄像发送

3.1:图片压缩

XML 1XML 2

大家发送的录制,是经过定时器每秒截5张图发送过去的,每分钟将爆发5张图片,因而,图片压缩变的一对一关键。

于是,找一种图片压缩算法,是一种开端:

一起首:是从网上down了个PngEncoder,压缩160*160的截图后,图片大小是40K,看成是4K[因为看字节时是4背后好多0,看少了2个0],喜悦的自家~~~

之所以一开端在地点测试是常规的,上到网上就oh..no了。

40K*5,即每秒要发送200K的数目,那样就也正是把2M/200K带宽给用光了,房东那限制的512K/56K带宽,就更提不上了~~~

说到底:依旧用上了大家普通通用的JpgEncoder,压缩160*160的截图后,图片大小是10K,每秒发生10K*5=50K,56K带宽刚好够用了。

 

由于JpgEncoder为第一方插件,因而其代码就不贴了,上面不难介绍下:

XML 3XML 4

1:JpgEncoder下载后内容为:FJ.Core.dll、JpgEncoder.cs三个文件。

2:JpgEncoder.cs有一静态方法,直接能够博得Stream流:

 public static Stream GetStream(WriteableBitmap bitmap)

3:没了~~~

ps:具体FJ.Core.dll、JpgEncoder.cs五个文件能够从下载源码下找到。

 

3.2 录制发送

为了定时发送录像,大家须要开启定时器:

XML 5XML 6

        System.Windows.Threading.DispatcherTimer timer;//全局定义
         public MainPage()
        {
            InitializeComponent();
            timer = new System.Windows.Threading.DispatcherTimer();
            timer.Interval = TimeSpan.FromSeconds(0.2);//0.2秒一次,每秒5次
            timer.Tick += new EventHandler(timer_Tick);          
        }
        void timer_Tick(object sender, EventArgs e)
        {
           //那里正是出殡和埋葬录像的代码了
        }
        private void btnSend_Click(object sender, RoutedEventArgs e)
        {
            timer.Start();//点击发送摄像时,运营定时器即可
        }

在点击发送触发定时器时,发送录像

XML 7XML 8

        byte[] content = new byte[56 * 1024];
        int length;       
        void timer_Tick(object sender, EventArgs e)
        {
            WriteableBitmap img = new WriteableBitmap(canVideo, null);
            Stream stream = JpgEncoder.GetStream(img); //获取压缩后的流
            length = (int)stream.Length;
            stream.Read(content, 0, length);
            stream.Close();

            SocketAsyncEventArgs sendEvent = new SocketAsyncEventArgs();
            sendEvent.SetBuffer(content, 0, length);
            videoSocket.SendAsync(send伊芙nt);//这里只管发送,发送后的结果不管了。
           
            img = null;
        }

 

于今,客户端的一多重动作就做到了,包含[开辟录制/注册编号/发送录制/接收看TV频],上边到服务端代码上场了。

 

4:控制台服务端Socket中间转播

4.1:额外的处监护人件

第2:服务端要求消除跨域难题,那么些看过:Silverlight+WCF
新手实例 象棋
WCF通信跨域(十五)
–就会领悟Silverlight客户端和通信端不在同一站点下广播发表时,需求化解跨域难题了。

就算如此那里没用WCF,改用Socket格局,一样需求化解跨域难题。

其次:用Socket通信格局,还索要敞开其余的943端口监听。

 

而是那两步,网上都有现成的代码,直接copy就能够了。

步骤如下:

1:新建控制台项目—》起名:TCP瑟维斯

2:新建类文件:PolicyServer.cs,完整代码如下,大伙直接选择就能够了:

XML 9XML 10PolicyServer类与跨域xml文件

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;

namespace TCPService
{
    class PolicyServer
    {
        Socket m_listenerIPv4;
        Socket m_listenerIPv6;
        byte[] m_policy;

        // pass in the path of an XML file containing the socket policy
        public PolicyServer(string policyContents)
        {
            m_policy = Encoding.UTF8.GetBytes(policyContents);

            // Create the Listening Sockets            
            m_listenerIPv4 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            m_listenerIPv4.Bind(new IPEndPoint(IPAddress.Any, 943));
            m_listenerIPv4.Listen(10);
            m_listenerIPv4.BeginAccept(new AsyncCallback(OnConnection), m_listenerIPv4);
            //Console.WriteLine(“Listenting on IPv4 port 943.”);

            if (System.Net.Sockets.Socket.OSSupportsIPv6)
            {
                m_listenerIPv6 = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);
                m_listenerIPv6.Bind(new IPEndPoint(IPAddress.IPv6Any, 943));
                m_listenerIPv6.Listen(10);
                m_listenerIPv6.BeginAccept(new AsyncCallback(OnConnection), m_listenerIPv6);
                //Console.WriteLine(“Listenting on IPv6 port 943.”);
            }
            else
            {
               // Console.WriteLine(“IPv6 is not supported by the system.”);
            }
        }

        // Called when we receive a connection from a client
        public void OnConnection(IAsyncResult res)
        {
            Socket listener = (Socket)res.AsyncState;
            Socket client = null;

            try
            {
                client = listener.EndAccept(res);
            }
            catch (SocketException)
            {
                return;
            }

            // handle this policy request with a PolicyConnection
            PolicyConnection pc = new PolicyConnection(client, m_policy);

            // look for more connections
            listener.BeginAccept(new AsyncCallback(OnConnection), listener);
        }

        public void Close()
        {
            m_listenerIPv4.Close();
            if (m_listenerIPv6 != null)
            {
                m_listenerIPv6.Close();
            }
        }
    }
    class PolicyConnection
    {
        Socket m_connection;

        // buffer to receive the request from the client
        byte[] m_buffer;
        int m_received;

        // the policy to return to the client
        byte[] m_policy;

        // the request that we’re expecting from the client
        static string s_policyRequestString = “<policy-file-request/>”;

        public PolicyConnection(Socket client, byte[] policy)
        {
            m_connection = client;
            m_policy = policy;

            m_buffer = new byte[s_policyRequestString.Length];
            m_received = 0;

            try
            {
                // receive the request from the client
                m_connection.BeginReceive(m_buffer, 0, s_policyRequestString.Length, SocketFlags.None, new AsyncCallback(OnReceive), null);
            }
            catch (SocketException)
            {
                m_connection.Close();
            }
        }

        // Called when we receive data from the client
        private void OnReceive(IAsyncResult res)
        {
            try
            {
                m_received += m_connection.EndReceive(res);

                // if we haven’t gotten enough for a full request yet, receive again
                if (m_received < s_policyRequestString.Length)
                {
                    m_connection.BeginReceive(m_buffer, m_received, s_policyRequestString.Length – m_received, SocketFlags.None, new AsyncCallback(OnReceive), null);
                    return;
                }

                // make sure the request is valid
                string request = System.Text.Encoding.UTF8.GetString(m_buffer, 0, m_received);
                if (StringComparer.InvariantCultureIgnoreCase.Compare(request, s_policyRequestString) != 0)
                {
                    m_connection.Close();
                    return;
                }

                // send the policy
                m_connection.BeginSend(m_policy, 0, m_policy.Length, SocketFlags.None, new AsyncCallback(OnSend), null);
            }
            catch (SocketException)
            {
                m_connection.Close();
            }
        }

        // called after sending the policy to the client; close the connection.
        public void OnSend(IAsyncResult res)
        {
            try
            {
                m_connection.EndSend(res);
            }
            finally
            {
                m_XML,connection.Close();
            }
        }
    }
//跨域用的xml文件,以代码的法门传入。
public static class SocketPolicy
    {
        public const string Policy = @”
            <?xml version=””1.0″” encoding =””utf-8″”?>
            <access-policy>
              <cross-domain-access>
                <policy>
                  <allow-from>
                    <domain uri=””*”” />
                  </allow-from>
                  <grant-to>
                    <socket-resource port=””4502-4530″” protocol=””tcp”” />
                  </grant-to>
                </policy>
              </cross-domain-access>
            </access-policy>
            “;
    }
}

3:控制台运维首行代码

 static void Main(string[] args)
 {
    PolicyServer ps = new PolicyServer(SocketPolicy.Policy);//Silverlight跨域访问与开启943端口
  }

 

迄今,大家添加了个附加的拍卖类来消除943端口和跨域难点[瞩目下边代码中xml的端口号配置范围哦],下边早先和气的服务端处理流程

 

4.2:服务端处理流程

4.2.1:开启监听

XML 11XML 12

namespace TCPService
{
    class Program
    {
        public static Dictionary<int, ThreadProxy> soketList;//房号+颜色值
         static void Main(string[] args)
        {
            PolicyServer ps = new PolicyServer(SocketPolicy.Policy);//Silverlight跨域访问及943端口
            //主线程监听
            soketList = new Dictionary<int, ThreadProxy>();
            Console.WriteLine(“TCPService正在运维运作”);
            IPEndPoint ip = new IPEndPoint(IPAddress.Any, 4505);//本地任意IP及4505端口
            Socket mainSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            mainSocket.Bind(ip);
            mainSocket.Listen(-1);
            while (true)
            {
                Socket socket = mainSocket.Accept();
                new ThreadProxy(socket).Run();//收到音讯即时处理。

            }
        }
        public static void WriteLine(string msg)
        {
            Console.WriteLine(msg);
        }
    }
    class ThreadProxy
    {
        public Socket socket;
        public ThreadProxy(Socket newSocket)
        {
            socket = newSocket;
        }
        public void Run()
        {
            Thread thread = new Thread(new ThreadStart(Action));
            thread.Start();
        }
        public void Action()
        {
            Program.WriteLine(“有人来了—-“);
            //上边开启处理逻辑
        }
   }
}

 

说明:

这里要留意的是监听的端口号必必要跨域文件配置的限定内。同时用一字典泛型soketList保存了于是注册的用户通信socket,那样能够一本万利寻找对方的socket进行中间转播。

 

4.2.2 定义下全局变量

XML 13XML 14

        public Socket socket;//我方的Socket
        ThreadProxy youThreadProxy;//对方

       
int num;//注册的号子

        byte[] buffer = new byte[30 * 1024];//缓冲字节30K,简单说就是用户10K殡葬叁次,那里吸收满30K才转载叁遍
        bool firstConn = true;//是或不是第①遍建立链接,第①遍链接都以注册编号,不发送录像的;

 

4.2.3 处理编号注册、移除、查找对方

号码注册:

XML 15XML 16

        private void RegSocket(string key)
        {
            firstConn = false;//注册完后,设置下标识
            if (key.Length < 10)//字节太多就是图表流了
            {
                if (int.TryParse(key, out num))
                {
                    if (Program.soketList.ContainsKey(num))//以前都有人在了
                       {
                        Program.soketList[num].socket.Close();
                        Program.soketList[num].socket.Dispose();
                        Program.soketList.Remove(num);
                    }
                    Program.soketList.Add(num, this);
                    Program.WriteLine(“用户注册:” + key);
                    FindYouSocket();
                    return;
                }
            }
        }

线程错误,编号移除:

XML 17XML 18

       private void OnError(ThreadProxy errorProxy,string errorMsg)
        {
            if (errorProxy.socket != null)
            {
                errorProxy.socket.Close();
            }
            Console.WriteLine(“删除用户:” + errorProxy.num +”错误消息:”+ errorMsg);
            Program.soketList.Remove(errorProxy.num);
            
        }

询问对方:

XML 19XML 20

       private void FindYouSocket()
       {
            int youNum = num % 2 == 0 ? num – 1 : num + 1;
            if (Program.soketList.ContainsKey(youNum))
            {
                youThreadProxy = Program.soketList[youNum];
            }
         }

 

4.2.4 主业务处理中间转播流程

XML 21XML 22

       public ThreadProxy(Socket newSocket)
        {
            socket = newSocket;
            socket.SendBufferSize = buffer.Length;
            socket.ReceiveBufferSize = buffer.Length;
        }
        public void Run()
        {
            Thread thread = new Thread(new ThreadStart(Action));
            thread.Start();
        }
        public void Action()
        {
            Program.WriteLine(“有人来了—-“);
            try
            {
                while (true)
                {
                    if (socket.Connected)
                    {
                        int length = 0, count = 0;
                        do
                        {
                            System.Threading.Thread.Sleep(20);//关键点,请求太快数据接收不全
                            length = socket.Receive(buffer, count, socket.Available, 0);
                            count = count + length;

                        }
                        while (socket.Available > 0);

                        if (count > 1)
                        {

                            if (count < 4)//小字节,命令字符
                            {
                                if (firstConn)//第3遍登陆,必要注册ID
                                {
                                    string key = ASCIIEncoding.ASCII.GetString(buffer, 0, count);
                                    RegSocket(key);
                                }
                            }
                            else if (youThreadProxy == null)
                            {
                                Program.WriteLine(“没人接收。。。”);
                                FindYouSocket();
                            }
                            else if (youThreadProxy.canReceive)//对方允许收取图片发送
                                {
                                Program.WriteLine(“图片来了:” + count);
                                if (youThreadProxy.socket.Connected)
                                {
                                    Program.WriteLine(“图片转载:” + buffer.Length);
                                    try
                                    {
                                        youThreadProxy.socket.Send(buffer, count, 0);
                                    }
                                    catch(Exception err)
                                    {
                                        OnError(youThreadProxy, err.Message);
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        OnError(this,”socket链接已关门”);
                        break;
                    }
                }
            }
            catch(Exception err)
            {
                OnError(this,err.Message);
            }

        }

拍卖流程也很不难,依据请求的字节大小来调用是“注册”依旧“中间转播”。

由来,整个完整的摄像传输篇完毕了,达成的图纸和上一节一样了:

XML 23

 

 

末尾是我们期待已久的演示源码下载:点击下载 [别忘了留下言推荐下啊^-^]

声明:摄像源码中的内容会多一些,包含一发端自小编写的一对别样杂七杂八的代码,可是不影响总体的运维。

 

说到底:多谢大家对本体系的欣赏,谢谢援助~

PS:典故点一下推荐会有十个园豆,喜欢麻烦点一下“推荐”,thank you very
much!!

 

相关文章

网站地图xml地图