สาเหตุของการเชื่อมต่อของผู้ใช้ล้มเหลว
การเชื่อมต่อที่ล้มเหลวจะมีข้อผิดพลาดสองประการคือ connection refuse
และ connection timeout
connection refuse
(การปฏิเสธการเชื่อมต่อ)
โดยทั่วไปเป็นเพราะ:
- ผู้ใช้เชื่อมต่อไปยังพอร์ตผิด
- ผู้ใช้เชื่อมต่อไปยังโดเมนหรือไอพีผิด
- หากผู้ใช้เชื่อมต่อโดยใช้ชื่อโดเมน อาจมีโดเมนชี้ไปที่ไอพีเซิร์ฟเวอร์ที่ผิด
- เซิร์ฟเวอร์ใช้ CDN หรือโปรกฤส์เซอร์เพิ่มเร็ว ซึ่งมีผลให้ไอพีที่เชื่อมต่อและไอพีที่คาดหวังไม่ตรงกัน
- เซิร์ฟเวอร์ไม่ได้เริ่มทำงานหรือพอร์ตไม่ได้รับการตรวจสอบ
- ใช้ซอฟต์แวร์พร็อกซี่เครือข่าย
- ไอพีที่เซิร์ฟเวอร์ตรวจจับและที่ที่มีการเข้าถึงไม่ได้อยู่ในช่วงที่อนุญาต ตัวอย่างเช่น หากเซิร์ฟเวอร์ตรวจสอบ 127.0.0.1 ลูกค้าจึงสามารถเชื่อมต่อได้แค่ผ่านทาง 127.0.0.1 เท่านั้น ไม่สามารถเชื่อมต่อผ่านทางไอพีโฮสต์หรือไอพีภายนอก ขอแนะนำให้ตั้งค่าที่อยู่ที่ฟังและเป็น 0.0.0.0 เพื่อทำให้เครื่องที่อยู่ในเครือข่ายและเครื่องที่อยู่ภายนอกสามารถเชื่อมต่อได้
connection timeout
(การเชื่อมต่อหมดเวลา)
โดยทั่วไปเป็นเพราะ:
- ไฟร์วอลล์ของเซิร์ฟเวอร์ไปยังการเชื่อมต่อ คุณสามารถลองปิดไฟร์วอลล์ชั่วคราว
- หากเป็นเซิร์ฟเวอร์คลาวด์ กลุ่มความปลอดภัยอาจจะขัดข้องในการสร้างการเชื่อมต่อ ควรเปิดพอร์ตที่เหมาะสมในหน้าจอดูแลระบบ
- หากใช้การควบคุมเชิงกระดอน เช่น Control Panel จะต้องเปิดพอร์ตที่เหมาะสมใน Control Panel
- เซิร์ฟเวอร์ไม่มีหรือไม่ได้เริ่มต้น
- หากผู้ใช้เชื่อมต่อโดยใช้ชื่อโดเมน อาจมีโดเมนชี้ไปที่ไอพีเซิร์ฟเวอร์ที่ผิด
- ไอพีที่เชื่อมต่อของลูกค้าเป็นไอพีภายในเซิร์ฟเวอร์และไคพีเซิร์ฟเวอร์และลูกค้าไม่ได้อยู่ในเครือข่ายเดียวกัน
ไม่สามารถกำหนดที่อยู่ที่ร้องขอได้ (Cannot assign requested address)
ในบทบาทของลูกค้า ทุกครั้งที่ที่เริ่มต้นการเชื่อมต่อ จะต้องใช้พอร์ตชั่วคราวของเครื่องที่ตั้งเริ่มต้น ซึ่งเซิร์ฟเวอร์หนึ่งเครื่องมีจำนวนพอร์ตชั่วคราวที่สามารถใช้ประมาณ 2-3 หมื่น หากมีการเชื่อมต่อที่เริ่มต้นกับเซิร์ฟเวอร์ที่ระบุพอร์ตเกินค่าที่กำหนดจะเกิดข้อผิดพลาดนี้ คุณสามารถเพิ่มจำนวนพอร์ตชั่วคราวที่กำหนดได้ในไฟล์ etc/sysctl.conf
ในพารามิเตอร์ net.ipv4.ip_local_port_range
เช่นการตั้งค่าเป็น 10000 65535
(กำหนดระยะช่วงของพอร์ตชั่วคราวเป็น 10000 ถึง 65535 ดังนั้นจำนวนพอร์ตชั่วคราวเพิ่มเป็น 55535 ช่วง) และทำการรัน sysctl -p
เพื่อให้เกิดผล อย่างไรก็ตามหลังจากการเชื่อมต่อสิ้นสุดลง การเชื่อมต่อยังคงอยู่ในสถานะ TIME_WAIT ทำให้ยังคงใช้พอร์ตที่ร้องขอกับเครื่องที่ตั้งเป็นเวลา ดังนั้นการเชื่อมต่อที่มีจำนวนมากภายในเวลาสั้น (เกิน 2-3 หมื่น) ก็จะแสดงข้อผิดพลาด Cannot assign requested address
หากเป็นกรณีนี้คุณสามารถแก้ไขปัญหาโดยการตั้งค่าการรับซ้ำของ TIME_WAIT โดยอ้างอิงการปรับแต่งการทำงานของคำสั่ง
ข้อควรระวัง
การจำกัดจำนวนของพอร์ตชั่วคราวเฉพาะต่อลูกค้าเท่านั้น ซึ่งเซิร์ฟเวอร์ไม่จำกัดจำนวนของพอร์ตชั่วคราว ถ้าทรัพยากรเพียงพอ เซิร์ฟเวอร์สามารถรักษาจำนวนการเชื่อมต่อได้มากที่แทบจะไม่จำกัดเลย
ข้อผิดพลาดอื่น ๆ
หากเกิดข้อผิดพลาดที่ไม่ใช่ connection refuse
และ connection timeout
โดยทั่วไปเป็นเพราะ:
1. การใช้โปรโตคอลการสื่อสารของลูกค้าและเซิร์ฟเวอร์ไม่ตรงกัน
เช่น เซิร์ฟเวอร์เป็นโปรโตคอลการสื่อสารhttp ลูกค้าใช้โปรโตคอลการสื่อสารเว็บซ็อกเก็ตไม่สามารถเชื่อมต่อ ถ้าลูกค้าใช้โปรโตคอลการสื่อสารเว็บซ็อกเก็ตที่เชื่อมต่อ จึงเซิร์ฟเวอร์จำเป็นจะต้องเป็นโปรโตคอลการสื่อสารเว็บซ็อกเก็ต ถ้าเซิร์ฟเวอร์เป็นโปรโตคอลการสื่อสารโปรโตคอลการสื่อสารhttp ลูกค้าจำเป็นจะต้องใช้โปรโตคอลการสื่อสารhttp
หลักการที่แน่นอนนี้คือ เมื่อคุณต้องการพูดคุยกับคนอังกฤษ คุณต้องใช้ภาษาอังกฤษ หากต้องการพูดคุยกับคนญี่ปุ่น คุณต้องใช้ภาษาญี่ปุ่น การใช้งานโปรโตคอลสื่อสารนี้มีความคล้ายคลึงกันภาษา เจ้าของอุปกรณ์และเซิร์ฟเวอร์ต้องใช้โปรโตคอลสื่อสารร่วมกันเพื่อพูดคุย ไม่เช่นนั้นก็ไม่สามารถสื่อสารได้
ข้อผิดพลาดที่เกิดขึ้นเนื่องจากโปรโตคอลการสื่อสารที่ไม่ตรงกันประกอบด้วย:
การเชื่อมต่อ WebSocket ไปยัง 'ws://xxx.com:xx/' ล้มเหลว: ข้อผิดพลาดในระหว่างการเฮ็นดชุกของ WebSocket: รหัสการตอบกลับที่ไม่ได้คาดการณ์: xxx
การเชื่อมต่อ WebSocket ไปยัง 'ws://xxx.com:xx/' ล้มเหลว: ข้อผิดพลาดในระหว่างการเฮ็นดชุกของ WebSocket: net::ERR_INVALID_HTTP_RESPONSE
วิธีการแก้ไข:
จากข้อผิดพลาดสองข้อด้านบน จะเห็นว่าลูกค้าใช้การเชื่อมต่อเวบซ็อกเก็ตคือโปรโตคอลการสื่อสารเว็บซ็อกเก็ต เซิร์ฟเวอร์จำเป็นต้องเป็นโปรโตคอลการสื่อสารเว็บซ็อกเก็ต ส่วนที่เป็นการเชื่อมต่อของเซิร์ฟเวอร์ต้องระบุโปรโตคอลการสื่อสารเว็บซ็อกเก็ตเท่านั้น เช่นที่แสดงในส่วนของการตรวจสอบเช่นนี้
หากเป็นการทำงานแบบเกตเวย์เวิร์กเกตเวิร์คเกตฟังขั้นตอนดังนี้
// โปรโตคอลการสื่อสารเว็บซ็อกเก็ต รหัสถือพอร์ตได้แก่ xxxx
$gateway = new Gateway('websocket://0.0.0.0:xxxx');
ถ้าเป็น Workerman คือ
// โปรโตคอลการสื่อสารเว็บซ็อกเก็ต รหัสถือพอร์ตได้แก่ xxxx
$worker = new Worker('websocket://0.0.0.0:xxxx');