你的分享就是我们的动力 ---﹥

Silverlight与WCF之间的通信(7) 双工netTcp实现视频对话

时间:2011-09-30 10:05来源:www.chengxuyuans.com 点击:

效果

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

实现

双工的服务契约定义

[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,

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

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

随文源码

转载注明地址:http://www.chengxuyuans.com/slverlight/19412.html