สำหรับข้อเสนอแนะของฉันโปรดอ่านส่วนล่าสุด: “เมื่อใช้ SO_LINGER กับหมดเวลา 0”
ก่อนที่เราจะมาบรรยายเล็กน้อยเกี่ยวกับ:
- การสิ้นสุด TCP ปกติ
TIME_WAIT
FIN
, ACK
และRST
การสิ้นสุด TCP ปกติ
ลำดับการสิ้นสุด TCP ปกติมีลักษณะดังนี้ (แบบง่าย):
เรามีเพื่อนสองคน: A และ B
- โทร
close()
- A ส่ง
FIN
ไปยัง B
- A เข้าสู่
FIN_WAIT_1
สถานะ
- B ได้รับ
FIN
- B ส่ง
ACK
ให้ A
- B เข้าสู่
CLOSE_WAIT
สถานะ
- ได้รับ
ACK
- B โทร
close()
- B ส่ง
FIN
ให้ A
- B เข้าสู่
LAST_ACK
สถานะ
- ได้รับ
FIN
- A ส่ง
ACK
ไปยัง B
- A เข้าสู่
TIME_WAIT
สถานะ
- B ได้รับ
ACK
- B เข้าสู่
CLOSED
สถานะ - คือถูกลบออกจากตารางซ็อกเก็ต
TIME_WAIT
ดังนั้นเพียร์ที่เริ่มต้นการยุติ - เช่นการโทรclose()
ก่อน - จะจบลงในTIME_WAIT
สถานะ
เพื่อทำความเข้าใจว่าเหตุใดTIME_WAIT
รัฐจึงเป็นเพื่อนของเราโปรดอ่านหัวข้อ 2.7 ใน "UNIX Network Programming" ฉบับที่สามโดย Stevens et al (หน้า 43)
อย่างไรก็ตามอาจมีปัญหากับซ็อกเก็ตจำนวนมากในTIME_WAIT
สถานะบนเซิร์ฟเวอร์เนื่องจากอาจทำให้การเชื่อมต่อใหม่ไม่ได้รับการยอมรับ
เพื่อหลีกเลี่ยงปัญหานี้ผมได้เห็นหลายแนะนำการตั้งค่าตัวเลือกซ็อกเก็ต SO_LINGER กับหมดเวลา 0 close()
ก่อนที่จะเรียก อย่างไรก็ตามนี่เป็นวิธีแก้ปัญหาที่ไม่ดีเนื่องจากทำให้การเชื่อมต่อ TCP ถูกยกเลิกด้วยข้อผิดพลาด
ให้ออกแบบโปรโตคอลของแอปพลิเคชันแทนเพื่อให้การยุติการเชื่อมต่อเริ่มต้นจากฝั่งไคลเอ็นต์ หากไคลเอนต์รู้เสมอว่าเมื่อใดที่อ่านข้อมูลที่เหลือทั้งหมดก็สามารถเริ่มต้นลำดับการสิ้นสุดได้ ตัวอย่างเช่นเบราว์เซอร์รู้จากContent-Length
ส่วนหัว HTTP เมื่ออ่านข้อมูลทั้งหมดแล้วและสามารถเริ่มการปิดได้ (ฉันรู้ว่าใน HTTP 1.1 จะเปิดไว้สักพักเพื่อนำกลับมาใช้ใหม่ได้แล้วจึงปิด)
close()
ถ้าเซิร์ฟเวอร์จำเป็นต้องปิดการเชื่อมต่อการออกแบบโปรโตคอลประยุกต์ใช้เพื่อให้เซิร์ฟเวอร์ถามลูกค้าที่จะเรียกร้อง
เมื่อใดควรใช้ SO_LINGER กับการหมดเวลา 0
อีกครั้งตาม "UNIX Network Programming" รุ่นที่สามหน้า 202-203 การตั้งค่าSO_LINGER
ด้วยการหมดเวลา 0 ก่อนการโทรclose()
จะทำให้ลำดับการสิ้นสุดปกติไม่สามารถเริ่มต้นได้
ในทางกลับกันเพียร์ที่ตั้งค่าตัวเลือกนี้และการโทรclose()
จะส่งRST
(รีเซ็ตการเชื่อมต่อ) ซึ่งระบุเงื่อนไขข้อผิดพลาดและนี่คือวิธีที่จะรับรู้ในอีกด้านหนึ่ง โดยทั่วไปคุณจะเห็นข้อผิดพลาดเช่น "การเชื่อมต่อรีเซ็ตโดยเพียร์"
ดังนั้นในสถานการณ์ปกติจึงเป็นความคิดที่แย่มากที่จะตั้งค่าการSO_LINGER
หมดเวลาเป็น 0 ก่อนที่จะโทรclose()
- จากนี้ไปเรียกว่ายกเลิกการปิด - ในแอปพลิเคชันเซิร์ฟเวอร์
อย่างไรก็ตามสถานการณ์บางอย่างรับประกันว่าจะทำเช่นนั้น:
- หากไคลเอนต์ของแอปพลิเคชันเซิร์ฟเวอร์ของคุณทำงานผิดพลาด (หมดเวลาส่งคืนข้อมูลที่ไม่ถูกต้อง ฯลฯ ) การปิดที่ไม่ถูกต้องเหมาะสมที่จะหลีกเลี่ยงการติดขัด
CLOSE_WAIT
หรือสิ้นสุดในTIME_WAIT
สถานะ
- หากคุณต้องรีสตาร์ทแอ็พพลิเคชันเซิร์ฟเวอร์ของคุณซึ่งปัจจุบันมีการเชื่อมต่อไคลเอ็นต์หลายพันรายการคุณอาจพิจารณาตั้งค่าอ็อพชันซ็อกเก็ตนี้เพื่อหลีกเลี่ยงซ็อกเก็ตเซิร์ฟเวอร์นับพันใน
TIME_WAIT
(เมื่อเรียกclose()
จากเซิร์ฟเวอร์ปลายทาง) เนื่องจากอาจทำให้เซิร์ฟเวอร์ไม่สามารถรับพอร์ตที่พร้อมใช้งานสำหรับการเชื่อมต่อไคลเอ็นต์ใหม่ หลังจากเริ่มต้นใหม่
- ในหน้า 202 ในหนังสือดังกล่าวระบุไว้เป็นพิเศษว่า: "มีสถานการณ์บางอย่างที่รับประกันว่าจะใช้คุณลักษณะนี้เพื่อส่งการปิดที่ยกเลิกตัวอย่างหนึ่งคือเซิร์ฟเวอร์เทอร์มินัล RS-232 ซึ่งอาจหยุดทำงานตลอดไปในการ
CLOSE_WAIT
พยายามส่งข้อมูลไปยังเทอร์มินัลที่ติดขัด พอร์ต แต่จะรีเซ็ตพอร์ตที่ติดอย่างถูกต้องหากมีการRST
ทิ้งข้อมูลที่รอดำเนินการ "
ฉันจะแนะนำนี้บทความยาวซึ่งผมเชื่อว่าจะช่วยให้คำตอบที่ดีมากที่จะตอบคำถามของคุณ