建立與使用WebSocket API(2)

by vivid 24. 四月 2013 10:53

.NET Magazine國際中文電子雜誌
作 者:許薰尹
審 稿:張智凱
文章編號:N130413502
出刊日期:2013/4/24

在前一篇文章中介紹了HTML5 WebSocket API基本呼叫方式,在JavaScript中定義WebSocket物件建立用戶端應用程式與伺服端之間的連線,並傳送文字格式到伺服端ASP.NET網站,同時ASP.NET網頁收到請求之後,回應文字格式的訊息。除了傳送文字格式的訊息之外,WebSocket也可以,傳送二進位格式、JSON格式訊息或陣列資料到伺服端。本文介紹如何使用HTML5 WebSocket API進行在用戶端與伺服端之間,進行各種資料格式的交換動作。

 

WebSocket協定

回顧前文提及的WebSocket協定,它可使用雙工方式,讓瀏覽器和Web伺服器進行即時通訊。建立WebSocket連線,在初化階段用戶端與伺服端就從使用HTTP協定,自動升級轉換改用WebSocket協定。

例如底下是使用Chrome瀏覽器觀察WebSocket呼叫結果,瀏覽器以HTTP GET方式傳送請求給WebSocket伺服端程式,建立連線之後,後續會改用Transmission Control Protocol (TCP) socket協定進行通訊。

clip_image002

圖 1:瀏覽器與伺服端自動升級轉換改用WebSocket協定。

WebSocket可以傳送的文字類型資訊包含UTF8格式文字、純文字、JSON格式字串,或base64編碼的字串,以下將探討如何交換這些不同格式的資料。

傳送ArrayBuffer物件

ArrayBuffer表示一個緩衝區,用於存放二進位資料,您可以利用Uint8Array或Int32Array來取得ArrayBuffer物件。Uint8Array或Int32Array都是屬於具型別陣列(Typed Array),分別存放8位元(bit) 不帶正負號整數值,以及32位元帶正負號的整數值。Uint8Array值的有效範圍為0~255;Int32Array值的有效範圍為-2147483648到2147483647。預設陣列中的成員會被初始化為0。

傳送Uint8Array範例

以下範例程式在建立連線,觸發Websocket的open事件時,建立一個Uint8Array陣列,送出8位元不帶正負號的整数(unsigned integer)陣列到Websocket伺服端進行加總。並在message事件中將伺服端計算完的結果取回顯示在網頁畫面上:

<!DOCTYPE html>
<html xmlns = "http://www.w3.org/1999/xhtml" >
<head>
    <meta http-equiv = "Content-Type" content = "text/html; charset=utf-8" />
    <title> </title>
    <script type = "text/javascript">
        function openSocket ( ) {
            var socket = new WebSocket( "ws://localhost:26462/WSHandler1.ashx" );
            socket.onopen = function () {
                var numbers = new Uint8Array( [10, 20, 30, 40, 50, 60, 70, 80, 90] );
                socket.send( numbers.buffer );
                alert( "訊息已傳送..." );
            };
            socket.onmessage = function ( event ) {
                var msg = event.data;
                document.getElementById( "result" ).innerHTML = "已收到伺服器傳送的訊息 : " + msg;
            };
            socket.onclose = function ( event ) {
                alert( "連線已關閉... state :" + socket.readyState );
            };
            socket.onerror = function ( event ) {
                alert( "發生錯誤 : " + event.data );
            };
        }
    </script>
</head>
<body>
    <input id = "Button1" type = "button" value = "Connect to Server" onclick = "openSocket()" />
    <div id = "result"> </div>
</body>
</html>

 

伺服端的程式如下,收到資料之後利用一個迴圈,將接收到的位元陣列內容取出之後進行加總,最後再將運算結果傳回用戶端:

<%@ WebHandler Language = "C#" Class = "WSHandler" %>

using System;
using System.Web;
using System.Threading.Tasks;
using System.Web.WebSockets;
using System.Net.WebSockets;
using System.Text;
using System.Threading;

public class WSHandler : IHttpHandler
{

    public void ProcessRequest( HttpContext context )
    {
        if ( context.IsWebSocketRequest )
            context.AcceptWebSocketRequest( MyWebSocketHandler );
        else
            context.Response.StatusCode = 400; //Bad Request
    }

    public async Task MyWebSocketHandler( AspNetWebSocketContext context )
    {
        WebSocket socket = context.WebSocket;

        while ( true )
        {
            ArraySegment<byte> buffer = new ArraySegment<byte>( new byte [ 1024 ] );
            WebSocketReceiveResult r = await socket.ReceiveAsync( buffer , CancellationToken.None );

            if ( socket.State == WebSocketState.Open )
            {

                int total = 0;
                for ( int i = 0 ; i < buffer.Array.Length ; i++ )
                {
                    total += buffer.Array [ i ];
                }
                string msg = total.ToString( );
                msg = "伺服端收到你傳送的訊息 : " + msg + "  時間 : " + DateTime.Now.ToString( );

                buffer = new ArraySegment<byte>( Encoding.UTF8.GetBytes( msg ) );
                await socket.SendAsync( buffer , WebSocketMessageType.Text , true , CancellationToken.None );
            }
        }
    }
    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

 

此網頁範例執行的結果,請參考下圖所示:

clip_image003

圖 2:傳送Uint8Array陣列範例。

傳送Uint32Array範例

若用戶端傳送的是Uint32Array,例如將上一個範例open事件的程式碼修改如下:

 

socket.onopen = function ( ) {
    alert( "建立與伺服端的連線!  state :" + socket.readyState );
   var numbers = new Uint32Array( [-10,-20,-30, -40, 50, 60, 70, 80, 90] );
   socket.send( numbers.buffer );
    alert( "訊息已傳送..." );
};

 

則伺服端收到資料時,需要加上一些程式碼,將32位元的資料轉換成數值。每一個數值的資料以四個位元組組成,例如第一個「-10」的值送到伺服端時,伺服端的buffer.Array前四個項目的內容分別為「246、255、255、255」,我們可以利用BitConverter.ToInt32方法將其轉換回「-10」:

<%@ WebHandler Language = "C#" Class = "WSHandler" %>

using System;
using System.Web;
using System.Threading.Tasks;
using System.Web.WebSockets;
using System.Net.WebSockets;
using System.Text;
using System.Threading;

public class WSHandler : IHttpHandler
{

    public void ProcessRequest( HttpContext context )
    {
        if ( context.IsWebSocketRequest )
            context.AcceptWebSocketRequest( MyWebSocketHandler );
        else
            context.Response.StatusCode = 400; //Bad Request
    }

    public async Task MyWebSocketHandler( AspNetWebSocketContext context )
    {
        WebSocket socket = context.WebSocket;

        while ( true )
        {
            ArraySegment<byte> buffer = new ArraySegment<byte>( new byte [ 1024 ] );
            WebSocketReceiveResult r = await socket.ReceiveAsync( buffer , CancellationToken.None );

            if ( socket.State == WebSocketState.Open )
            {
                var len = buffer.Array.Length;
                int [ ] data = new int [ len / 4 ];
                var idx = 0;
                for ( int i = 0 ; i < len ; i += 4 )
                {
                    byte [ ] number = { buffer.Array [ i ] , buffer.Array [ i + 1 ] , buffer.Array [ i + 2 ] , buffer.Array [ i + 3 ] };
                    data [ idx ] = BitConverter.ToInt32( number , 0 );
                    idx++;
                }

                int total = 0;
                for ( int i = 0 ; i < data.Length ; i++ )
                {
                    total += data [ i ];
                }
                string msg = total.ToString( );
                msg = "伺服端收到你傳送的訊息 : " + msg + "  時間 : " + DateTime.Now.ToString( );

                buffer = new ArraySegment<byte>( Encoding.UTF8.GetBytes( msg ) );
                await socket.SendAsync( buffer , WebSocketMessageType.Text , true , CancellationToken.None );
            }
        }
    }
    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

 

此網頁範例執行的結果,請參考下圖所示:

clip_image004

圖 3:傳送Uint32Array範例。

 

傳送字串陣列範例

若用戶端傳送的是字串陣列,例如將上一個範例open事件的程式碼修改如下:

 

socket.onopen = function () {
   alert( "建立與伺服端的連線!  state :" + socket.readyState );
   var arr = [ "a" , "b" , "c" , "d" ];     
   socket.send( arr );
   alert( "訊息已傳送..." );
};

 

伺服端可以使用Encoding.UTF8.GetString方法,將收到的位元組陣列轉換字串。以此範例而言,伺服端收到的資料轉換成字串的結果為:「a,b,c,d」,陣列中的元素將會以逗號作區隔;因此只需要利用string類別的split方法,根據「,」號將字串切割之後,就可以利用索引取出任一個陣列元素值:

 

<%@ WebHandler Language = "C#" Class = "WSHandler" %>

using System;
using System.Web;
using System.Threading.Tasks;
using System.Web.WebSockets;
using System.Net.WebSockets;
using System.Text;
using System.Threading;

public class WSHandler : IHttpHandler
{

    public void ProcessRequest( HttpContext context )
    {
        if ( context.IsWebSocketRequest )
            context.AcceptWebSocketRequest( MyWebSocketHandler );
        else
            context.Response.StatusCode = 400; //Bad Request
    }

    public async Task MyWebSocketHandler( AspNetWebSocketContext context )
    {
        WebSocket socket = context.WebSocket;

        while ( true )
        {
            ArraySegment<byte> buffer = new ArraySegment<byte>( new byte [ 1024 ] );
            WebSocketReceiveResult r = await socket.ReceiveAsync( buffer , CancellationToken.None );
            if ( socket.State == WebSocketState.Open )
            {
                string msg = Encoding.UTF8.GetString( buffer.Array );
                string [ ] data = msg.Split( new char [ ] { ',' } );
                msg = "伺服端收到你傳送的訊息 : 第一個文字是 : " + data [ 0 ];
                buffer = new ArraySegment<byte>( Encoding.UTF8.GetBytes( msg ) );
                await socket.SendAsync( buffer , WebSocketMessageType.Text , true , CancellationToken.None );
            }
        }
    }
    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

 

此網頁範例執行的結果,請參考下圖所示:

clip_image005

圖 4:傳送字串陣列範例。

傳送文字檔案範例

用戶端也可以將檔案傳送到伺服端,只要在網頁中使用型別為file的input項目,瀏覽器就可以顯示選取檔案的方塊,讓你挑選要傳送到Websocket的檔案,參考以下範例:

<!DOCTYPE html>
<html xmlns = "http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv = "Content-Type" content = "text/html; charset=utf-8" />
    <title> </title>
    <script type = "text/javascript" >
        function openSocket( ) {
            var socket = new WebSocket( "ws://localhost:26462/WSHandler2.ashx" );
            socket.onopen = function ( ) {
                alert( "建立與伺服端的連線!  state :" + socket.readyState );
                var file = document.querySelector( 'input[type="file"]' ).files[0];
                socket.send( file );
                alert("訊息已傳送...");
            };
            socket.onmessage = function ( event ) {
                var msg = event.data;
                alert( "已收到伺服器傳送的訊息..." + msg );
            };
            socket.onclose = function ( event ) {
                alert( "連線已關閉... state :" + socket.readyState );
            };
            socket.onerror = function ( event ) {
                alert( "發生錯誤 : " + event.data );
            };
        }
    </script>
</head>
<body>
    <input id = "Button1" type = "button" value = "Connect to Server" onclick = "openSocket()" />
    <input id = "File1" type = "file" />
</body>
</html>

 

伺服端收到檔案之後,便可從buffer.Array取出檔案內容,並利用FileStream物件將檔案儲存在伺服端:

<%@ WebHandler Language = "C#" Class = "WSHandler" %>

using System;
using System.Web;
using System.Threading.Tasks;
using System.Web.WebSockets;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
public class WSHandler : IHttpHandler
{
    public void ProcessRequest( HttpContext context )
    {
        if ( context.IsWebSocketRequest )
            context.AcceptWebSocketRequest( MyWebSocketHandler );
        else
            context.Response.StatusCode = 400; //Bad Request
    }
    public async Task MyWebSocketHandler( AspNetWebSocketContext context )
    {
        WebSocket socket = context.WebSocket;
        while ( true )
        {
            ArraySegment<byte> buffer = new ArraySegment<byte>( new byte [ 1024 ] );
            WebSocketReceiveResult r = await socket.ReceiveAsync( buffer , CancellationToken.None );
            if ( socket.State == WebSocketState.Open )
            {
                int len = buffer.Array.Length;
                System.IO.FileStream s = new System.IO.FileStream( HttpContext.Current.Server.MapPath( "data.txt" ) , System.IO.FileMode.Create , System.IO.FileAccess.ReadWrite );
                s.Write( buffer.Array , 0 , len );
                s.Close( );

                string msg = len.ToString( );
                msg = "伺服端收到你傳送的訊息 : " + msg + "  時間 : " + DateTime.Now.ToString( );

                buffer = new ArraySegment<byte>( Encoding.UTF8.GetBytes( msg ) );
                await socket.SendAsync( buffer , WebSocketMessageType.Text , true , CancellationToken.None );
            }
        }
    }
    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

 

此網頁範例執行的結果,請參考下圖所示,先選取要傳送的文字檔,再傳送到伺服器:

clip_image006

圖 5:傳送文字檔案範例。

伺服器回傳的訊息,請參考下圖所示:

clip_image007

圖 6:傳送文字檔案範例。

整合拖曳功能傳送檔案

在實務上,我們經常使用拖曳的方式,將檔案傳送到伺服端,你可以整合HTML5拖曳功能來傳送檔案。關於HTML5拖曳功能在本站《.NET Magazine國際中文電子雜誌》的《使用HTML 5 File API(1)》一文中已有詳細的說明,本文不再贅述,參考以下範例延續上一個範例情境,但改用拖曳的方式進行檔案上傳:

<!DOCTYPE html>
<html xmlns = "http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv  ="Content-Type" content = "text/html; charset=utf-8" />
    <title> </title>
    <script type = "text/javascript" >
        function wireDragEvents( ) {
            var div = document.getElementById( 'dropDiv' );
            div.addEventListener( 'drop' , handleDrop , false );
            div.addEventListener( 'dragenter' , handleEnter , false );
            div.addEventListener( 'dragover' , handleDragOver , false );
        }
        function handleEnter( e ) {
            document.getElementById( 'dropDiv' ).textContent = "";
            e.stopPropagation( );
            e.preventDefault( );
        }
        function handleDrop( e ) {
            e.stopPropagation ();
            e.preventDefault( );
            var files = e.dataTransfer.files;
            var file = files[0];
            document.getElementById( 'dropDiv' ).innerHTML += " <br/>" +
                    " 檔案名稱 :" + file.name + " <br/>  " +
                    "&nbsp;&nbsp;&nbsp;&nbsp; 檔案大小 : " + file.size + "<br/>   " +
                    "&nbsp;&nbsp;&nbsp;&nbsp; 最後修改時間 : " + file.lastModifiedDate;

            var socket = new WebSocket( "ws://localhost:26462/WSHandler2.ashx" );
            socket.onopen = function ( ) {
                socket.send( file );
                alert( "訊息已傳送..." );
            };
            socket.onmessage = function ( event ) {
                var msg = event.data;
                alert( "已收到伺服器傳送的訊息..." + msg );
            };

            socket.onclose = function ( event ) {
                alert( "連線已關閉... state :" + socket.readyState );
            };

            socket.onerror = function ( event ) {
                alert( "發生錯誤 : " + event.data );
            };
        }
        function handleDragOver( e ) {
            e.stopPropagation( );
            e.preventDefault( );
            e.dataTransfer.dropEffect = 'copy';
        }
        window.addEventListener( "DOMContentLoaded", wireDragEvents, false );
    </script>
</head>
<body>

    <div id = "dropDiv" dropzone = "copy" style = "width: 300px; height: 200px; padding: 50px; border: solid 2px black;" >
        請拖曳檔案到此區塊以上傳到伺服器
    </div>
</body>
</html>


 

此網頁範例執行的結果,請參考下圖所示,執行時顯示可以拖放檔案的區塊:

clip_image009

圖 7:整合拖曳功能傳送檔案。

從檔案總管拖曳檔案到網頁中的區塊之後,執行結果如下所示:

clip_image011

圖 8:整合拖曳功能傳送檔案。

傳送JSON格式資料範例

用戶端若要傳送JSON格式資料,只需要建立物件,設好屬性之後,利用JSON.stringify方法,將物件序列化成字串後,便可以進行傳送,例如以下範例所示:

<!DOCTYPE html>
<html xmlns = http://www.w3.org/1999/xhtml >
<head>
    <meta http-equiv = "Content-Type" content = "text/html; charset=utf-8" />
    <title> </title>
    <script type = "text/javascript" >

        function openSocket( ) {
            var socket = new WebSocket( "ws://localhost:26462/WSHandler3.ashx" );
            socket.onopen = function ( ) {
                alert( "建立與伺服端的連線!  state :" + socket.readyState );
                var emp = {
                    ID : document.getElementById("Text1").value,
                    Name : document.getElementById("Text2").value,
                    Age : document.getElementById("Text3").value
                };
                var s = JSON.stringify( emp );
                socket.send( s );
                alert( "訊息已傳送..." );
            };
            socket.onmessage = function ( event ) {
                var msg = event.data;
                alert( "已收到伺服器傳送的訊息..." + msg );
            };

            socket.onclose = function ( event ) {
                alert( "連線已關閉... state :" + socket.readyState );
            };

            socket.onerror = function ( event ) {
                alert( "發生錯誤 : " + event.data );
            };
        }

    </script>
</head>
<body>
    <fieldset>
        <legend>Personal Data</legend>
        <label for = "Text1"> ID: </label>
        <input id = "Text1" type = "text" /> <br />
        <label for = "Text2"> Name: </label>
        <input id = "Text2" type = "text" /> <br />
        <label for = "Text3"> Age: </label>
        <input id = "Text3" type = "text" /> <br />
    </fieldset>

    <input id = "Button1" type = "button" value = "Connect to Server" onclick = "openSocket()" />
</body>
</html>


 

伺服端收到字串格式的JSON格式資料之後,可以利用JavaScriptSerializer物件進行還原序列化成IDictionary<string , object>字典類型物件,參考以下範例所示:

<%@ WebHandler Language = "C#" Class = "WSHandler" %>

using System;
using System.Web;
using System.Threading.Tasks;
using System.Web.WebSockets;
using System.Net.WebSockets;
using System.Text;
using System.Threading;

public class WSHandler : IHttpHandler
{

    public void ProcessRequest( HttpContext context )
    {
        if ( context.IsWebSocketRequest )
            context.AcceptWebSocketRequest( MyWebSocketHandler );
        else
            context.Response.StatusCode = 400; //Bad Request
    }

    public async Task MyWebSocketHandler( AspNetWebSocketContext context )
    {
        WebSocket socket = context.WebSocket;

        while ( true )
        {
            ArraySegment<byte> buffer = new ArraySegment<byte>( new byte [ 1024 ] );
            WebSocketReceiveResult r = await socket.ReceiveAsync( buffer , CancellationToken.None );

            if ( socket.State == WebSocketState.Open )
            {
                var json = Encoding.UTF8.GetString( buffer.Array , 0 , r.Count );
                var serializer = new System.Web.Script.Serialization.JavaScriptSerializer( );
                var emp = ( System.Collections.Generic.IDictionary<string , object> ) serializer.DeserializeObject( json );

                var ID = emp [ "ID" ];
                var Name = emp [ "Name" ];
                var Age = emp [ "Age" ];

                string msg = ID + "," + Name + "," + Age;
                msg = "伺服端收到你傳送的訊息 : " + msg + "  時間 : " + DateTime.Now.ToString( );

                buffer = new ArraySegment<byte>( Encoding.UTF8.GetBytes( msg ) );
                await socket.SendAsync( buffer , WebSocketMessageType.Text , true , CancellationToken.None );
            }
        }
    }
    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

 

clip_image012

圖 9:傳送JSON格式資料範例。

clip_image013

圖 10:傳送JSON格式資料範例,用戶端接收到的訊息。

 

接收伺服端JSON格式資料範例

若用戶端預期將收到伺服端回傳的JSON格式資料,便可以在message事件中,利用JSON.parse方法將取回的字串還原成物件,便可直接存取物件的屬性值,例如以下範例程式:

 

<!DOCTYPE html>
<html xmlns = "http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv = "Content-Type" content = "text/html; charset=utf-8" />
    <title ></title>
    <script type = "text/javascript">
        function openSocket( ) {
            var socket = new WebSocket( "ws://localhost:26462/WSHandler4.ashx" );
            socket.onopen = function ( ) {
                var emp = {
                    ID : document.getElementById("Text1").value,
                    Name : document.getElementById("Text2").value,
                    Age : document.getElementById("Text3").value
                };
                var s = JSON.stringify( emp );
                socket.send( s );
                alert( "訊息已傳送..." );
            };
            socket.onmessage = function ( event ) {
                var msg = event.data;
                var emp = JSON.parse( msg );
                document.getElementById( "result" ).innerHTML = "已收到伺服器傳送的訊息 : "
                    + emp.ID + "," + emp.Name + "," + emp.Age;
            };
            socket.onclose = function ( event ) {
                alert( "連線已關閉... state :" + socket.readyState );
            };
            socket.onerror = function ( event ) {
                alert( "發生錯誤 : " + event.data );
            };
        }
    </script>
</head>
<body>
    <fieldset>
        <legend>Personal Data</legend>
        <label for = "Text1"> ID:</label>
        <input id = "Text1" type = "text" /> <br />
        <label for = "Text2"> Name:</label>
        <input id = "Text2" type = "text" /> <br />
        <label for = "Text3"> Age:</label>
        <input id = "Text3" type  ="text" /> <br />
    </fieldset>
    <input id = "Button1" type = "button" value = "Connect to Server" onclick = "openSocket()" />
    <div id = "result"></div>
</body>
</html>

 

伺服端若要送出JSON格式的資料,可以利用JavaScriptSerializer類別的Serialize方法,將物件序列化成JSON格式的資料來傳送,參考以下範例程式碼:

<%@ WebHandler Language = "C#" Class = "WSHandler" %>

using System;
using System.Web;
using System.Threading.Tasks;
using System.Web.WebSockets;
using System.Net.WebSockets;
using System.Text;
using System.Threading;

public class WSHandler : IHttpHandler
{

    public void ProcessRequest( HttpContext context )
    {
        if ( context.IsWebSocketRequest )
            context.AcceptWebSocketRequest( MyWebSocketHandler );
        else
            context.Response.StatusCode = 400; //Bad Request
    }
    public async Task MyWebSocketHandler( AspNetWebSocketContext context )
    {
        WebSocket socket = context.WebSocket;
        while ( true )
        {
            ArraySegment<byte> buffer = new ArraySegment<byte>( new byte [ 1024 ] );
            WebSocketReceiveResult r = await socket.ReceiveAsync( buffer , CancellationToken.None );

            if ( socket.State == WebSocketState.Open )
            {
                var json = Encoding.UTF8.GetString( buffer.Array , 0 , r.Count );
                var serializer = new System.Web.Script.Serialization.JavaScriptSerializer( );
                var emp = ( System.Collections.Generic.IDictionary<string , object> ) serializer.DeserializeObject( json );

                var obj = new
                {
                    ID = emp [ "ID" ] ,
                    Name = emp [ "Name" ] ,
                    Age = emp [ "Age" ]
                };
                serializer = new System.Web.Script.Serialization.JavaScriptSerializer( );
                var msg = serializer.Serialize( obj );

                buffer = new ArraySegment<byte>( Encoding.UTF8.GetBytes( msg ) );
                await socket.SendAsync( buffer , WebSocketMessageType.Text , true , CancellationToken.None );
            }
        }
    }
    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

 

此網頁範例執行的結果,請參考下圖所示:

clip_image014

圖 11:接收伺服端JSON格式資料範例。

 

傳送Canvas資料範例

用戶端若使用HTML5 的Canvas進行繪圖,若想要將繪製的結果上傳到伺服器,參考以下的範例將WebSocket物件的binaryType設定為「arraybuffer」,表示以ArrayBuffer來表示繪圖資料,binaryType的預設值是「blob」。接著利用getImageData,從(0,0),取出寬、高為10的繪圖資料,放到Uint8Array陣列之中,以便傳送到伺服器。在message事件中,則利用Canvas的putImageData方法,將伺服端傳回的資料繪製在下方的Canvas之中:

<!DOCTYPE html>
<html xmlns= "http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv = "Content-Type" content = "text/html; charset=utf-8" />
    <title> </title>
    <script type = "text/javascript">
        function openSocket( ) {
            var socket = new WebSocket( "ws://localhost:26462/WSHandler6.ashx" );
            socket.binaryType = 'arraybuffer';
            socket.onopen = function () {
                var canvas = document.getElementById('myCanvas');
                var context = canvas.getContext('2d');

                var image = context.getImageData( 0, 0, 10, 10 );
                var binArray = new Uint8Array( image.data.length );
                for ( var i = 0;  i < image.data.length;  i++ ) {
                    binArray[i] = image.data[i];
                }
                socket.send( binArray );
            };
            socket.onmessage = function ( event ) {
                var data = event.data;
                var binArray = new Uint8Array( data );
                var canvas = document.getElementById( "serverCanvas" ).getContext("2d");
                var image = canvas.createImageData( 10, 10 );
                for ( var i = 0; i < image.data.length;  i++ ) {
                    image.data[i] = binArray[i];
                }
                canvas.putImageData( image, 0, 0 );
            };
            socket.onclose = function ( event ) {
                alert( "連線已關閉... state :" + socket.readyState );
            };
            socket.onerror = function ( event ) {
                alert( "發生錯誤 : " + event.data );
            };
        }
    </script>
</head>
<body>
    <input id = "Button1" type = "button" value = "Connect to Server" onclick = "openSocket()" />
    <br />
    <canvas id = "myCanvas" style = "border: 1px solid" >
        <div>此瀏覽器不支援 canvas </div>
    </canvas>
    <script>
        var canvas = document.getElementById('myCanvas');
        var context = canvas.getContext('2d');
        context.fillStyle = "red";
        context.fillRect(0, 0, 10, 10);
        context.strokeStyle = "blue";
        context.arc(0, 0, 10, 10, 0.5 * Math.PI , true);
        context.stroke();
    </script>
    <canvas id = "serverCanvas" style = "border: 1px solid" >
        <div>此瀏覽器不支援 canvas </div>
    </canvas>
</body>
</html>

 

伺服端接收到繪圖資料之後,將資料直接送回用戶端:

 

<%@ WebHandler Language = "C#" Class = "WSHandler" %>

using System;
using System.Web;
using System.Threading.Tasks;
using System.Web.WebSockets;
using System.Net.WebSockets;
using System.Text;
using System.Threading;

public class WSHandler : IHttpHandler
{

    public void ProcessRequest( HttpContext context )
    {
        if ( context.IsWebSocketRequest )
            context.AcceptWebSocketRequest( MyWebSocketHandler );
        else
            context.Response.StatusCode = 400;
    }

    public async Task MyWebSocketHandler( AspNetWebSocketContext context )
    {
        WebSocket socket = context.WebSocket;
        while ( true )
        {
            ArraySegment<byte> buffer = new ArraySegment<byte>( new byte [ 1024 ] );
            WebSocketReceiveResult r = await socket.ReceiveAsync( buffer , CancellationToken.None );

            if ( socket.State == WebSocketState.Open )
            {
                await socket.SendAsync( buffer , WebSocketMessageType.Binary , true , CancellationToken.None );
            }
        }
    }
    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

 

注意,本文範例只處理小於1024位元組資料,若資料過多,您可以撰寫一個迴圈,從WebSocketReceiveResult的EndOfMessage屬性判斷是否還有其它資料需要接收,若EndOfMessage屬性為false,代表尚有資料,則可再叫用ReceiveAsync方法,接收後續的資料。此網頁範例執行的結果,請參考下圖所示:

clip_image015

圖 12:傳送Canvas繪圖資料範例。

Tags:

.NET Magazine國際中文電子雜誌 | HTML | 許薰尹Vivid Hsu

新增評論




  Country flag
biuquote
  • 評論
  • 線上預覽
Loading






NET Magazine國際中文電子雜誌

NET Magazine國際中文電子版雜誌,由恆逸資訊創立於2000,自發刊日起迄今已發行超過500篇.NET相關技術文章,擁有超過40000名註冊讀者群。NET Magazine國際中文電子版雜誌希望藉於電子雜誌與NET Developer達到共同學習與技術新知分享,歡迎每一位對.NET 技術有興趣的朋友們多多支持本雜誌,讓作者群們可以有持續性的動力繼續爬文。<請加入免費訂閱>

月分類Month List