ฉันรู้ว่ามีคำถาม SE ค่อนข้างน้อยเกี่ยวกับเรื่องนี้และฉันเชื่อว่าฉันอ่านมากเท่าที่มันสำคัญก่อนที่จะมาถึงจุดนี้
โดย "ฝั่งเซิร์ฟเวอร์TIME_WAIT
" ฉันหมายถึงสถานะของคู่ซ็อกเก็ตฝั่งเซิร์ฟเวอร์ที่มีการปิด () เริ่มต้นที่ฝั่งเซิร์ฟเวอร์
ฉันมักจะเห็นข้อความเหล่านี้ที่ขัดแย้งกับฉัน:
- ฝั่งเซิร์ฟเวอร์
TIME_WAIT
ไม่เป็นอันตราย - คุณควรออกแบบแอปเครือข่ายของคุณเพื่อให้ไคลเอนต์เริ่มต้นปิด () ดังนั้นให้ลูกค้ารับ
TIME_WAIT
เหตุผลที่ฉันพบข้อขัดแย้งนี้เป็นเพราะTIME_WAIT
ในไคลเอนต์อาจมีปัญหา - ลูกค้าสามารถเรียกใช้จากพอร์ตที่มีอยู่ดังนั้นในสาระสำคัญข้างต้นจะแนะนำให้ย้ายภาระของTIME_WAIT
ไปยังฝั่งไคลเอ็นต์ซึ่งอาจเป็นปัญหาจาก ฝั่งเซิร์ฟเวอร์ที่ไม่มีปัญหา
TIME_WAIT
แน่นอนว่าลูกค้าฝั่งนั้นมีปัญหาเฉพาะกรณีใช้งานในจำนวนที่ จำกัด โซลูชันไคลเอนต์เซิร์ฟเวอร์ส่วนใหญ่จะเกี่ยวข้องกับเซิร์ฟเวอร์เดียวและไคลเอนต์จำนวนมากลูกค้ามักจะไม่จัดการกับการเชื่อมต่อในปริมาณมากพอที่จะเป็นปัญหาและแม้ว่าพวกเขาจะทำมีคำแนะนำจำนวนมากให้ "sanely" ( เมื่อเทียบSO_LINGER
กับการหมดเวลา 0 หรือยุ่งกับ tcp_tw sysctls) ต่อสู้ฝั่งไคลเอ็นต์TIME_WAIT
โดยหลีกเลี่ยงการสร้างการเชื่อมต่อมากเกินไปเร็วเกินไป แต่นั่นไม่ได้เป็นไปได้เสมอตัวอย่างเช่นคลาสของแอปพลิเคชันเช่น:
- ระบบตรวจสอบ
- เครื่องกำเนิดไฟฟ้าโหลด
- ผู้รับมอบฉันทะ
ในอีกด้านฉันไม่เข้าใจด้วยซ้ำว่าฝั่งเซิร์ฟเวอร์TIME_WAIT
มีประโยชน์อย่างไร เหตุผลTIME_WAIT
ก็คือแม้จะมีเพราะมันช่วยป้องกันการฉีดTCP
ชิ้นส่วนเก่าค้างในกระแสที่พวกเขาไม่ได้เป็นของอีกต่อไป สำหรับฝั่งไคลเอ็นต์TIME_WAIT
มันสามารถทำได้โดยเพียงทำให้เป็นไปไม่ได้ที่จะสร้างการเชื่อมต่อกับip:port
คู่เดียวกันกับที่การเชื่อมต่อเก่านี้อาจมี (คู่ที่ใช้ถูกล็อคโดยTIME_WAIT
) แต่สำหรับฝั่งเซิร์ฟเวอร์สิ่งนี้ไม่สามารถป้องกันได้เนื่องจากที่อยู่ในท้องถิ่นจะมีพอร์ตที่ยอมรับและจะเหมือนกันเสมอและเซิร์ฟเวอร์ไม่สามารถ (AFAIK ฉันมีหลักฐานเชิงประจักษ์เท่านั้น) ปฏิเสธการเชื่อมต่อเพียงเพราะ เพียร์ที่เข้ามาจะสร้างคู่ที่อยู่เดียวกันที่มีอยู่แล้วในตารางซ็อกเก็ต
ฉันเขียนโปรแกรมที่แสดงว่าเซิร์ฟเวอร์จะไม่สนใจ TIME-WAIT ยิ่งกว่านั้นเนื่องจากการทดสอบเสร็จสิ้นใน 127.0.0.1 เคอร์เนลจะต้องมีบิตพิเศษที่จะบอกว่าเป็นฝั่งเซิร์ฟเวอร์หรือฝั่งไคลเอ็นต์ (เนื่องจากมิฉะนั้น tuple จะเหมือนกัน)
แหล่งที่มา: http://pastebin.com/5PWjkjEfทดสอบบน Fedora 22 แล้วค่าปริยายสุทธิ
$ gcc -o rtest rtest.c -lpthread
$ ./rtest 44400 s # will do server-side close
Will initiate server close
... iterates ~20 times successfully
^C
$ ss -a|grep 44400
tcp TIME-WAIT 0 0 127.0.0.1:44400 127.0.0.1:44401
$ ./rtest 44500 c # will do client-side close
Will initiate client close
... runs once and then
connecting...
connect: Cannot assign requested address
ดังนั้นสำหรับฝั่งเซิร์ฟเวอร์การTIME_WAIT
เชื่อมต่อในคู่พอร์ตเดียวกันที่แน่นอนสามารถสร้างขึ้นใหม่ได้ทันทีและสำเร็จและสำหรับฝั่งไคลเอ็นต์TIME-WAIT
ในการทำซ้ำครั้งที่สองconnect()
ล้มเหลวอย่างถูกต้อง
เพื่อสรุปคำถามคือสองเท่า:
- ฝั่งเซิร์ฟเวอร์
TIME_WAIT
ไม่ได้ทำอะไรจริง ๆ และเหลือเพียงเพราะวิธีRFC
นี้ใช่ไหม? - เหตุผลที่ลูกค้าแนะนำให้เริ่มต้นปิด () เพราะเซิร์ฟเวอร์
TIME_WAIT
ไม่มีประโยชน์หรือไม่?
TIME_WAIT
ครั้ง