WebSocket - ปิดการจับมือกอริลลา

ตัวอย่างจาก WebSocket RFC:

หากต้องการ เริ่ม WebSocket Closing Handshake ด้วยรหัสสถานะ (ส่วนที่ 7.4) /code/ และเหตุผลการปิดเพิ่มเติม (ส่วนที่ 7.1.6) /reason/ จุดสิ้นสุดจะต้องส่งเฟรมควบคุมการปิด ตามที่อธิบายไว้ ในส่วนที่ 5.5.1 ซึ่งมีรหัสสถานะตั้งเป็น /code/ และมีเหตุผลปิดตั้งเป็น /reason/ เมื่อจุดสิ้นสุดมีทั้งส่งและรับกรอบการควบคุมปิด จุดสิ้นสุดนั้นควร ปิดการเชื่อมต่อ WebSocket ตามที่กำหนดไว้ในส่วน 7.1.1

ฉันกำลังพยายามปิด Handshake โดยใช้แพ็คเกจ Gorilla WebSocket ด้วยรหัสต่อไปนี้:

เซิร์ฟเวอร์:

// Create upgrader function
conn, err := upgrader.Upgrade(w, r, nil)

// If there is an error stop everything.
if err != nil {
    fmt.Println(err)
    return
}

for {
    // Read Messages
    _, _, err := conn.ReadMessage()
    // Client is programmed to send a close frame immediately...
    // When reading close frame resend close frame with same
    // reason and code
    conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(1000, "woops"))
    fmt.Println(err)
    break
}

ลูกค้า:

d := &websocket.Dialer{}

conn, _, err := d.Dial("ws://localhost:8080", nil)

if err != nil {
    fmt.Println(err)
    return
}

go func() {
    for {
        // Read Messages
        _, _, err := conn.ReadMessage()

        if c, k := err.(*websocket.CloseError); k {
            if(c.Code == 1000) {
                // Never entering since c.Code == 1005
                fmt.Println(err)
                break
            }
        }
    }
}()

conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(1000, "woops"))

for {}

เซิร์ฟเวอร์กำลังอ่านเฟรมปิดตามที่คาดไว้ซึ่งจะแสดงผลลัพธ์ต่อไปนี้:

websocket: ปิด 1,000 (ปกติ): อุ๊ย

อย่างไรก็ตาม ลูกค้าก็เหมือนกับการหยุดอ่านเมื่อส่งข้อความปิดไปแล้ว ReadMessage ยังคงส่งคืนข้อผิดพลาด 1005 ฉันทำอะไรผิด


person Kim Byer    schedule 15.02.2016    source แหล่งที่มา


คำตอบ (1)


เซิร์ฟเวอร์ตอบสนองต่อเฟรมปิดด้วยโค้ด:

    c.WriteControl(CloseMessage, []byte{}, time.Now().Add(writeWait))

สิ่งนี้ถูกแปลเป็นรหัสปิด 1005 (ไม่ได้รับสถานะ) โดยลูกค้า

แอปพลิเคชันไคลเอ็นต์มองไม่เห็นเฟรมปิด 1,000 oops ที่เขียนโดยเซิร์ฟเวอร์ เนื่องจากการเชื่อมต่อ websocket หยุดอ่านจากเครือข่ายหลังจากได้รับเฟรมปิดแรก

แอปพลิเคชันไคลเอนต์ควรออกจากการวนซ้ำเมื่อมีการส่งคืนข้อผิดพลาดจาก ReadMessage ไม่จำเป็นต้องตรวจสอบรหัสปิดที่เฉพาะเจาะจง

for {
    // Read Messages
    _, _, err := conn.ReadMessage()
    if err != nil {
        break
    }
}

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

อีกทั้งไม่เกี่ยวข้องกับปัญหาในคำถาม ให้ใช้ select {} แทน for {} เพื่อบล็อก goroutine หลัก แบบแรกจะบล็อก goroutine หลังหมุนโดยใช้เวลา CPU

person Cerise Limón    schedule 15.02.2016
comment
ฉันรู้ว่าคำตอบนี้เก่า แต่ดูเหมือนว่าจะผิด: ใช้ select {} แทน for {} อดีตบล็อกตลอดไป หลังจะหมุนตลอดไป OP ไม่ได้อ่านจาก Chans นั่นคือสิ่งที่เลือกไว้ conn.ReadMessage() กำลังบล็อกอยู่แล้ว - person Artur Sapek; 17.10.2017
comment
@ArturSapek ความคิดเห็นอ้างอิงถึงการใช้ for {} ที่ส่วนท้ายของโค้ดของ OP โครงสร้าง for {} จะเบิร์นเวลา CPU select {} จะบล็อกง่ายๆ การเรียก conn.ReadMessage() ไม่ได้ปิดกั้น goroutine หลัก - person Cerise Limón; 17.10.2017