天天看點

Silverlight+WCF 實戰-網絡象棋最終篇之對戰視訊-下篇[用戶端發送與服務端中轉](六)

略,内容見上篇。

1:如何打開視訊

2:silverlight如何使用socket進行通訊

2.1:與遠端建立連結:

2.2:注冊編号[這裡的規則是“房間号+棋手顔色值”]

2.3:開新線程,等待接收對方視訊

2.4:将視訊顯示出來,需要用主線程來操作

3:圖檔壓縮與視訊發送

3.1:圖檔壓縮

Silverlight+WCF 實戰-網絡象棋最終篇之對戰視訊-下篇[用戶端發送與服務端中轉](六)
Silverlight+WCF 實戰-網絡象棋最終篇之對戰視訊-下篇[用戶端發送與服務端中轉](六)

我們發送的視訊,是通過定時器每秒截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帶寬剛好夠用了。

Silverlight+WCF 實戰-網絡象棋最終篇之對戰視訊-下篇[用戶端發送與服務端中轉](六)

由于jpgencoder為第三方插件,是以其代碼就不貼了,下面簡單介紹下:

Silverlight+WCF 實戰-網絡象棋最終篇之對戰視訊-下篇[用戶端發送與服務端中轉](六)
Silverlight+WCF 實戰-網絡象棋最終篇之對戰視訊-下篇[用戶端發送與服務端中轉](六)

1:jpgencoder下載下傳後内容為:fj.core.dll、jpgencoder.cs兩個檔案。

2:jpgencoder.cs有一靜态方法,直接可以擷取stream流:

 public static stream getstream(writeablebitmap bitmap)

3:沒了~~~

ps:具體fj.core.dll、jpgencoder.cs兩個檔案可以從下載下傳源碼下找到。

Silverlight+WCF 實戰-網絡象棋最終篇之對戰視訊-下篇[用戶端發送與服務端中轉](六)

3.2 視訊發送

為了定時發送視訊,我們需要開啟定時器:

Silverlight+WCF 實戰-網絡象棋最終篇之對戰視訊-下篇[用戶端發送與服務端中轉](六)
Silverlight+WCF 實戰-網絡象棋最終篇之對戰視訊-下篇[用戶端發送與服務端中轉](六)

        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();//點選發送視訊時,啟動定時器即可

Silverlight+WCF 實戰-網絡象棋最終篇之對戰視訊-下篇[用戶端發送與服務端中轉](六)

在點選發送觸發定時器時,發送視訊

Silverlight+WCF 實戰-網絡象棋最終篇之對戰視訊-下篇[用戶端發送與服務端中轉](六)
Silverlight+WCF 實戰-網絡象棋最終篇之對戰視訊-下篇[用戶端發送與服務端中轉](六)

        byte[] content = new byte[56 * 1024];

        int length;       

            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;

Silverlight+WCF 實戰-網絡象棋最終篇之對戰視訊-下篇[用戶端發送與服務端中轉](六)

至此,用戶端的一系列動作就完成了,包括[打開視訊/注冊編号/發送視訊/接收視訊],下面到服務端代碼上場了。

4:控制台服務端socket中轉

4.1:額外的處理事件

Silverlight+WCF 實戰-網絡象棋最終篇之對戰視訊-下篇[用戶端發送與服務端中轉](六)

雖然這裡沒用wcf,改用socket方式,一樣需要解決跨域問題。

第二:用socket通訊方式,還需要開啟另外的943端口監聽。

Silverlight+WCF 實戰-網絡象棋最終篇之對戰視訊-下篇[用戶端發送與服務端中轉](六)

不過這兩步,網上都有現成的代碼,直接copy就可以了。

步驟如下:

1:建立控制台項目—》起名:tcpservice

2:建立類檔案:policyserver.cs,完整代碼如下,大夥直接使用就可以了:

Silverlight+WCF 實戰-網絡象棋最終篇之對戰視訊-下篇[用戶端發送與服務端中轉](六)

policyserver類與跨域xml檔案

3:控制台啟動首行代碼

 static void main(string[] args)

 {

    policyserver ps = new policyserver(socketpolicy.policy);//silverlight跨域通路與開啟943端口

  }

至此,我們添加了個額外的處理類來解決943端口和跨域問題[注意上面代碼中xml的端口号配置範圍哦],下面開始自己的服務端處理流程

4.2:服務端處理流程

4.2.1:開啟監聽

Silverlight+WCF 實戰-網絡象棋最終篇之對戰視訊-下篇[用戶端發送與服務端中轉](六)
Silverlight+WCF 實戰-網絡象棋最終篇之對戰視訊-下篇[用戶端發送與服務端中轉](六)

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("有人來了----");

            //下面開啟處理邏輯

   }

}

Silverlight+WCF 實戰-網絡象棋最終篇之對戰視訊-下篇[用戶端發送與服務端中轉](六)

說明:

這裡要注意的是監聽的端口号必須要跨域檔案配置的範圍内。同時用一字典泛型soketlist儲存了是以注冊的使用者通訊socket,這樣可以友善查找對方的socket進行中轉。

4.2.2 定義下全局變量

Silverlight+WCF 實戰-網絡象棋最終篇之對戰視訊-下篇[用戶端發送與服務端中轉](六)
Silverlight+WCF 實戰-網絡象棋最終篇之對戰視訊-下篇[用戶端發送與服務端中轉](六)

        public socket socket;//我方的socket

        threadproxy youthreadproxy;//對方

        int num;//注冊的編号

        byte[] buffer = new byte[30 * 1024];//緩沖位元組30k,簡單說就是使用者10k發送3次,這裡收到滿30k才轉發一次

        bool firstconn = true;//是否第一次建立連結,首次連結都是注冊編号,不發送視訊的;

Silverlight+WCF 實戰-網絡象棋最終篇之對戰視訊-下篇[用戶端發送與服務端中轉](六)

4.2.3 處理編号注冊、移除、查找對方

編号注冊:

Silverlight+WCF 實戰-網絡象棋最終篇之對戰視訊-下篇[用戶端發送與服務端中轉](六)
Silverlight+WCF 實戰-網絡象棋最終篇之對戰視訊-下篇[用戶端發送與服務端中轉](六)

        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;

                }

Silverlight+WCF 實戰-網絡象棋最終篇之對戰視訊-下篇[用戶端發送與服務端中轉](六)

線程錯誤,編号移除:

Silverlight+WCF 實戰-網絡象棋最終篇之對戰視訊-下篇[用戶端發送與服務端中轉](六)
Silverlight+WCF 實戰-網絡象棋最終篇之對戰視訊-下篇[用戶端發送與服務端中轉](六)

       private void onerror(threadproxy errorproxy,string errormsg)

            if (errorproxy.socket != null)

                errorproxy.socket.close();

            console.writeline("删除使用者:" + errorproxy.num +"錯誤資訊:"+ errormsg);

            program.soketlist.remove(errorproxy.num);

Silverlight+WCF 實戰-網絡象棋最終篇之對戰視訊-下篇[用戶端發送與服務端中轉](六)

查詢對方:

Silverlight+WCF 實戰-網絡象棋最終篇之對戰視訊-下篇[用戶端發送與服務端中轉](六)
Silverlight+WCF 實戰-網絡象棋最終篇之對戰視訊-下篇[用戶端發送與服務端中轉](六)

       private void findyousocket()

       {

            int younum = num % 2 == 0 ? num - 1 : num + 1;

            if (program.soketlist.containskey(younum))

                youthreadproxy = program.soketlist[younum];

         }

Silverlight+WCF 實戰-網絡象棋最終篇之對戰視訊-下篇[用戶端發送與服務端中轉](六)

4.2.4 主業務進行中轉流程

Silverlight+WCF 實戰-網絡象棋最終篇之對戰視訊-下篇[用戶端發送與服務端中轉](六)
Silverlight+WCF 實戰-網絡象棋最終篇之對戰視訊-下篇[用戶端發送與服務端中轉](六)

       public threadproxy(socket newsocket)

            socket.sendbuffersize = buffer.length;

            socket.receivebuffersize = buffer.length;

            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);

Silverlight+WCF 實戰-網絡象棋最終篇之對戰視訊-下篇[用戶端發送與服務端中轉](六)

處理流程也很簡單,根據請求的位元組大小來調用是“注冊”還是“中轉”。

至此,整個完整的視訊傳輸篇完成了,完成的圖檔和上一節一樣了:

<a href="http://images.cnblogs.com/cnblogs_com/cyq1162/201012/chess_5_3.jpg"></a>

Silverlight+WCF 實戰-網絡象棋最終篇之對戰視訊-下篇[用戶端發送與服務端中轉](六)

說明:視訊源碼中的内容會多一些,包括一開始我寫的一些其它雜七雜八的代碼,不過不影響整個的運作。

最後:謝謝大家對本系列的喜歡,謝謝支援~

ps:傳說點一下推薦會有10個園豆,喜歡麻煩點一下“推薦”,thank you very much!!

版權聲明:本文原創發表于部落格園,作者為路過秋天,原文連結:

http://www.cnblogs.com/cyq1162/archive/2010/12/03/1895177.html

繼續閱讀