zoukankan      html  css  js  c++  java
  • 一起谈.NET技术,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

  • 相关阅读:
    Java 中文数字转换为阿拉伯数字
    正则表达式转义符
    git .gitignore详解
    git 陷阱小记
    git log 附加命令归纳
    git 命令归纳版
    《Effective Java》 读书笔记(九)使用try-with-resources 语句替代try-finally
    架构设计 | 接口幂等性原则,防重复提交Token管理
    数据源管理 | OLAP查询引擎,ClickHouse集群化管理
    Java并发编程(04):线程间通信,等待/通知机制
  • 原文地址:https://www.cnblogs.com/waw/p/2162664.html
Copyright © 2011-2022 走看看