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