Silverlight+WCF 实战-网络象棋最终篇之对战视频-下篇[客户端发送与服务端中转](六)...

    xiaoxiao2022-07-09  72

    本篇继上一篇: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:图片压缩

    我们发送的视频,是通过定时器每秒截5张图发送过去的,每秒钟将产生5张图片,因此,图片压缩变的相当重要。 因此,找一种图片压缩算法,是一种开始: 一开始:是从网上down了个PngEncoder,压缩160*160的截图后,图片大小是40K,看成是4K[因为看字节时是4后面好多0,看少了一个0],兴奋的我~~~ 因此一开始在本地测试是正常的,上到网上就oh..no了。 40K*5,即每秒要发送200K的数据,这样就等于把2M/200K带宽给用光了,房东那限制的512K/56K带宽,就更提不上了~~~ 最后:还是用上了大伙普通通用的JpgEncoder,压缩160*160的截图后,图片大小是10K,每秒产生10K*5=50K,56K带宽刚好够用了。

     

    由于JpgEncoder为第三方插件,因此其代码就不贴了,下面简单介绍下:

    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 视频发送

    为了定时发送视频,我们需要开启定时器:

            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(); // 点击发送视频时,启动定时器即可         }

    在点击发送触发定时器时,发送视频

             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(sendEvent); // 这里只管发送,发送后的结果不管了。                         img  =   null ;         }

     

    至此,客户端的一系列动作就完成了,包括[打开视频/注册编号/发送视频/接收视频],下面到服务端代码上场了。

     

    4:控制台服务端Socket中转

    4.1:额外的处理事件

    第一:服务端需要解决跨域问题,这个看过:Silverlight+WCF 新手实例 象棋 WCF通讯跨域(十五) -- 就会明白Silverlight客户端和通讯端不在同一站点下通讯时,需要解决跨域问题了。 虽然这里没用WCF,改用Socket方式,一样需要解决跨域问题。 第二:用Socket通讯方式,还需要开启另外的943端口监听。

     

    不过这两步,网上都有现成的代码,直接copy就可以了。

    步骤如下:

    1:新建控制台项目—》起名:TCPService

    2:新建类文件:PolicyServer.cs,完整代码如下,大伙直接使用就可以了:

    PolicyServer类与跨域xml文件

    3:控制台启动首行代码

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

     

    至此,我们添加了个额外的处理类来解决943端口和跨域问题[注意上面代码中xml的端口号配置范围哦],下面开始自己的服务端处理流程

     

    4.2:服务端处理流程

    4.2.1:开启监听

    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 定义下全局变量

             public  Socket socket; // 我方的Socket         ThreadProxy youThreadProxy; // 对方         int num;//注册的编号          byte [] buffer  =   new   byte [ 30   *   1024 ]; // 缓冲字节30K,简单说就是用户10K发送3次,这里收到满30K才转发一次          bool  firstConn  =   true ; // 是否第一次建立链接,首次链接都是注册编号,不发送视频的;

     

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

    编号注册:

             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 ;                 }             }         }

    线程错误,编号移除:

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

    查询对方:

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

     

    4.2.4 主业务处理中转流程

            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) // 首次登陆,需要注册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);             }         }

    处理流程也很简单,根据请求的字节大小来调用是“注册”还是“中转”。

    至此,整个完整的视频传输篇完成了,完成的图片和上一节一样了:

     

     

    最后是大家期待已久的示例源码下载:点击下载 [别忘了留下言推荐下哦^-^]

    说明:视频源码中的内容会多一些,包括一开始我写的一些其它杂七杂八的代码,不过不影响整个的运行。

     

    最后:谢谢大家对本系列的喜欢,谢谢支持~

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

    版权声明:本文原创发表于博客园,作者为路过秋天,原文链接:

    http://www.cnblogs.com/cyq1162/archive/2010/12/03/1895177.html

    相关资源:敏捷开发V1.0.pptx
    最新回复(0)