จะบังคับให้ปิดซ็อกเก็ตได้อย่างไรใน TIME_WAIT


113

ฉันรันโปรแกรมเฉพาะบน linux ซึ่งบางครั้งก็ขัดข้อง ถ้าคุณเปิดมันอย่างรวดเร็วหลังจากนั้นมันจะฟังบนซ็อกเก็ต 49201 แทนที่จะเป็น 49200 เหมือนครั้งแรกที่มันทำ netstat เปิดเผยว่า 49200 อยู่ในสถานะ TIME_WAIT

มีโปรแกรมที่คุณสามารถเรียกใช้เพื่อบังคับให้ซ็อกเก็ตนั้นย้ายออกจากสถานะ TIME_WAIT ทันทีหรือไม่


1
หากคุณมาที่นี่เนื่องจากมี" TIME_WAITเซิร์ฟเวอร์มากเกินไป"เพียงข้ามคำตอบสามข้อแรกซึ่งหลีกเลี่ยงคำถามแทนที่จะตอบคำถาม
Pacerier

คำตอบ:


148
/etc/init.d/networking restart

ให้ฉันทำอย่างละเอียด Transmission Control Protocol (TCP) ได้รับการออกแบบให้เป็นแบบสองทิศทางสั่งและโปรโตคอลการส่งข้อมูลที่เชื่อถือได้ระหว่างสองจุดสิ้นสุด (โปรแกรม) ในบริบทนี้คำที่น่าเชื่อถือหมายความว่ามันจะส่งแพ็กเก็ตใหม่หากได้รับกลาง TCP รับประกันความน่าเชื่อถือโดยการส่งแพ็กเก็ต Acknowleduation (ACK) กลับไปสำหรับแพ็กเก็ตเดียวหรือหลายช่วงที่ได้รับจากเพียร์

สิ่งนี้จะเหมือนกันสำหรับสัญญาณควบคุมเช่นคำขอยกเลิก / ตอบกลับ RFC 793กำหนดสถานะ TIME-WAIT ให้เป็นดังนี้:

TIME-WAIT - หมายถึงการรอเวลามากพอที่จะผ่านเพื่อให้แน่ใจว่า TCP ระยะไกลได้รับการตอบรับการร้องขอการยกเลิกการเชื่อมต่อ

ดูแผนภาพสถานะ TCP ต่อไปนี้: ข้อความแสดงแทน

TCP เป็นโปรโตคอลการสื่อสารสองทิศทางดังนั้นเมื่อสร้างการเชื่อมต่อแล้วจะไม่มีความแตกต่างระหว่างไคลเอนต์และเซิร์ฟเวอร์ นอกจากนี้ผู้ใช้หนึ่งคนสามารถโทรออกจากการทำงานได้และเพื่อนทั้งคู่ต้องเห็นด้วยกับการปิดเพื่อปิดการเชื่อมต่อ TCP ที่สร้างไว้อย่างสมบูรณ์

ลองเรียกอันแรกเพื่อเรียกการออกจากการเป็นคนใกล้ชิดและคนอื่น ๆ ที่ใกล้ชิดเรื่อย ๆ เมื่อแอ็คทีฟเข้าใกล้ส่ง FIN สถานะจะไปที่ FIN-WAIT-1 จากนั้นจะได้รับ ACK สำหรับ FIN ที่ส่งและสถานะจะไปที่ FIN-WAIT-2 เมื่อได้รับ FIN จากการแฝงที่แฝงอยู่ใกล้ยิ่งขึ้นแอคทีฟก็จะส่ง ACK ไปที่ FIN และสถานะจะไปที่ TIME-WAIT ในกรณีที่ passive passive ไม่ได้รับ ACK ไปยัง FIN ที่สองมันจะทำการส่งแพ็กเก็ต FIN อีกครั้ง

RFC 793ตั้งค่า TIME-OUT ให้เป็นสองเท่าของอายุการใช้งานสูงสุดของเซ็กเมนต์หรือ 2MSL ตั้งแต่ MSL เวลาสูงสุดที่แพ็คเก็ตสามารถท่องอินเทอร์เน็ตได้คือ 2 นาทีและ 2MSL คือ 4 นาที เนื่องจากไม่มี ACK ไปยัง ACK การใช้งานที่ใกล้ชิดจึงไม่สามารถทำอะไรได้นอกจากรอ 4 นาทีหากปฏิบัติตามโปรโตคอล TCP / IP อย่างถูกต้องในกรณีที่ผู้ส่งแฝงไม่ได้รับ ACK ไปยัง FIN (ตามหลักเหตุผล) .

ในความเป็นจริงแพ็คเก็ตที่หายไปอาจเป็นของหายากและหายากมากถ้ามันเกิดขึ้นภายใน LAN หรือภายในเครื่องเดียว

หากต้องการตอบคำถามทุกข้อวิธีบังคับให้ปิดซ็อกเก็ตใน TIME_WAIT ได้อย่างไรฉันจะยังคงยึดคำตอบเดิมของฉัน:

/etc/init.d/networking restart

ในทางปฏิบัติฉันจะตั้งโปรแกรมเพื่อให้ละเว้นสถานะ TIME-WAIT โดยใช้ตัวเลือก SO_REUSEADDR ตามที่ระบุใน WMR SO_REUSEADDR ทำอะไรกันแน่

ตัวเลือกซ็อกเก็ตนี้จะบอกเคอร์เนลว่าแม้ว่าพอร์ตนี้จะไม่ว่าง (ใน
สถานะ TIME_WAIT) ให้ดำเนินการต่อและนำกลับมาใช้ใหม่ หากไม่ว่าง แต่มีสถานะอื่นคุณจะยังคงได้รับข้อผิดพลาดที่ใช้งานอยู่แล้ว มันจะมีประโยชน์ถ้าเซิร์ฟเวอร์ของคุณถูกปิดแล้วรีสตาร์ททันทีในขณะที่ซ็อกเก็ตยังคงใช้งานบนพอร์ตของมัน คุณควรทราบว่าหากมีข้อมูลที่ไม่คาดคิดเกิดขึ้นอาจทำให้เซิร์ฟเวอร์ของคุณสับสน แต่ในขณะนี้อาจเป็นไปได้ แต่ก็ไม่น่าเป็นไปได้


8
คำตอบที่ดี แต่ไม่ใช่คำตอบที่ถูกต้องสำหรับคำถามของเขา การรีสตาร์ทเครือข่ายจะใช้งานได้ แต่จากนั้นจะทำการรีบูตดังนั้นจึงไม่ถูกต้อง
Chris Huang-Leaver

3
@Chris Huang-Leaver คำถามคือ "มีโปรแกรมที่คุณสามารถเรียกใช้เพื่อบังคับให้ซ็อกเก็ตย้ายออกจากสถานะ TIME_WAIT ทันทีหรือไม่" หากการรีบูตอาจถือเป็นการใช้งานโปรแกรมก็จะเป็นคำตอบที่ถูกต้อง ทำไมคุณคิดว่าสิ่งนี้ไม่ถูกต้อง
Eugene Yokota

8
WMR มีคำตอบที่มีประโยชน์ที่สุด (ซึ่งเป็นสิ่งที่ฉันทำเมื่อฉันพบปัญหาประเภทนี้) การรีสตาร์ทเครือข่ายนั้นรุนแรงเกินไปที่จะแก้ปัญหาและอาจใช้เวลานานกว่าการรอหมดเวลาคำตอบที่ถูกต้องสำหรับคำถามของเขาคือ 'ไม่' แต่จะไม่ยอมให้คุณพิมพ์คำตอบสองตัวอักษร :-)
Chris Huang- Leaver

6
โอ้ไม่เป็นไรครั้งต่อไปที่กระบวนการบางอย่างจะหยุดทำงานกับ SIGTERM ฉันจะทุบคอมพิวเตอร์ของฉันแทนการแก้ไข
Longpoke

ลักษณะทั่วไปของสิ่งนี้คือ "เริ่มบริการเครือข่าย" ตำแหน่งเฉพาะ/etc/init.d/networkingเป็นแพลตฟอร์มเฉพาะ (Debian?) ดังนั้นบรรทัดคำสั่งที่แม่นยำจะแตกต่างกัน (บางครั้งค่อนข้างรุนแรงดังนั้น) สำหรับระบบอื่น ๆ ฉันเห็นด้วยกับผู้แสดงความคิดเห็นคนอื่นว่านี่เป็นเรื่องที่เกินความจำเป็นและเห็นได้ชัดว่าบริการเครือข่ายใด ๆ
tripleee

51

ฉันไม่ทราบว่าคุณมีซอร์สโค้ดของโปรแกรมเฉพาะที่คุณกำลังใช้งานอยู่หรือไม่ แต่ถ้าเป็นเช่นนั้นคุณสามารถตั้งค่า SO_REUSEADDR ผ่านทางsetsockopt(2)ซึ่งอนุญาตให้คุณผูกที่อยู่โลคอลเดียวกันแม้ว่าซ็อกเก็ตจะอยู่ในสถานะ TIME_WAIT (เว้นแต่ว่า ซ็อกเก็ตกำลังฟังดูsocket(7))

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับสถานะ TIME_WAIT ส่วนดูUnix ซ็อกเก็ตคำถามที่พบบ่อย


แต่ฉันไม่ได้รับข้อผิดพลาดที่ถูกผูกไว้แล้ว เมื่อฉันรันโปรแกรมอีกครั้งมันฟังในโพสต์ (123456) ฉันยังเห็นว่าระบบกำลังแสดง TIME_WAIT สำหรับพอร์ตนั้น แต่ก็ยังสามารถเชื่อมต่อได้ ทำไม?
Jayapal Chandran

2
แม้จะมี SO_REUSEADDR ก็ยังคงเป็นไปได้ที่จะได้รับข้อผิดพลาด "ที่มีอยู่แล้วในการใช้งาน" สำหรับรายละเอียดโปรดดูhea-www.harvard.edu/~fine/Tech/addrinuse.html
Jingguo Yao

@WMR SO_REUSEADDRไม่ได้ "ปิด" ซ็อกเก็ต มันช่วยให้คุณสามารถนำสิ่งที่เปิดอยู่ไปใช้ซ้ำได้ ดังนั้นคำถามยังคงเป็น "วิธีการบังคับให้ปิดซ็อกเก็ตได้TIME_WAITอย่างไร"
Pacerier

นี่คือคำตอบที่ถูกต้อง แต่คำถามนั้นไม่ถูกต้องทั้งหมด อย่างน้อยก็แก้ปัญหาของฉันได้ดี (ไม่เหมือนการรีสตาร์ทเครือข่ายทั้งหมดทำให้การเชื่อมต่ออื่น ๆ หมด)
V-Mark

SO_REUSEADDRจะให้bind()ดำเนินการต่อ; แต่ถ้าคุณต้องการฟังซ็อกเก็ตนั้นlisten()ก็จะกลับมาEADDRINUSEเหมือนเดิม คำตอบนี้อาจช่วยให้ซอฟต์แวร์ไคลเอนต์ใช้พอร์ตชั่วคราว แต่ไม่สามารถแก้ปัญหาสำหรับซอฟต์แวร์เซิร์ฟเวอร์ได้
Will

33

เท่าที่ฉันรู้ว่าไม่มีวิธีบังคับให้ปิดซ็อกเก็ตด้านนอกของการเขียนตัวจัดการสัญญาณที่ดีขึ้นในโปรแกรมของคุณ แต่มีไฟล์ / proc ซึ่งควบคุมระยะเวลาที่หมดเวลา ไฟล์นี้คือ

/proc/sys/net/ipv4/tcp_tw_recycle

และคุณสามารถตั้งค่าการหมดเวลาเป็น 1 วินาทีโดยทำดังนี้

echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle 

อย่างไรก็ตามหน้านี้มีคำเตือนเกี่ยวกับปัญหาความน่าเชื่อถือที่เป็นไปได้เมื่อตั้งค่าตัวแปรนี้

นอกจากนี้ยังมีไฟล์ที่เกี่ยวข้อง

/proc/sys/net/ipv4/tcp_tw_reuse

ซึ่งควบคุมว่าจะสามารถนำซ็อกเก็ต TIME_WAIT มาใช้ซ้ำได้หรือไม่ (สมมุติว่าไม่มีการหมดเวลา)

บังเอิญเอกสารประกอบเคอร์เนลเตือนคุณไม่ให้เปลี่ยนค่าใดค่าหนึ่งโดยไม่มี 'คำแนะนำ / คำขอจากผู้เชี่ยวชาญด้านเทคนิค' ซึ่งฉันไม่ได้

โปรแกรมต้องถูกเขียนเพื่อพยายามเชื่อมโยงกับพอร์ต 49200 และเพิ่มขึ้นทีละ 1 ถ้าพอร์ตนั้นถูกใช้งานอยู่ ดังนั้นหากคุณมีการควบคุมซอร์สโค้ดคุณสามารถเปลี่ยนพฤติกรรมนี้เพื่อรอสักครู่และลองอีกครั้งบนพอร์ตเดียวกันแทนที่จะเพิ่มขึ้น


คิดว่าสองตัวอย่างที่สองควรเป็น s / rw / tw / ฉันจะแก้ไข แต่ไม่มีตัวแทนเพียงพอ

1
นำมาจากเอกสารของเคอร์เนล: ข้อควรระวัง ทั้ง tcp_tw_recycle และ tcp_tw_reuse อาจทำให้เกิดปัญหา คุณไม่ควรเปิดใช้งานโดยไม่เข้าใจโทโพโลยีเครือข่ายในระหว่างโหนดที่ใช้หรือใช้งานโดยโหนดที่เปิดใช้งานพารามิเตอร์ การเชื่อมต่อที่ดำเนินการผ่านโหนดที่รับรู้สถานะการเชื่อมต่อ TCP เช่นไฟร์วอลล์ NAT หรือโหลดบาลานเซอร์อาจเริ่มวางเฟรมเนื่องจากการตั้งค่า ปัญหาจะปรากฏให้เห็นเมื่อมีการเชื่อมต่อจำนวนมากพอ

การตั้งค่าให้ใช้1งานได้สำหรับการเชื่อมต่อในอนาคต แต่สิ่งที่เกี่ยวกับคนปัจจุบันที่เปิดอยู่แล้ว?
Pacerier

18

ที่จริงมีวิธีที่จะฆ่าเชื่อมต่อได้ - killcx พวกเขาอ้างว่ามันใช้งานได้ในสถานะใด ๆ ของการเชื่อมต่อ (ซึ่งฉันไม่ได้ตรวจสอบ) คุณต้องรู้ว่าการสื่อสารเกิดขึ้นได้อย่างไรดูเหมือนว่าจะถือว่า eth0 เป็นค่าเริ่มต้น

UPDATE: วิธีอื่นคือตัดที่มาในที่เก็บบาง distros ลินุกซ์


3

อีกตัวเลือกหนึ่งคือการใช้ตัวเลือก SO_LINGER ด้วยการหมดเวลาเป็น 0 ด้วยวิธีนี้เมื่อคุณปิดซ็อกเก็ตถูกบังคับให้ปิดส่ง RST แทนการเข้าสู่พฤติกรรมการปิด FIN / ACK สิ่งนี้จะหลีกเลี่ยงสถานะ TIME_WAIT และอาจเหมาะสมกว่าสำหรับการใช้งานบางอย่าง


2
นอกจากนี้ยังสูญเสียข้อมูลขาออกใด ๆ ที่ยังอยู่ระหว่างการจัดส่งและอาจทำให้เกิดข้อผิดพลาดที่ปลายอีกด้าน ไม่แนะนำ.
user207421

@EJP ความล้มเหลวในช่วงต้นมักเป็นการโทรที่ถูกต้องเสมอ ระบบเครือข่ายไม่น่าเชื่อถือและการต่อสู้ที่จะทำให้ทุกอย่างช้าลง แอปที่เสียหายไม่สามารถสันนิษฐานได้ว่าข้อมูลใด ๆ ที่สร้างขึ้นอย่างปลอดภัย
Tobu

1
ที่จริงแล้วฉันขอแนะนำวันนี้เมื่อปลายทางอื่นเป็นรถบัสอุตสาหกรรมฝังตัวที่ใช้การขนส่งที่เชื่อถือได้ของชั้นแอพลิเคชันผ่าน TCP ซึ่งการขนส่งดังกล่าวป้องกันการเชื่อมต่อไม่เคยปิดเว้นแต่จะได้รับ RST และเติมเต็ม ขีด จำกัด การเชื่อมต่อบนเกตเวย์นั้น ที่นั่น ฉันให้ตัวอย่างที่เฉพาะเจาะจงและเป็นจริงมากแก่คุณที่น่าเศร้าต้องอาศัยการแฮ็กแบบนี้
andyn

@Tobu Networking ไม่น่าเชื่อถือ แต่ TCP พยายามทำสิ่งนั้นให้แย่ลงไม่ได้เป็นการสร้างสิ่งที่ดีกว่าและการให้ TCP ทำงานนั้นก็ไม่ได้เป็นการต่อสู้อะไรเลย
user207421

2

อีกทางเลือกหนึ่งคือการมีพร็อกซีหรือซอฟต์แวร์ส่งต่อพอร์ตที่เชื่อถือได้ซึ่งรับฟังพอร์ต 49200 จากนั้นส่งต่อการเชื่อมต่อไปยังหนึ่งในหลาย ๆ อินสแตนซ์ของโปรแกรมที่เชื่อถือได้น้อยกว่าโดยใช้พอร์ตที่แตกต่างกัน ... HAPROXY

พอร์ตที่คุณเชื่อมต่ออยู่ค่อนข้างสูง คุณสามารถลองใช้อันที่ไม่ได้ใช้ซึ่งอยู่เหนือช่วง 0-1024 ระบบของคุณมีโอกาสน้อยที่จะใช้หมายเลขพอร์ตที่ต่ำกว่าเป็นพอร์ตชั่วคราว


0

TIME_WAIT เป็นปัญหาที่พบบ่อยที่สุดในสถาปัตยกรรมเซิร์ฟเวอร์ไคลเอ็นต์ซ็อกเก็ตการเขียนโปรแกรม รอสักสองสามวินาทีการลองเป็นระยะ ๆ เป็นทางออกที่ดีที่สุด สำหรับแอปพลิเคชันตามเวลาจริงที่พวกเขาต้องการเซิร์ฟเวอร์จะต้องตื่นขึ้นมาทันทีมีตัวเลือก SO_REUSEADDR สำหรับพวกเขา

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.