略,内容見上篇。
1:如何打開視訊
2:silverlight如何使用socket進行通訊
2.1:與遠端建立連結:
2.2:注冊編号[這裡的規則是“房間号+棋手顔色值”]
2.3:開新線程,等待接收對方視訊
2.4:将視訊顯示出來,需要用主線程來操作
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;
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:額外的處理事件

雖然這裡沒用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.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);

處理流程也很簡單,根據請求的位元組大小來調用是“注冊”還是“中轉”。
至此,整個完整的視訊傳輸篇完成了,完成的圖檔和上一節一樣了:
<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