การสร้างตัวอย่าง WebSocket“ Hello World”


86

ฉันไม่เข้าใจว่าทำไมฉันจึงไม่สามารถทำให้โค้ดต่อไปนี้ใช้งานได้ ฉันต้องการเชื่อมต่อกับ JavaScript กับแอปพลิเคชันคอนโซลเซิร์ฟเวอร์ของฉัน จากนั้นส่งข้อมูลไปยังเซิร์ฟเวอร์

นี่คือรหัสเซิร์ฟเวอร์:

    static void Main(string[] args)
    {            
        TcpListener server = new TcpListener(IPAddress.Parse("127.0.0.1"), 9998);
        server.Start();
        var client = server.AcceptTcpClient();
        var stream = client.GetStream();

        while (true)
        {
            var buffer = new byte[1024]; 
            // wait for data to be received
            var bytesRead = stream.Read(buffer, 0, buffer.Length);                
            var r = System.Text.Encoding.UTF8.GetString(buffer);
            // write received data to the console
            Console.WriteLine(r.Substring(0, bytesRead));
        }
    }

และนี่คือ JavaScript:

        var ws = new WebSocket("ws://localhost:9998/service");
        ws.onopen = function () {
            ws.send("Hello World"); // I WANT TO SEND THIS MESSAGE TO THE SERVER!!!!!!!!
        };

        ws.onmessage = function (evt) {
            var received_msg = evt.data;
            alert("Message is received...");
        };
        ws.onclose = function () {
            // websocket is closed.
            alert("Connection is closed...");
        };

เมื่อฉันเรียกใช้รหัสนั้นสิ่งนี้จะเกิดขึ้น:

โปรดทราบว่าเมื่อฉันรัน JavaScript เซิร์ฟเวอร์ยอมรับและสร้างการเชื่อมต่อสำเร็จ JavaScript ไม่สามารถส่งข้อมูลได้ เมื่อใดก็ตามที่ฉันวางวิธีการส่งมันจะไม่ส่งแม้ว่าจะมีการเชื่อมต่อแล้วก็ตาม ฉันจะทำงานนี้ได้อย่างไร?


10
"คำถาม" นี้ดูเหมือนจะไม่เป็นคำถามอีกต่อไปดังนั้นจึงไม่เหมาะกับรูปแบบของ StackOverflow FWIW ข้อความของไคลเอ็นต์ไม่ได้เข้ารหัสมันถูกปิดบัง (ทำให้สับสน) โดย XOR เทียบกับค่าสุ่มที่ส่งเป็นส่วนหนึ่งของเฟรม รายละเอียดโปรโตคอลนี้มีไว้เพื่อหลีกเลี่ยงการโจมตีพร็อกซีเซิร์ฟเวอร์ที่เป็นพิษซึ่งอาจเข้าใจผิดในการรับส่ง
EricLaw

2
ขอบคุณคำตอบนี้มีประโยชน์มาก :) เดี๋ยวก่อนสิ่งเดียวคือ "static private string guid =" 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 ";" สิ่งที่คงที่เสมอ? ถ้าไม่ฉันจะหาค่าเหล่านี้ได้ที่ไหน?
Charmie

2
ฉันได้รับสิ่งนี้: "เซิร์ฟเวอร์ต้องไม่ปิดบังเฟรมใด ๆ ที่ส่งไปยังไคลเอนต์"
Charmie

6
คุณน่าจะทิ้งคำถามเดิมไว้เหมือนเดิม วัตถุประสงค์ของคำถามคือการนำเสนอปัญหาไม่ใช่วิธีแก้ปัญหา คำตอบตามที่คุณสามารถเดาได้มีไว้สำหรับการแก้ปัญหา
Kehlan Krumme

2
เหตุใด URL ของ WebSocket จึงลงท้ายด้วย '/ service' (ws: // localhost: 8080 / service) ทำไมไม่แค่ 'ws: // localhost: 8080' ล่ะ
andree

คำตอบ:


72

WebSockets เป็นโปรโตคอลที่อาศัยการเชื่อมต่อแบบสตรีม TCP แม้ว่า WebSockets จะเป็นโปรโตคอลที่ใช้ข้อความ

หากคุณต้องการที่จะใช้โปรโตคอลของคุณเองแล้วผมแนะนำให้ใช้ล่าสุดและข้อกำหนดที่มีเสถียรภาพ (สำหรับ 18/04/12) RFC 6455 ข้อกำหนดนี้ประกอบด้วยข้อมูลที่จำเป็นทั้งหมดเกี่ยวกับการจับมือและการจัดเฟรม เช่นเดียวกับคำอธิบายส่วนใหญ่เกี่ยวกับสถานการณ์การทำงานจากฝั่งเบราว์เซอร์และจากฝั่งเซิร์ฟเวอร์ ขอแนะนำอย่างยิ่งให้ปฏิบัติตามคำแนะนำที่บอกเกี่ยวกับฝั่งเซิร์ฟเวอร์ระหว่างการติดตั้งโค้ดของคุณ

พูดไม่กี่คำฉันจะอธิบายการทำงานกับ WebSockets ดังนี้:

  1. สร้างซ็อกเก็ตเซิร์ฟเวอร์ (System.Net.Sockets) ผูกเข้ากับพอร์ตเฉพาะและรับฟังการเชื่อมต่อแบบอะซิงโครนัส อะไรแบบนั้น:

    Socket serverSocket = Socket ใหม่ (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
    serverSocket.Bind (IPEndPoint ใหม่ (IPAddress.Any, 8080));
    serverSocket.Listen (128);
    serverSocketBeginAccept (null, 0, OnAccept, null);
  2. คุณควรมีการยอมรับฟังก์ชัน "OnAccept" ที่จะดำเนินการจับมือ ในอนาคตจะต้องอยู่ในเธรดอื่นหากระบบมีจุดประสงค์เพื่อรองรับการเชื่อมต่อจำนวนมากต่อวินาที

    โมฆะส่วนตัว OnAccept (ผล IAsyncResult) {
    ลอง {
        ไคลเอนต์ซ็อกเก็ต = null;
        ถ้า (serverSocket! = null && serverSocket.IsBound) {
            ไคลเอนต์ = serverSocket.EndAccept (ผลลัพธ์);
        }
        ถ้า (ไคลเอนต์! = null) {
            / * การจับมือและการจัดการ ClientSocket * /
        }
    } catch (ข้อยกเว้น SocketException) {
    
    } ในที่สุด {
        ถ้า (serverSocket! = null && serverSocket.IsBound) {
            serverSocketBeginAccept (null, 0, OnAccept, null);
        }
    }
    }
  3. หลังจากที่การเชื่อมต่อที่จัดตั้งขึ้นที่คุณต้องทำจับมือกัน ตามข้อกำหนด1.3 การเปิด Handshakeหลังจากสร้างการเชื่อมต่อแล้วคุณจะได้รับคำขอ HTTP พื้นฐานพร้อมข้อมูลบางอย่าง ตัวอย่าง:

    รับ / แชท HTTP / 1.1
    โฮสต์: server.example.com
    อัพเกรด: websocket
    การเชื่อมต่อ: อัพเกรด
    Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ ==
    ที่มา: http://example.com
    Sec-WebSocket-Protocol: แชท superchat
    Sec-WebSocket เวอร์ชัน: 13

    ตัวอย่างนี้เป็นไปตามเวอร์ชันของโปรโตคอล 13 โปรดทราบว่าเวอร์ชันเก่ามีความแตกต่างบางประการ แต่เวอร์ชันล่าสุดส่วนใหญ่สามารถทำงานร่วมกันได้ เบราว์เซอร์ต่างๆอาจส่งข้อมูลเพิ่มเติมให้คุณ ตัวอย่างเช่นรายละเอียดเบราว์เซอร์และระบบปฏิบัติการแคชและอื่น ๆ

    จากรายละเอียดการจับมือคุณต้องสร้างบรรทัดคำตอบโดยส่วนใหญ่จะเหมือนกัน แต่จะมี Accpet-Key ซึ่งอ้างอิงจาก Sec-WebSocket-Key ที่ให้มา ในข้อกำหนด 1.3 ได้อธิบายไว้อย่างชัดเจนถึงวิธีสร้างคีย์การตอบสนอง นี่คือฟังก์ชั่นของฉันที่ฉันใช้กับ V13:

    guid สตริงส่วนตัวแบบคงที่ = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
    สตริงส่วนตัว AcceptKey (คีย์สตริงอ้างอิง) {
        สตริง longKey = คีย์ + guid;
        SHA1 sha1 = SHA1CryptoServiceProvider.Create ();
        ไบต์ [] hashBytes = sha1.ComputeHash (System.Text.Encoding.ASCII.GetBytes (longKey));
        กลับ Convert ToBase64String (hashBytes);
    }
    

    คำตอบการจับมือมีลักษณะดังนี้:

    HTTP / 1.1 101 โปรโตคอลการสลับ
    อัพเกรด: websocket
    การเชื่อมต่อ: อัพเกรด
    Sec-WebSocket- ยอมรับ: s3pPLMBiTxaQ9kYGzzhZRbK + xOo =

    แต่ยอมรับคีย์จะต้องเป็นคีย์ที่สร้างขึ้นตามคีย์ที่ให้มาจากไคลเอนต์และวิธีการ AcceptKey ที่ฉันให้ไว้ก่อนหน้านี้ เช่นกันตรวจสอบให้แน่ใจว่าหลังจากอักขระสุดท้ายของคีย์ยอมรับคุณได้ใส่บรรทัดใหม่สองบรรทัด "\ r \ n \ r \ n"

  4. หลังจากจับมือคำตอบจะถูกส่งจากเซิร์ฟเวอร์ของลูกค้าควรเรียก " OnOpenฟังก์ชัน" นั่นหมายความว่าคุณสามารถส่งข้อความหลัง
  5. จะไม่ส่งข้อความในรูปแบบดิบ แต่พวกเขามีข้อมูลกรอบ และจากไคลเอนต์ไปยังเซิร์ฟเวอร์รวมทั้งใช้การกำบังสำหรับข้อมูลตาม 4 ไบต์ที่ให้ไว้ในส่วนหัวของข้อความ แม้ว่าจากเซิร์ฟเวอร์ไปยังไคลเอนต์คุณไม่จำเป็นต้องใช้การปิดบังข้อมูล อ่านหัวข้อที่5 การจัดกรอบข้อมูลในข้อกำหนด นี่คือการคัดลอกวางจากการใช้งานของฉันเอง ไม่ใช่โค้ดพร้อมใช้งานและต้องแก้ไขฉันกำลังโพสต์เพียงเพื่อให้แนวคิดและตรรกะโดยรวมของการอ่าน / เขียนด้วย WebSocket framing ไปที่ลิงค์นี้
  6. หลังจากติดตั้งเฟรมแล้วตรวจสอบให้แน่ใจว่าคุณได้รับข้อมูลอย่างถูกต้องโดยใช้ซ็อกเก็ต ตัวอย่างเช่นเพื่อป้องกันไม่ให้ข้อความบางส่วนถูกรวมเข้าด้วยกันเนื่องจาก TCP ยังคงเป็นโปรโตคอลที่ใช้สตรีม นั่นหมายความว่าคุณต้องอ่านจำนวนไบต์ที่เจาะจงเท่านั้น ความยาวของข้อความจะขึ้นอยู่กับส่วนหัวเสมอและให้รายละเอียดความยาวข้อมูลในส่วนหัวด้วยตนเอง ดังนั้นเมื่อคุณรับข้อมูลจาก Socket ให้รับ 2 ไบต์ก่อนรับรายละเอียดจากส่วนหัวตามข้อกำหนดของ Framing จากนั้นหากมาสก์ให้อีก 4 ไบต์และความยาวอาจเป็น 1, 4 หรือ 8 ไบต์ตามความยาวของข้อมูล และหลังจากข้อมูลด้วยตนเอง หลังจากที่คุณอ่านแล้วให้ใช้ demasking และข้อมูลข้อความของคุณก็พร้อมใช้งาน
  7. คุณอาจต้องการใช้Data Protocolฉันขอแนะนำให้ใช้ JSON เนื่องจากการจราจรประหยัดและใช้งานง่ายในฝั่งไคลเอ็นต์ใน JavaScript สำหรับฝั่งเซิร์ฟเวอร์คุณอาจต้องการตรวจสอบตัวแยกวิเคราะห์บางตัว มีจำนวนมาก Google มีประโยชน์มาก

การใช้โปรโตคอล WebSockets ของตัวเองนั้นมีประโยชน์และประสบการณ์ที่ยอดเยี่ยมที่คุณจะได้รับเช่นเดียวกับการควบคุมโปรโตคอลด้วยตนเอง แต่คุณต้องใช้เวลาพอสมควรและตรวจสอบให้แน่ใจว่าการนำไปใช้นั้นมีความน่าเชื่อถือสูง

ในเวลาเดียวกันคุณอาจได้ดูโซลูชันที่พร้อมใช้งานซึ่ง google (อีกครั้ง) มีเพียงพอ


ฉันเดาว่าฉันติดอยู่ในงานจับมือ เมื่อได้รับการเชื่อมต่อใหม่ฉันต้องส่งแฮช sha1 ของคีย์ยาวบวกคีย์สั้นให้กับลูกค้าใช่ไหม
Tono Nam

ฉันได้เพิ่มข้อมูลเพิ่มเติมในส่วนที่ 3 มันอธิบายรายละเอียดเพิ่มเติมเกี่ยวกับการจับมือจากฝั่งเซิร์ฟเวอร์
moka

1
รวมทั้งตรวจสอบให้แน่ใจว่าหากมีการร้องขอโปรโตคอลให้ใช้เดียวกันในการตอบสนองสำหรับสาย Sec-WebSocket-Protocol แต่ถ้ามีให้ตามคำขอ เช่นกันคุณไม่จำเป็นต้องมีเวอร์ชันสำหรับการตอบสนอง และเพิ่ม NewLine อีกอันต่อท้าย ส่งสตริงการตอบกลับทั้งหมดที่เข้ารหัสโดยใช้ UTF8: Encoding.UTF8.GetBytes (responseBytes)
moka

เราสนิทกัน. ขอบคุณมากสำหรับความช่วยเหลือ ฉันสามารถส่งข้อความได้แล้ว แต่ฉันเชื่อว่าข้อความถูกเข้ารหัส ดูการแก้ไขของฉันที่ฉันจะเริ่มทำงานในเร็ว ๆ นี้ ...
โตโน่นาม

คุณต้องใช้ data framing นี่จะเป็นเพราะฉันเชื่อว่าบิตที่ซับซ้อนที่สุดในการใช้งานโปรโตคอล WebSockets ฉันจะเพิ่มโค้ดคัดลอกวางจากการใช้งานของฉันลงในโพสต์ แต่อย่าลืมแก้ไขเพราะมันมีบางอย่างที่ต้องเปลี่ยนแปลง แต่โดยรวมแล้วให้แนวคิดและตรรกะในการทำงานกับเฟรม
moka

10

(โพสต์คำตอบในนามของสหกรณ์)

ฉันสามารถส่งข้อมูลได้แล้ว นี่เป็นโปรแกรมเวอร์ชันใหม่ของฉันขอบคุณสำหรับคำตอบและรหัสของ @Maksims Mihejevs

เซิร์ฟเวอร์

using System;
using System.Net.Sockets;
using System.Net;
using System.Security.Cryptography;
using System.Threading;

namespace ConsoleApplication1
{
    class Program
    {
        static Socket serverSocket = new Socket(AddressFamily.InterNetwork, 
        SocketType.Stream, ProtocolType.IP);
        static private string guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";

        static void Main(string[] args)
        {            
            serverSocket.Bind(new IPEndPoint(IPAddress.Any, 8080));
            serverSocket.Listen(128);
            serverSocket.BeginAccept(null, 0, OnAccept, null);            
            Console.Read();
        }

        private static void OnAccept(IAsyncResult result)
        {
            byte[] buffer = new byte[1024];
            try
            {
                Socket client = null;
                string headerResponse = "";
                if (serverSocket != null && serverSocket.IsBound)
                {
                    client = serverSocket.EndAccept(result);
                    var i = client.Receive(buffer);
                    headerResponse = (System.Text.Encoding.UTF8.GetString(buffer)).Substring(0,i);
                    // write received data to the console
                    Console.WriteLine(headerResponse);

                }
                if (client != null)
                {
                    /* Handshaking and managing ClientSocket */

                    var key = headerResponse.Replace("ey:", "`")
                              .Split('`')[1]                     // dGhlIHNhbXBsZSBub25jZQ== \r\n .......
                              .Replace("\r", "").Split('\n')[0]  // dGhlIHNhbXBsZSBub25jZQ==
                              .Trim();

                    // key should now equal dGhlIHNhbXBsZSBub25jZQ==
                    var test1 = AcceptKey(ref key);

                    var newLine = "\r\n";

                    var response = "HTTP/1.1 101 Switching Protocols" + newLine
                         + "Upgrade: websocket" + newLine
                         + "Connection: Upgrade" + newLine
                         + "Sec-WebSocket-Accept: " + test1 + newLine + newLine
                         //+ "Sec-WebSocket-Protocol: chat, superchat" + newLine
                         //+ "Sec-WebSocket-Version: 13" + newLine
                         ;

                    // which one should I use? none of them fires the onopen method
                    client.Send(System.Text.Encoding.UTF8.GetBytes(response));

                    var i = client.Receive(buffer); // wait for client to send a message

                    // once the message is received decode it in different formats
                    Console.WriteLine(Convert.ToBase64String(buffer).Substring(0, i));                    

                    Console.WriteLine("\n\nPress enter to send data to client");
                    Console.Read();

                    var subA = SubArray<byte>(buffer, 0, i);
                    client.Send(subA);
                    Thread.Sleep(10000);//wait for message to be send


                }
            }
            catch (SocketException exception)
            {
                throw exception;
            }
            finally
            {
                if (serverSocket != null && serverSocket.IsBound)
                {
                    serverSocket.BeginAccept(null, 0, OnAccept, null);
                }
            }
        }

        public static T[] SubArray<T>(T[] data, int index, int length)
        {
            T[] result = new T[length];
            Array.Copy(data, index, result, 0, length);
            return result;
        }

        private static string AcceptKey(ref string key)
        {
            string longKey = key + guid;
            byte[] hashBytes = ComputeHash(longKey);
            return Convert.ToBase64String(hashBytes);
        }

        static SHA1 sha1 = SHA1CryptoServiceProvider.Create();
        private static byte[] ComputeHash(string str)
        {
            return sha1.ComputeHash(System.Text.Encoding.ASCII.GetBytes(str));
        }
    }
}

JavaScript:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <script type="text/javascript">
        function connect() {
            var ws = new WebSocket("ws://localhost:8080/service");
            ws.onopen = function () {
                alert("About to send data");
                ws.send("Hello World"); // I WANT TO SEND THIS MESSAGE TO THE SERVER!!!!!!!!
                alert("Message sent!");
            };

            ws.onmessage = function (evt) {
                alert("About to receive data");
                var received_msg = evt.data;
                alert("Message received = "+received_msg);
            };
            ws.onclose = function () {
                // websocket is closed.
                alert("Connection is closed...");
            };
        };


    </script>
</head>
<body style="font-size:xx-large" >
    <div>
    <a href="#" onclick="connect()">Click here to start</a></div>
</body>
</html>

เมื่อฉันเรียกใช้รหัสนั้นฉันสามารถส่งและรับข้อมูลจากทั้งไคลเอนต์และเซิร์ฟเวอร์ ปัญหาเดียวคือข้อความถูกเข้ารหัสเมื่อมาถึงเซิร์ฟเวอร์ ขั้นตอนการทำงานของโปรแกรมมีดังนี้:

ใส่คำอธิบายภาพที่นี่

สังเกตว่าข้อความจากไคลเอนต์ถูกเข้ารหัสอย่างไร


5

WebSockets จะถูกนำมาใช้กับโปรโตคอลที่เกี่ยวข้องกับการจับมือกันระหว่างไคลเอ็นต์และเซิร์ฟเวอร์ ฉันไม่คิดว่าพวกเขาจะทำงานเหมือนกับซ็อกเก็ตทั่วไป อ่านโปรโตคอลและรับแอปพลิเคชันของคุณเพื่อพูดคุย หรือใช้ห้องสมุด WebSocket ที่มีอยู่หรือ .Net4.5beta ซึ่งมีWebSocket API


แน่นอนว่าพวกเขาทำงานเหมือนกับซ็อกเก็ตทั่วไป ซ็อกเก็ตอยู่ในระดับที่ต่ำกว่าโปรโตคอลของแอปพลิเคชันดังนั้นจึงไม่ได้ขึ้นอยู่กับมัน นั่นหมายความว่าคุณสามารถเรียกใช้ FTP, SMTP, HTTP, WebSockets ฯลฯ ... บนซ็อกเก็ต ขึ้นอยู่กับผู้ดำเนินการที่จะตรวจสอบให้แน่ใจว่าเธอปฏิบัติตามโปรโตคอลอย่างถูกต้องมิฉะนั้นจะไม่มีใครสามารถพูดคุยกับเซิร์ฟเวอร์ได้
SRM

5

ฉันไม่สามารถหาตัวอย่างการทำงานง่ายๆได้ทุกที่ (ณ วันที่ 19 มกราคม) ดังนั้นนี่คือเวอร์ชันอัปเดต ฉันมี Chrome เวอร์ชัน 71.0.3578.98

เซิร์ฟเวอร์ C # Websocket:

using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;

namespace WebSocketServer
{
    class Program
    {
    static Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
    static private string guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";

    static void Main(string[] args)
    {
        serverSocket.Bind(new IPEndPoint(IPAddress.Any, 8080));
        serverSocket.Listen(1); //just one socket
        serverSocket.BeginAccept(null, 0, OnAccept, null);
        Console.Read();
    }

    private static void OnAccept(IAsyncResult result)
    {
        byte[] buffer = new byte[1024];
        try
        {
            Socket client = null;
            string headerResponse = "";
            if (serverSocket != null && serverSocket.IsBound)
            {
                client = serverSocket.EndAccept(result);
                var i = client.Receive(buffer);
                headerResponse = (System.Text.Encoding.UTF8.GetString(buffer)).Substring(0, i);
                // write received data to the console
                Console.WriteLine(headerResponse);
                Console.WriteLine("=====================");
            }
            if (client != null)
            {
                /* Handshaking and managing ClientSocket */
                var key = headerResponse.Replace("ey:", "`")
                          .Split('`')[1]                     // dGhlIHNhbXBsZSBub25jZQ== \r\n .......
                          .Replace("\r", "").Split('\n')[0]  // dGhlIHNhbXBsZSBub25jZQ==
                          .Trim();

                // key should now equal dGhlIHNhbXBsZSBub25jZQ==
                var test1 = AcceptKey(ref key);

                var newLine = "\r\n";

                var response = "HTTP/1.1 101 Switching Protocols" + newLine
                     + "Upgrade: websocket" + newLine
                     + "Connection: Upgrade" + newLine
                     + "Sec-WebSocket-Accept: " + test1 + newLine + newLine
                     //+ "Sec-WebSocket-Protocol: chat, superchat" + newLine
                     //+ "Sec-WebSocket-Version: 13" + newLine
                     ;

                client.Send(System.Text.Encoding.UTF8.GetBytes(response));
                var i = client.Receive(buffer); // wait for client to send a message
                string browserSent = GetDecodedData(buffer, i);
                Console.WriteLine("BrowserSent: " + browserSent);

                Console.WriteLine("=====================");
                //now send message to client
                client.Send(GetFrameFromString("This is message from server to client."));
                System.Threading.Thread.Sleep(10000);//wait for message to be sent
            }
        }
        catch (SocketException exception)
        {
            throw exception;
        }
        finally
        {
            if (serverSocket != null && serverSocket.IsBound)
            {
                serverSocket.BeginAccept(null, 0, OnAccept, null);
            }
        }
    }

    public static T[] SubArray<T>(T[] data, int index, int length)
    {
        T[] result = new T[length];
        Array.Copy(data, index, result, 0, length);
        return result;
    }

    private static string AcceptKey(ref string key)
    {
        string longKey = key + guid;
        byte[] hashBytes = ComputeHash(longKey);
        return Convert.ToBase64String(hashBytes);
    }

    static SHA1 sha1 = SHA1CryptoServiceProvider.Create();
    private static byte[] ComputeHash(string str)
    {
        return sha1.ComputeHash(System.Text.Encoding.ASCII.GetBytes(str));
    }

    //Needed to decode frame
    public static string GetDecodedData(byte[] buffer, int length)
    {
        byte b = buffer[1];
        int dataLength = 0;
        int totalLength = 0;
        int keyIndex = 0;

        if (b - 128 <= 125)
        {
            dataLength = b - 128;
            keyIndex = 2;
            totalLength = dataLength + 6;
        }

        if (b - 128 == 126)
        {
            dataLength = BitConverter.ToInt16(new byte[] { buffer[3], buffer[2] }, 0);
            keyIndex = 4;
            totalLength = dataLength + 8;
        }

        if (b - 128 == 127)
        {
            dataLength = (int)BitConverter.ToInt64(new byte[] { buffer[9], buffer[8], buffer[7], buffer[6], buffer[5], buffer[4], buffer[3], buffer[2] }, 0);
            keyIndex = 10;
            totalLength = dataLength + 14;
        }

        if (totalLength > length)
            throw new Exception("The buffer length is small than the data length");

        byte[] key = new byte[] { buffer[keyIndex], buffer[keyIndex + 1], buffer[keyIndex + 2], buffer[keyIndex + 3] };

        int dataIndex = keyIndex + 4;
        int count = 0;
        for (int i = dataIndex; i < totalLength; i++)
        {
            buffer[i] = (byte)(buffer[i] ^ key[count % 4]);
            count++;
        }

        return Encoding.ASCII.GetString(buffer, dataIndex, dataLength);
    }

    //function to create  frames to send to client 
    /// <summary>
    /// Enum for opcode types
    /// </summary>
    public enum EOpcodeType
    {
        /* Denotes a continuation code */
        Fragment = 0,

        /* Denotes a text code */
        Text = 1,

        /* Denotes a binary code */
        Binary = 2,

        /* Denotes a closed connection */
        ClosedConnection = 8,

        /* Denotes a ping*/
        Ping = 9,

        /* Denotes a pong */
        Pong = 10
    }

    /// <summary>Gets an encoded websocket frame to send to a client from a string</summary>
    /// <param name="Message">The message to encode into the frame</param>
    /// <param name="Opcode">The opcode of the frame</param>
    /// <returns>Byte array in form of a websocket frame</returns>
    public static byte[] GetFrameFromString(string Message, EOpcodeType Opcode = EOpcodeType.Text)
    {
        byte[] response;
        byte[] bytesRaw = Encoding.Default.GetBytes(Message);
        byte[] frame = new byte[10];

        int indexStartRawData = -1;
        int length = bytesRaw.Length;

        frame[0] = (byte)(128 + (int)Opcode);
        if (length <= 125)
        {
            frame[1] = (byte)length;
            indexStartRawData = 2;
        }
        else if (length >= 126 && length <= 65535)
        {
            frame[1] = (byte)126;
            frame[2] = (byte)((length >> 8) & 255);
            frame[3] = (byte)(length & 255);
            indexStartRawData = 4;
        }
        else
        {
            frame[1] = (byte)127;
            frame[2] = (byte)((length >> 56) & 255);
            frame[3] = (byte)((length >> 48) & 255);
            frame[4] = (byte)((length >> 40) & 255);
            frame[5] = (byte)((length >> 32) & 255);
            frame[6] = (byte)((length >> 24) & 255);
            frame[7] = (byte)((length >> 16) & 255);
            frame[8] = (byte)((length >> 8) & 255);
            frame[9] = (byte)(length & 255);

            indexStartRawData = 10;
        }

        response = new byte[indexStartRawData + length];

        int i, reponseIdx = 0;

        //Add the frame bytes to the reponse
        for (i = 0; i < indexStartRawData; i++)
        {
            response[reponseIdx] = frame[i];
            reponseIdx++;
        }

        //Add the data bytes to the response
        for (i = 0; i < length; i++)
        {
            response[reponseIdx] = bytesRaw[i];
            reponseIdx++;
        }

        return response;
    }
}
}

ไคลเอนต์ html และ javascript:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <script type="text/javascript">
        var socket = new WebSocket('ws://localhost:8080/websession');
        socket.onopen = function() {
           // alert('handshake successfully established. May send data now...');
		   socket.send("Hi there from browser.");
        };
		socket.onmessage = function (evt) {
                //alert("About to receive data");
                var received_msg = evt.data;
                alert("Message received = "+received_msg);
            };
        socket.onclose = function() {
            alert('connection closed');
        };
    </script>
</head>
<body>
</body>
</html>


3

ปัญหา

เนื่องจากคุณใช้ WebSocket อะไรต่อมิอะไรจึงถูกต้อง หลังจากได้รับข้อมูลเบื้องต้นจาก WebSocket แล้วคุณจะต้องส่งข้อความจับมือจากเซิร์ฟเวอร์ C # ก่อนที่ข้อมูลเพิ่มเติมจะไหลได้

HTTP/1.1 101 Web Socket Protocol Handshake
Upgrade: websocket
Connection: Upgrade
WebSocket-Origin: example
WebSocket-Location: something.here
WebSocket-Protocol: 13

บางสิ่งบางอย่างตามแนวเหล่านั้น

คุณสามารถค้นคว้าเพิ่มเติมเกี่ยวกับการทำงานของ WebSocket บน w3 หรือ google

ลิงค์และแหล่งข้อมูล

นี่คือข้อกำหนดเฉพาะของโปรโตคอล: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76#section-1.3

รายการตัวอย่างการทำงาน:


นอกจากนี้ฉันกำลังใช้การเข้ารหัส UTF8 ฉันควรใช้อันอื่นเช่น ASCII หรือไม่
Tono Nam

@TonoNam: เท่าที่ฉันรู้การเข้ารหัส UTF8 นั้นถูกต้องแม้ว่าฉันจะไม่ใช่ผู้เชี่ยวชาญด้าน HTML5 ดังนั้นฉันก็ไม่รู้แน่ชัด
Caesay

ฉันทำให้มันทำงานกับซาฟารี !!! ฉันต้องการมันเพื่อให้มันใช้งานได้กับ google chrome ตัวอย่างทั้งหมดเชื่อมต่อกัน แต่ไม่มีตัวอย่างใดที่ส่งข้อมูลได้สำเร็จ ฉันจะพยายามต่อไป ขอบคุณมากสำหรับความช่วยเหลือ!
Tono Nam

แน่นอน .... ถ้าฉันยังไม่ได้ผลฉันจะทำให้คำถามนี้เป็นคำถามที่น่ายินดี! ฉันอยากรู้อยากเห็นจริงๆที่จะเห็นการทำงานนี้ ขอบคุณสำหรับความช่วยเหลือ
Tono Nam

ลิงก์ข้อมูลจำเพาะโปรโตคอลของคุณล้าสมัย Safari ยังคงใช้สิ่งนี้ แต่เบราว์เซอร์อื่น ๆ ได้ย้ายไปยังRFC 6455 ที่เข้ากันไม่ได้ นอกจากนี้แม้ว่าคุณจะแก้ไขการเชื่อมต่อครั้งแรกให้ถูกต้อง แต่ข้อความเพิ่มเติมก็ไม่ได้
simonc
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.