การปิด WebSocket อย่างถูกต้อง (HTML5, Javascript)


127

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

มีพฤติกรรมแปลก ๆ เมื่อผู้ใช้รีเฟรชเพจโดยไม่โทรwebsocket.close()- เมื่อพวกเขากลับมาหลังจากรีเฟรชมันจะเข้าสู่websocket.oncloseเหตุการณ์

คำตอบ:


111

ตามข้อกำหนดโปรโตคอล v76 (ซึ่งเป็นเวอร์ชันที่เบราว์เซอร์ที่มีการสนับสนุนในปัจจุบันใช้งาน):

ในการปิดการเชื่อมต่ออย่างสมบูรณ์เฟรมที่ประกอบด้วยไบต์ 0xFF ตามด้วย 0x00 ไบต์จะถูกส่งจากเพียร์หนึ่งเพื่อขอให้เพียร์อีกคนปิดการเชื่อมต่อ

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

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

window.onbeforeunload = function() {
    websocket.onclose = function () {}; // disable onclose handler first
    websocket.close();
};

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


2
เป็นไปได้ว่าใน Firefox การเชื่อมต่อดูเหมือนจะค้างในการโหลดหน้าถัดไป ฉันไม่พบข้อมูลอ้างอิง แต่ฉันคิดว่าอาจมีข้อบกพร่องเกี่ยวกับเรื่องนี้ ความเป็นไปได้อื่น ๆ ก็คือoncloseเหตุการณ์ถูกทริกเกอร์โดยไม่คาดคิดหรืออาจจะเป็นไปตามวัตถุประสงค์เนื่องจากผู้ใช้นำทาง / เพจถูกโหลดซ้ำ ฉันเคยโพสต์คำถามที่ถามว่าพฤติกรรมที่คาดไว้ควรเป็นอย่างไรเบราว์เซอร์ใดที่ถูกต้องและเราใช้การเชื่อมต่ออัตโนมัติอีกครั้ง
leggetter

4
พิจารณาปัญหาเหล่านี้กับonbeforeunloadเหตุการณ์
artkoenig


35

สิ่งที่เกิดขึ้นคือมี WebSockets 2 เวอร์ชันหลักที่ใช้อยู่ในปัจจุบัน รุ่นเก่าที่ใช้[0x00][message][0xFF]โปรโตคอลและจากนั้นยังมีรุ่นใหม่ที่ใช้แพ็กเก็ต Hybi จัดรูปแบบ

Opera และ iPod / iPad / iPhones ใช้โปรโตคอลเวอร์ชันเก่าดังนั้นจึงเป็นเรื่องสำคัญที่จะต้องใช้ความเข้ากันได้แบบย้อนหลังในเซิร์ฟเวอร์ WebSockets ด้วยเบราว์เซอร์เหล่านี้ที่ใช้โปรโตคอลแบบเก่าฉันพบว่าการรีเฟรชเพจหรือการนำทางออกจากเพจหรือปิดเบราว์เซอร์ล้วนส่งผลให้เบราว์เซอร์ปิดการเชื่อมต่อโดยอัตโนมัติ ยิ่งใหญ่ !!

อย่างไรก็ตามสำหรับเบราว์เซอร์ที่ใช้โปรโตคอลเวอร์ชันใหม่ (เช่น Firefox, Chrome และสุดท้ายคือ IE10) การปิดเบราว์เซอร์เท่านั้นที่จะส่งผลให้เบราว์เซอร์ปิดการเชื่อมต่อโดยอัตโนมัติ กล่าวคือถ้าคุณรีเฟรชเพจหรือออกจากเพจเบราว์เซอร์จะไม่ปิดการเชื่อมต่อโดยอัตโนมัติ อย่างไรก็ตามสิ่งที่เบราว์เซอร์ทำคือส่งแพ็คเก็ต hybi ไปยังเซิร์ฟเวอร์โดยมีไบต์แรก (ตัวระบุโปรโต) อยู่0x88(รู้จักกันดีในชื่อเฟรมข้อมูลปิด) เมื่อเซิร์ฟเวอร์ได้รับแพ็กเก็ตนี้ก็สามารถบังคับปิดการเชื่อมต่อได้เองหากคุณเลือก


4
ตัวอย่างที่ใช้ได้จริงในฝั่งเซิร์ฟเวอร์จะจัดการกับ hybi packet ได้อย่างไร?
albanx

9
ดูเหมือนว่าบ้าที่ไม่ได้ปิดการเชื่อมต่อ ตัวแปร WebSocket จะถูกลบออกในการโหลดหน้าใหม่ดังนั้นเหตุใดการเชื่อมต่อจึงยังคงเปิดอยู่หากไม่สามารถเข้าถึงได้ การใช้การเชื่อมต่อซ้ำก็ไม่สมเหตุสมผลเช่นกัน
Triynko

@albanx ตรวจสอบคำตอบของฉันด้านล่าง
artkoenig

ตัวอย่างโค้ดใด ๆ เกี่ยวกับวิธีการส่งปิดเฟรมจากไคลเอนต์ websocket (เช่นเบราว์เซอร์) ฉันใช้โมดูล ws npm
Shaik Syed Ali

3

ตามที่theoobeกล่าวไว้เบราว์เซอร์บางตัวจะไม่ปิด websockets โดยอัตโนมัติ อย่าพยายามจัดการเหตุการณ์ "ปิดหน้าต่างเบราว์เซอร์" ใด ๆ ในฝั่งไคลเอ็นต์ ขณะนี้ยังไม่มีวิธีที่เชื่อถือได้หากคุณพิจารณาการสนับสนุนเบราว์เซอร์เดสก์ท็อปและเบราว์เซอร์มือถือหลัก ๆ(เช่นonbeforeunloadจะไม่ทำงานใน Mobile Safari) ฉันมีประสบการณ์ที่ดีในการจัดการปัญหานี้ทางฝั่งเซิร์ฟเวอร์ เช่นถ้าคุณใช้ Java EE ให้ดูที่javax.websocket.Endpointทั้งนี้ขึ้นอยู่กับเบราว์เซอร์ว่าจะเรียกOnCloseวิธีการหรือOnErrorวิธีใดหากคุณปิด / โหลดหน้าต่างเบราว์เซอร์ใหม่


1
ในกรณีของฉันการเชื่อมต่อกำลังถูกปิดระหว่างการถ่ายโอนข้อมูลทำงานได้ดีในกรณีของเบราว์เซอร์ตระกูล Opera และ iOS โปรดช่วยฉันด้วย .. ฉันกำลังต่อสู้กับปัญหานี้ตั้งแต่สองสัปดาห์ที่แล้ว stackoverflow.com/q/30799814/2225439
Mrug

2

โดยใช้วิธีปิดเว็บซ็อกเก็ตซึ่งคุณสามารถเขียนฟังก์ชันใดก็ได้ตามความต้องการ

var connection = new WebSocket('ws://127.0.0.1:1337');
    connection.onclose = () => {
            console.log('Web Socket Connection Closed');
        };
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.