天天看点

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

继续阅读