Silverlight+WCF 实战-网络象棋最终篇之对战视频-下篇[客户端发送与服务端中转](六)...
本篇繼上一篇:Silverlight+WCF 實戰-網絡象棋最終篇之對戰視頻-上篇[客戶端開啟視頻/注冊編號/接收視頻](五)
?
?一:對戰視頻?簡單原理
略,內容見上篇。?
二:對戰視頻?步驟解析:
略,內容見上篇。?
三:對戰視頻?具體實施
1:如何打開視頻
略,內容見上篇。?
2:Silverlight如何使用Socket進行通訊
2.1:與遠程建立鏈接:
2.2:注冊編號[這里的規則是“房間號+棋手顏色值”]
2.3:開新線程,等待接收對方視頻
2.4:將視頻顯示出來,需要用主線程來操作
略,以上內容見上篇。作者:路過秋天?博客:http://cyq1162.cnblogs.com/?秋色園:http://www.cyqdata.com/?
3:圖片壓縮與視頻發送
3.1:圖片壓縮
我們發送的視頻,是通過定時器每秒截5張圖發送過去的,每秒鐘將產生5張圖片,因此,圖片壓縮變的相當重要。因此,找一種圖片壓縮算法,是一種開始:
一開始:是從網上down了個PngEncoder,壓縮160*160的截圖后,圖片大小是40K,看成是4K[因為看字節時是4后面好多0,看少了一個0],興奮的我~~~
因此一開始在本地測試是正常的,上到網上就oh..no了。
40K*5,即每秒要發送200K的數據,這樣就等于把2M/200K帶寬給用光了,房東那限制的512K/56K帶寬,就更提不上了~~~
最后:還是用上了大伙普通通用的JpgEncoder,壓縮160*160的截圖后,圖片大小是10K,每秒產生10K*5=50K,56K帶寬剛好夠用了。
?
由于JpgEncoder為第三方插件,因此其代碼就不貼了,下面簡單介紹下:
1:JpgEncoder下載后內容為:FJ.Core.dll、JpgEncoder.cs兩個文件。2:JpgEncoder.cs有一靜態方法,直接可以獲取Stream流:
?public?static?Stream?GetStream(WriteableBitmap?bitmap)
3:沒了~~~
ps:具體FJ.Core.dll、JpgEncoder.cs兩個文件可以從下載源碼下找到。
?
3.2 視頻發送
為了定時發送視頻,我們需要開啟定時器:
????????System.Windows.Threading.DispatcherTimer?timer;//全局定義?????????public?MainPage()
????????{
????????????InitializeComponent();
????????????timer?=?new?System.Windows.Threading.DispatcherTimer();
????????????timer.Interval?=?TimeSpan.FromSeconds(0.2);//0.2秒一次,每秒5次
????????????timer.Tick?+=?new?EventHandler(timer_Tick);??????????
????????}
????????void?timer_Tick(object?sender,?EventArgs?e)
????????{
???????????//這里就是發送視頻的代碼了
????????}
????????private?void?btnSend_Click(object?sender,?RoutedEventArgs?e)
????????{
????????????timer.Start();//點擊發送視頻時,啟動定時器即可
????????}
在點擊發送觸發定時器時,發送視頻
????????byte[]?content?=?new?byte[56?*?1024];????????int?length;???????
????????void?timer_Tick(object?sender,?EventArgs?e)
????????{
????????????WriteableBitmap?img?=?new?WriteableBitmap(canVideo,?null);
????????????Stream?stream?=?JpgEncoder.GetStream(img);?//獲取壓縮后的流
????????????length?=?(int)stream.Length;
????????????stream.Read(content,?0,?length);
????????????stream.Close();
????????????SocketAsyncEventArgs?sendEvent?=?new?SocketAsyncEventArgs();
????????????sendEvent.SetBuffer(content,?0,?length);
????????????videoSocket.SendAsync(sendEvent);//這里只管發送,發送后的結果不管了。
???????????
????????????img?=?null;
????????}
?
至此,客戶端的一系列動作就完成了,包括[打開視頻/注冊編號/發送視頻/接收視頻],下面到服務端代碼上場了。
?
4:控制臺服務端Socket中轉
4.1:額外的處理事件
第一:服務端需要解決跨域問題,這個看過:Silverlight+WCF 新手實例 象棋 WCF通訊跨域(十五)--就會明白Silverlight客戶端和通訊端不在同一站點下通訊時,需要解決跨域問題了。雖然這里沒用WCF,改用Socket方式,一樣需要解決跨域問題。
第二:用Socket通訊方式,還需要開啟另外的943端口監聽。
?
不過這兩步,網上都有現成的代碼,直接copy就可以了。
步驟如下:
1:新建控制臺項目—》起名:TCPService
2:新建類文件:PolicyServer.cs,完整代碼如下,大伙直接使用就可以了:
PolicyServer類與跨域xml文件3:控制臺啟動首行代碼
?static?void?Main(string[]?args)?{
????PolicyServer?ps?=?new?PolicyServer(SocketPolicy.Policy);//Silverlight跨域訪問與開啟943端口
??}
?
至此,我們添加了個額外的處理類來解決943端口和跨域問題[注意上面代碼中xml的端口號配置范圍哦],下面開始自己的服務端處理流程
?
4.2:服務端處理流程
4.2.1:開啟監聽
namespace?TCPService{
????class?Program
????{
????????public?static?Dictionary<int,?ThreadProxy>?soketList;//房號+顏色值
?????????static?void?Main(string[]?args)
????????{
????????????PolicyServer?ps?=?new?PolicyServer(SocketPolicy.Policy);//Silverlight跨域訪問及943端口
????????????//主線程監聽
????????????soketList?=?new?Dictionary<int,?ThreadProxy>();
????????????Console.WriteLine("TCPService正在啟動運行");
????????????IPEndPoint?ip?=?new?IPEndPoint(IPAddress.Any,?4505);//本地任意IP及4505端口
????????????Socket?mainSocket?=?new?Socket(AddressFamily.InterNetwork,?SocketType.Stream,?ProtocolType.Tcp);
????????????mainSocket.Bind(ip);
????????????mainSocket.Listen(-1);
????????????while?(true)
????????????{
????????????????Socket?socket?=?mainSocket.Accept();
????????????????new?ThreadProxy(socket).Run();//收到消息即時處理。
????????????}
????????}
????????public?static?void?WriteLine(string?msg)
????????{
????????????Console.WriteLine(msg);
????????}
????}
????class?ThreadProxy
????{
????????public?Socket?socket;
????????public?ThreadProxy(Socket?newSocket)
????????{
????????????socket?=?newSocket;
????????}
????????public?void?Run()
????????{
????????????Thread?thread?=?new?Thread(new?ThreadStart(Action));
????????????thread.Start();
????????}
????????public?void?Action()
????????{
????????????Program.WriteLine("有人來了----");
????????????//下面開啟處理邏輯
????????}
???}
}
?
說明:
這里要注意的是監聽的端口號必須要跨域文件配置的范圍內。同時用一字典泛型soketList保存了所以注冊的用戶通訊socket,這樣可以方便查找對方的socket進行中轉。?
4.2.2 定義下全局變量
????????public?Socket?socket;//我方的Socket????????ThreadProxy?youThreadProxy;//對方 ????????int?num;//注冊的編號 ????????byte[]?buffer?=?new?byte[30?*?1024];//緩沖字節30K,簡單說就是用戶10K發送3次,這里收到滿30K才轉發一次
????????bool?firstConn?=?true;//是否第一次建立鏈接,首次鏈接都是注冊編號,不發送視頻的;
?
4.2.3 處理編號注冊、移除、查找對方
編號注冊:
????????private?void?RegSocket(string?key)????????{
????????????firstConn?=?false;//注冊完后,設置下標識
????????????if?(key.Length?<?10)//字節太多就是圖片流了
????????????{
????????????????if?(int.TryParse(key,?out?num))
????????????????{
????????????????????if?(Program.soketList.ContainsKey(num))//之前都有人在了
???????????????????????{
????????????????????????Program.soketList[num].socket.Close();
????????????????????????Program.soketList[num].socket.Dispose();
????????????????????????Program.soketList.Remove(num);
????????????????????}
????????????????????Program.soketList.Add(num,?this);
????????????????????Program.WriteLine("用戶注冊:"?+?key);
????????????????????FindYouSocket();
????????????????????return;
????????????????}
????????????}
????????}
線程錯誤,編號移除:
???????private?void?OnError(ThreadProxy?errorProxy,string?errorMsg)????????{
????????????if?(errorProxy.socket?!=?null)
????????????{
????????????????errorProxy.socket.Close();
????????????}
????????????Console.WriteLine("刪除用戶:"?+?errorProxy.num?+"錯誤信息:"+?errorMsg);
????????????Program.soketList.Remove(errorProxy.num);
????????????
????????}
查詢對方:
???????private?void?FindYouSocket()???????{
????????????int?youNum?=?num?%?2?==?0???num?-?1?:?num?+?1;
????????????if?(Program.soketList.ContainsKey(youNum))
????????????{
????????????????youThreadProxy?=?Program.soketList[youNum];
????????????}
?????????}
?
4.2.4 主業務處理中轉流程
???????public?ThreadProxy(Socket?newSocket)????????{
????????????socket?=?newSocket;
????????????socket.SendBufferSize?=?buffer.Length;
????????????socket.ReceiveBufferSize?=?buffer.Length;
????????}
????????public?void?Run()
????????{
????????????Thread?thread?=?new?Thread(new?ThreadStart(Action));
????????????thread.Start();
????????}
????????public?void?Action()
????????{
????????????Program.WriteLine("有人來了----");
????????????try
????????????{
????????????????while?(true)
????????????????{
????????????????????if?(socket.Connected)
????????????????????{
????????????????????????int?length?=?0,?count?=?0;
????????????????????????do
????????????????????????{
????????????????????????????System.Threading.Thread.Sleep(20);//關鍵點,請求太快數據接收不全
????????????????????????????length?=?socket.Receive(buffer,?count,?socket.Available,?0);
????????????????????????????count?=?count?+?length;
????????????????????????}
????????????????????????while?(socket.Available?>?0);
????????????????????????if?(count?>?1)
????????????????????????{
????????????????????????????if?(count?<?4)//小字節,命令字符
????????????????????????????{
????????????????????????????????if?(firstConn)//首次登陸,需要注冊ID
????????????????????????????????{
????????????????????????????????????string?key?=?ASCIIEncoding.ASCII.GetString(buffer,?0,?count);
????????????????????????????????????RegSocket(key);
????????????????????????????????}
????????????????????????????}
????????????????????????????else?if?(youThreadProxy?==?null)
????????????????????????????{
????????????????????????????????Program.WriteLine("沒人接收。。。");
????????????????????????????????FindYouSocket();
????????????????????????????}
????????????????????????????else?if?(youThreadProxy.canReceive)//對方允許接收圖片發送
????????????????????????????????{
????????????????????????????????Program.WriteLine("圖片來了:"?+?count);
????????????????????????????????if?(youThreadProxy.socket.Connected)
????????????????????????????????{
????????????????????????????????????Program.WriteLine("圖片轉發:"?+?buffer.Length);
????????????????????????????????????try
????????????????????????????????????{
????????????????????????????????????????youThreadProxy.socket.Send(buffer,?count,?0);
????????????????????????????????????}
????????????????????????????????????catch(Exception?err)
????????????????????????????????????{
????????????????????????????????????????OnError(youThreadProxy,?err.Message);
????????????????????????????????????}
????????????????????????????????}
????????????????????????????}
????????????????????????}
????????????????????}
????????????????????else
????????????????????{
????????????????????????OnError(this,"socket鏈接已關閉");
????????????????????????break;
????????????????????}
????????????????}
????????????}
????????????catch(Exception?err)
????????????{
????????????????OnError(this,err.Message);
????????????}
????????}
處理流程也很簡單,根據請求的字節大小來調用是“注冊”還是“中轉”。
至此,整個完整的視頻傳輸篇完成了,完成的圖片和上一節一樣了:
?
?
最后是大家期待已久的示例源碼下載:點擊下載?[別忘了留下言推薦下哦^-^]
說明:視頻源碼中的內容會多一些,包括一開始我寫的一些其它雜七雜八的代碼,不過不影響整個的運行。
?
最后:謝謝大家對本系列的喜歡,謝謝支持~
PS:傳說點一下推薦會有10個園豆,喜歡麻煩點一下“推薦”,thank you very much!!
版權聲明:本文原創發表于博客園,作者為路過秋天,原文鏈接:
http://www.cnblogs.com/cyq1162/archive/2010/12/03/1895177.html
總結
以上是生活随笔為你收集整理的Silverlight+WCF 实战-网络象棋最终篇之对战视频-下篇[客户端发送与服务端中转](六)...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 台积电获苹果A10大量订单 三季度销售额
- 下一篇: NB-IoT标准落定 规模商用还需跨过成