zoukankan      html  css  js  c++  java
  • Siverlight与WCF通信之双工netTcp实现视频对话

    效果

    先看看效果再说,基本逻辑是两个人通过Silverlight端,借助TCP协议分别向服务器不断传输视频,服务器接收到视频后,会检测这些视频是发给谁的,然后回调某个客户端来接收并显示这些视频。

    image

    实现

    双工的服务契约定义

        [ServiceContract(CallbackContract=typeof(IChatServiceCallBack))]
        public interface IChatService
        { 
            [OperationContract]
            void SendVideo(UserVideo userVideo);
        }
        [ServiceContract]
        public interface IChatServiceCallBack
        {
            [OperationContract(IsOneWay=true)]
            void GetVideos(List<UserVideo> listVideos);
        }
    数据契约,由三部分组成,发送者,接受者和视频流,方便服务器进行判断,选择接收的回调句柄。
        [DataContract]
        public class UserVideo
        {
            [DataMember]
            public string UserName { get; set; }
            [DataMember]
            public string PartnerName { set; get; }
            [DataMember]
            public byte[] VideoByte { set; get; }
        } 
    既然是双工的,当然我们还需要定义一个客户端的回调句柄类,包括两个属性,一个是客户端名称,一个是回调句柄
    public class ClientHandler {
     public string Name { set; get; } 
     public IChatServiceCallBack Client { set; get; } } 
    服务实现,这里没有采用定时检测视频集合,而是在每次有客户端上传视频时进行检测并回调客户端来接收,这样做的好处是事件驱动,比定时检测更具有准确性
    public class ChatService : IChatService
        {
            static List<ClientHandler> listOfClientHandler = new List<ClientHandler>();
            private static List<UserVideo> listVideos = new List<UserVideo>();
            IChatServiceCallBack client;
            public void SendVideo(UserVideo userVideo)
            {
                Console.WriteLine("receiving...");
                listVideos.Add(userVideo);
                
                client = OperationContext.Current.GetCallbackChannel<IChatServiceCallBack>();
                if (listOfClientHandler.Where(m => m.Name == userVideo.UserName).Count() == 0)
                {
                    listOfClientHandler.Add(new ClientHandler() { Name=userVideo.UserName, Client=client });
                }
    
                foreach(var item in listOfClientHandler)
                {
                    if (listVideos.Where(m => m.PartnerName == item.Name).Count() > 0)
                    {
                        var videos = listVideos.Where(m => m.PartnerName == item.Name).ToList();
                        item.Client.GetVideos(videos);
                        Console.WriteLine("sending...");
                        listVideos.RemoveAll(m => m.PartnerName == item.Name);//处理一个视频后直接从服务器上删除此视频
                    }
                }
            }
        }
    客户端,基本原理是先发送视频,然后定义回调函数来处理服务器的回调:
    void btnSendVideo_Click(object sender, RoutedEventArgs e)
            {
                System.Windows.Threading.DispatcherTimer timer = new System.Windows.Threading.DispatcherTimer();
                timer.Interval = new TimeSpan(0, 0, 0, 0, 200);
                timer.Tick += new EventHandler(timer_Tick);
                timer.Start(); 
            } 
    
            void timer_Tick(object sender, EventArgs e)
            {
                proxy = new ChatServiceClient();
                proxy.GetVideosReceived += new EventHandler<GetVideosReceivedEventArgs>(proxy_GetVideosReceived);
                WriteableBitmap bmp = new WriteableBitmap(this.rectangleUser, null);
                MemoryStream ms = new MemoryStream();
                EncodeJpeg(bmp, ms);
                UserVideo userVideo = new UserVideo();
                userVideo.PartnerName = this.Partner;
                userVideo.UserName = this.User;
                userVideo.VideoByte = ms.GetBuffer();
                proxy.SendVideoCompleted += (se,ev) => { };
                proxy.SendVideoAsync(userVideo);
            }
    
            void proxy_GetVideosReceived(object sender, GetVideosReceivedEventArgs e)
            {
                foreach (ChatService.UserVideo video in e.listVideos)
                {
                    MemoryStream ms = new MemoryStream(video.VideoByte);
                    BitmapImage bitmap = new BitmapImage();
                    bitmap.SetSource(ms);
                    imagePartner.Source = bitmap;
                    ms.Close();
                }
            }
    app.config配置 
    <system.serviceModel>
        <bindings>
          <netTcpBinding>
            <binding name="netTcpBindConfig"
                     closeTimeout="00:01:00"
                     openTimeout="00:01:00"
                     receiveTimeout="00:10:00"
                     sendTimeout="00:01:00"
                     transactionFlow="false"
                     transferMode="Buffered"
                     transactionProtocol="OleTransactions"
                     hostNameComparisonMode="StrongWildcard"
                     listenBacklog="10"
                     maxBufferPoolSize="2147483647 "
                     maxBufferSize="2147483647 "
                     maxConnections="10"
                     maxReceivedMessageSize="2147483647 ">
              <readerQuotas maxDepth="32"
                            maxStringContentLength="2147483647 "
                            maxArrayLength="2147483647 "
                            maxBytesPerRead="4096"
                            maxNameTableCharCount="16384" />
              <reliableSession ordered="true"
                               inactivityTimeout="00:10:00"
                               enabled="false" />
              <security mode="None"> 
              </security>
            </binding>        
          </netTcpBinding>
        </bindings>
        <services>
    
          <service behaviorConfiguration="Server.ChatServiceBehavior" name="Server.ChatService">
            <host>
              <baseAddresses>
                <add baseAddress="net.tcp://localhost:4503/ChatService"/>
              </baseAddresses>
            </host>
            <endpoint address="" binding="netTcpBinding" contract="Server.IChatService" bindingConfiguration="netTcpBindConfig"></endpoint>
            <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" ></endpoint>
          </service>
    
        </services>
        <behaviors>
          <serviceBehaviors>
            <behavior name="Server.ChatServiceBehavior">
              <serviceMetadata/>
              <serviceDebug includeExceptionDetailInFaults="false"/>
            </behavior>
          </serviceBehaviors>
        </behaviors>
      </system.serviceModel>
    

    遗留问题

    1,由于客户端是定时上传视频流,而非长连接方式,需要不停的调用服务器来上传视频,有些耗资源,并且有时会出现下面的异常,猜想是由于不停的连接导致。

    image

    2,wcf传输方式配置的是transferMode="Buffered",这种方式并不适合流式传输。实时性上仍有待改进。

    https://files.cnblogs.com/wengyuli/chatTCPduplex.rar


  • 相关阅读:
    python学习(十七) 扩展python
    python学习(十六) 测试
    python学习(十五) 屏幕抓取
    python学习(十四) 网络编程
    python学习(十三) 数据库支持
    python学习(十二) 图形化用户界面
    python学习(十一) 文件和流
    python学习(十) 自带电池
    方法编写经验总结
    如何编写方法
  • 原文地址:https://www.cnblogs.com/fx2008/p/2278359.html
Copyright © 2011-2022 走看看