สาเหตุที่การเชื่อมต่อของไคลเอนต์ล้มเหลว
การเชื่อมต่อของไคลเอนต์ล้มเหลวโดยทั่วไปจะมีข้อผิดพลาดสองประเภท ได้แก่ connection refuse และ connection timeout
connection refuse (การเชื่อมต่อถูกปฏิเสธ)
โดยทั่วไปมีสาเหตุดังต่อไปนี้:
- พอร์ตที่ไคลเอนต์เชื่อมต่อผิด
- ชื่อโดเมนหรือตัว IP ที่ไคลเอนต์เชื่อมต่อผิด
- หากไคลเอนต์ใช้ชื่อโดเมนในการเชื่อมต่อ ชื่อโดเมนอาจชี้ไปที่ IP ของเซิร์ฟเวอร์ที่ผิด
- เซิร์ฟเวอร์ใช้ CDN หรือพร็อกซีการเร่งความเร็ว ทำให้ IP ที่เชื่อมต่อจริงไม่ตรงกับ IP ที่คาดหวัง
- เซิร์ฟเวอร์ไม่ได้เริ่มทำงานหรือพอร์ตไม่ได้ถูกฟัง
- ใช้ซอฟต์แวร์พร็อกซีเครือข่าย
- IP ที่เซิร์ฟเวอร์ฟังและที่อยู่ที่เข้าถึงไม่อยู่ในช่วงที่อยู่เดียวกัน ตัวอย่างเช่น หากเซิร์ฟเวอร์ฟังที่ 127.0.0.1 ไคลเอนต์จะเชื่อมต่อได้เฉพาะผ่าน 127.0.0.1 เท่านั้น ไม่สามารถเชื่อมต่อผ่าน IP ของเครือข่ายท้องถิ่นหรือ IP ภายนอกได้ แนะนำให้ตั้งค่าที่อยู่ที่ฟังเป็น 0.0.0.0 ซึ่งจะทำให้สามารถเชื่อมต่อได้ทั้งจากเครื่องนี้ เครือข่ายท้องถิ่นและภายนอก
connection timeout (การเชื่อมต่อหมดเวลา)
โดยทั่วไปมีสาเหตุดังต่อไปนี้:
- ไฟร์วอลล์ของเซิร์ฟเวอร์ป้องกันการเชื่อมต่อ อาจปิดไฟร์วอลล์ชั่วคราวเพื่อทดสอบ
- หากเป็นเซิร์ฟเวอร์คลาวด์ กลุ่มความปลอดภัยอาจจะป้องกันการเชื่อมต่อ ต้องไปที่หลังบ้านการจัดการเพื่อเปิดพอร์ตที่เกี่ยวข้อง
- หากใช้แพลตฟอร์มเช่น Baota ต้องเปิดพอร์ตที่เกี่ยวข้องใน Baota
- เซิร์ฟเวอร์ไม่มีอยู๋หรือไม่ได้เริ่มทำงาน
- หากไคลเอนต์ใช้ชื่อโดเมนในการเชื่อมต่อ ชื่อโดเมนอาจชี้ไปที่ IP ของเซิร์ฟเวอร์ที่ผิด
- IP ที่ไคลเอนต์เข้าถึงเป็น IP ภายในของเซิร์ฟเวอร์ และไคลเอนต์และเซิร์ฟเวอร์ไม่อยู่ในเครือข่ายท้องถิ่นเดียวกัน
cannot assign requested address (ไม่สามารถจัดสรรที่อยู่ตามคำขอได้)
ในฐานะไคลเอนต์ สำหรับการเปิดการเชื่อมต่อแต่ละครั้งจะต้องใช้พอร์ตชั่วคราวในเครื่องท้องถิ่น เซิร์ฟเวอร์แต่ละเครื่องสามารถใช้พอร์ตชั่วคราวได้ประมาณ 20,000-30,000 หากจำนวนการเชื่อมต่อที่เริ่มไปยังเซิร์ฟเวอร์เฉพาะเกินกว่าค่านี้ จะไม่สามารถจัดสรรพอร์ตที่ใช้ได้และจะเกิดข้อผิดพลาดนี้ขึ้น
สามารถเพิ่มจำนวนพอร์ตชั่วคราวในเครื่องได้โดยการเปลี่ยนพารามิเตอร์เคอร์เนลใน /etc/sysctl.conf ที่ net.ipv4.ip_local_port_range เช่น ตั้งค่าเป็น 10000 65535 (การตั้งค่าช่วงพอร์ตในเครื่องเป็น 10000 ถึง 65535 ทำให้มีพอร์ตในเครื่องเพิ่มเป็น 55,535 พอร์ต) และรัน sysctl -p เพื่อให้มีผล
นอกจากนี้เมื่อการเชื่อมต่อถูกตัดการเชื่อมต่อแล้วจะอยู่ในสถานะ TIME_WAIT ซึ่งจะยังคงใช้พอร์ตที่เกี่ยวข้องในเครื่องอยู่ระยะหนึ่ง การเริ่มทำการเชื่อมต่อสั้นๆ จำนวนมาก (เกิน 20,000-30,000) ในเวลาอันสั้นก็จะเกิดข้อผิดพลาด Cannot assign requested address หากเป็นกรณีนี้สามารถตั้งค่าเคอร์เนลเพื่อรีไซเคิล TIME_WAIT ได้อย่างรวดเร็ว อ้างอิงจาก การปรับแต่งเคอร์เนล。
หมายเหตุ
ข้อจำกัดในการใช้พอร์ตในเครื่องนั้นจำกัดอยู่เฉพาะที่ไคลเอนต์ เซิร์ฟเวอร์ไม่มีข้อจำกัดในพอร์ตในเครื่อง ตราบใดที่มีทรัพยากรเพียงพอ เซิร์ฟเวอร์สามารถรักษาจำนวนการเชื่อมต่อได้เหมือนเป็นไม่จำกัด
ข้อผิดพลาดอื่น ๆ
หากเกิดข้อผิดพลาดที่ไม่ใช่ connection refuse และ connection timeout โดยทั่วไปจะมีสาเหตุดังนี้:
1. โปรโตคอลการสื่อสารที่ไคลเอนต์ใช้ไม่ตรงกับเซิร์ฟเวอร์
ตัวอย่างเช่น หากเซิร์ฟเวอร์ใช้งานโปรโตคอลการสื่อสาร http ในขณะที่ไคลเอนต์ใช้โปรโตคอลการสื่อสาร websocket จะไม่สามารถเชื่อมต่อได้ หากไคลเอนต์ใช้โปรโตคอล websocket เชื่อมต่อ เซิร์ฟเวอร์ก็ต้องเป็นโปรโตคอล websocket ด้วย หากเซิร์ฟเวอร์เป็นบริการโปรโตคอล http ไคลเอนต์จึงต้องเข้าถึงด้วยโปรโตคอล http
หลักการที่นี่คล้ายกับถ้าคุณต้องการสื่อสารกับชาวอังกฤษ คุณต้องใช้ภาษาอังกฤษ หากคุณต้องการสื่อสารกับชาวญี่ปุ่น คุณต้องใช้ภาษาญี่ปุ่น ที่นี่ภาษาก็เหมือนกับโปรโตคอลการสื่อสาร ทั้งสองฝ่าย (ไคลเอนต์และเซิร์ฟเวอร์) ต้องใช้ภาษาที่เหมือนกัน才能สื่อสาร มิฉะนั้นจะไม่สามารถสื่อสารได้
ข้อผิดพลาดทั่วไปที่เกิดจากโปรโตคอลการสื่อสารที่ไม่ตรงกัน ได้แก่:
WebSocket connection to 'ws://xxx.com:xx/' failed: Error during WebSocket handshake: Unexpected response code: xxx
WebSocket connection to 'ws://xxx.com:xx/' failed: Error during WebSocket handshake: net::ERR_INVALID_HTTP_RESPONSE
วิธีแก้ไข:
จากข้อผิดพลาดทั้งสองข้างต้น แสดงให้เห็นว่าไคลเอนต์ใช้การเชื่อมต่อ ws ซึ่งเป็นโปรโตคอล websocket เซิร์ฟเวอร์ก็ต้องเป็นโปรโตคอล websocket ด้วย โค้ดส่วนที่ฟังของเซิร์ฟเวอร์ต้องระบุโปรโตคอล websocket เพื่อให้สามารถสื่อสารได้ เช่น ด้านล่างนี้
หากเป็น gatewayWorker การฟังจะมีลักษณะมาตรฐาน
// โปรโตคอล websocket เพื่อให้ไคลเอนต์สามารถเชื่อมต่อด้วย ws://... ได้ xxxx เป็นพอร์ตที่ไม่ต้องแก้ไข
$gateway = new Gateway('websocket://0.0.0.0:xxxx');
หากเป็น Workerman จะเป็น
// โปรโตคอล websocket เพื่อให้ไคลเอนต์สามารถเชื่อมต่อด้วย ws://... ได้ xxxx เป็นพอร์ตที่ไม่ต้องแก้ไข
$worker = new Worker('websocket://0.0.0.0:xxxx');