效果
先看看效果再说,基本逻辑是两个人通过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",这种方式并不适合流式传输。实时性上仍有待改进。
https://files.cnblogs.com/wengyuli/chatTCPduplex.rar