略,内容见上篇。
1:如何打开视频
2:silverlight如何使用socket进行通讯
2.1:与远程建立链接:
2.2:注册编号[这里的规则是“房间号+棋手颜色值”]
2.3:开新线程,等待接收对方视频
2.4:将视频显示出来,需要用主线程来操作
3:图片压缩与视频发送
3.1:图片压缩
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLlR2bjlHcvN2LcNXZnFWbp9CXt92YuM3ZvxmYuNmLu9Wbt92Yvw1LcpDc0RHaiojIsJye.gif)
我们发送的视频,是通过定时器每秒截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带宽刚好够用了。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLlR2bjlHcvN2LcNXZnFWbp9CXt92YuM3ZvxmYuNmLu9Wbt92Yvw1LcpDc0RHaiojIsJye.gif)
由于jpgencoder为第三方插件,因此其代码就不贴了,下面简单介绍下:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLlR2bjlHcvN2LcNXZnFWbp9CXt92YuM3ZvxmYuNmLu9Wbt92Yvw1LcpDc0RHaiojIsJye.gif)
1:jpgencoder下载后内容为:fj.core.dll、jpgencoder.cs两个文件。
2:jpgencoder.cs有一静态方法,直接可以获取stream流:
public static stream getstream(writeablebitmap bitmap)
3:没了~~~
ps:具体fj.core.dll、jpgencoder.cs两个文件可以从下载源码下找到。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLlR2bjlHcvN2LcNXZnFWbp9CXt92YuM3ZvxmYuNmLu9Wbt92Yvw1LcpDc0RHaiojIsJye.gif)
3.2 视频发送
为了定时发送视频,我们需要开启定时器:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLlR2bjlHcvN2LcNXZnFWbp9CXt92YuM3ZvxmYuNmLu9Wbt92Yvw1LcpDc0RHaiojIsJye.gif)
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();//点击发送视频时,启动定时器即可
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLlR2bjlHcvN2LcNXZnFWbp9CXt92YuM3ZvxmYuNmLu9Wbt92Yvw1LcpDc0RHaiojIsJye.gif)
在点击发送触发定时器时,发送视频
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLlR2bjlHcvN2LcNXZnFWbp9CXt92YuM3ZvxmYuNmLu9Wbt92Yvw1LcpDc0RHaiojIsJye.gif)
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;
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLlR2bjlHcvN2LcNXZnFWbp9CXt92YuM3ZvxmYuNmLu9Wbt92Yvw1LcpDc0RHaiojIsJye.gif)
至此,客户端的一系列动作就完成了,包括[打开视频/注册编号/发送视频/接收视频],下面到服务端代码上场了。
4:控制台服务端socket中转
4.1:额外的处理事件
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLlR2bjlHcvN2LcNXZnFWbp9CXt92YuM3ZvxmYuNmLu9Wbt92Yvw1LcpDc0RHaiojIsJye.gif)
虽然这里没用wcf,改用socket方式,一样需要解决跨域问题。
第二:用socket通讯方式,还需要开启另外的943端口监听。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLlR2bjlHcvN2LcNXZnFWbp9CXt92YuM3ZvxmYuNmLu9Wbt92Yvw1LcpDc0RHaiojIsJye.gif)
不过这两步,网上都有现成的代码,直接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:开启监听
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLlR2bjlHcvN2LcNXZnFWbp9CXt92YuM3ZvxmYuNmLu9Wbt92Yvw1LcpDc0RHaiojIsJye.gif)
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("有人来了----");
//下面开启处理逻辑
}
}
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLlR2bjlHcvN2LcNXZnFWbp9CXt92YuM3ZvxmYuNmLu9Wbt92Yvw1LcpDc0RHaiojIsJye.gif)
说明:
这里要注意的是监听的端口号必须要跨域文件配置的范围内。同时用一字典泛型soketlist保存了所以注册的用户通讯socket,这样可以方便查找对方的socket进行中转。
4.2.2 定义下全局变量
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLlR2bjlHcvN2LcNXZnFWbp9CXt92YuM3ZvxmYuNmLu9Wbt92Yvw1LcpDc0RHaiojIsJye.gif)
public socket socket;//我方的socket
threadproxy youthreadproxy;//对方
int num;//注册的编号
byte[] buffer = new byte[30 * 1024];//缓冲字节30k,简单说就是用户10k发送3次,这里收到满30k才转发一次
bool firstconn = true;//是否第一次建立链接,首次链接都是注册编号,不发送视频的;
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLlR2bjlHcvN2LcNXZnFWbp9CXt92YuM3ZvxmYuNmLu9Wbt92Yvw1LcpDc0RHaiojIsJye.gif)
4.2.3 处理编号注册、移除、查找对方
编号注册:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLlR2bjlHcvN2LcNXZnFWbp9CXt92YuM3ZvxmYuNmLu9Wbt92Yvw1LcpDc0RHaiojIsJye.gif)
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;
}
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLlR2bjlHcvN2LcNXZnFWbp9CXt92YuM3ZvxmYuNmLu9Wbt92Yvw1LcpDc0RHaiojIsJye.gif)
线程错误,编号移除:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLlR2bjlHcvN2LcNXZnFWbp9CXt92YuM3ZvxmYuNmLu9Wbt92Yvw1LcpDc0RHaiojIsJye.gif)
private void onerror(threadproxy errorproxy,string errormsg)
if (errorproxy.socket != null)
errorproxy.socket.close();
console.writeline("删除用户:" + errorproxy.num +"错误信息:"+ errormsg);
program.soketlist.remove(errorproxy.num);
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLlR2bjlHcvN2LcNXZnFWbp9CXt92YuM3ZvxmYuNmLu9Wbt92Yvw1LcpDc0RHaiojIsJye.gif)
查询对方:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLlR2bjlHcvN2LcNXZnFWbp9CXt92YuM3ZvxmYuNmLu9Wbt92Yvw1LcpDc0RHaiojIsJye.gif)
private void findyousocket()
{
int younum = num % 2 == 0 ? num - 1 : num + 1;
if (program.soketlist.containskey(younum))
youthreadproxy = program.soketlist[younum];
}
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLlR2bjlHcvN2LcNXZnFWbp9CXt92YuM3ZvxmYuNmLu9Wbt92Yvw1LcpDc0RHaiojIsJye.gif)
4.2.4 主业务处理中转流程
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLlR2bjlHcvN2LcNXZnFWbp9CXt92YuM3ZvxmYuNmLu9Wbt92Yvw1LcpDc0RHaiojIsJye.gif)
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);
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLlR2bjlHcvN2LcNXZnFWbp9CXt92YuM3ZvxmYuNmLu9Wbt92Yvw1LcpDc0RHaiojIsJye.gif)
处理流程也很简单,根据请求的字节大小来调用是“注册”还是“中转”。
至此,整个完整的视频传输篇完成了,完成的图片和上一节一样了:
<a href="http://images.cnblogs.com/cnblogs_com/cyq1162/201012/chess_5_3.jpg"></a>
说明:视频源码中的内容会多一些,包括一开始我写的一些其它杂七杂八的代码,不过不影响整个的运行。
最后:谢谢大家对本系列的喜欢,谢谢支持~
ps:传说点一下推荐会有10个园豆,喜欢麻烦点一下“推荐”,thank you very much!!
版权声明:本文原创发表于博客园,作者为路过秋天,原文链接:
http://www.cnblogs.com/cyq1162/archive/2010/12/03/1895177.html