Siverlight與WCF通信之雙工netTcp實現視頻對話
效果
先看看效果再說,基本邏輯是兩個人通過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);
}
public interface IChatService
{
[OperationContract]
void SendVideo(UserVideo userVideo);
}
[ServiceContract]
public interface IChatServiceCallBack
{
[OperationContract(IsOneWay=true)]
void GetVideos(List<UserVideo> listVideos);
}
數據契約,由三部分組成,發送者,接受者和視頻流,方便服務器進行判斷,選擇接收的回調句柄。
遺留問題
1、由于客戶端是定時上傳視頻流,而非長連接方式,需要不停的調用服務器來上傳視頻,有些耗資源,并且有時會出現下面的異常,猜想是由于不停的連接導致。
[DataContract]
public class UserVideo
{
[DataMember]
public string UserName { get; set; }
[DataMember]
public string PartnerName { set; get; }
[DataMember]
public byte[] VideoByte { set; get; }
}
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 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);//處理一個視頻后直接從服務器上刪除此視頻
}
}
}
}
{
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();
}
}
{
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>
<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",這種方式并不適合流式傳輸。實時性上仍有待改進。
全站熱搜