socket.shutdown กับ socket.close


122

ฉันเพิ่งเห็นรหัสเล็กน้อยที่มีลักษณะเช่นนี้ (แน่นอนว่าถุงเท้าเป็นวัตถุซ็อกเก็ต):

sock.shutdown(socket.SHUT_RDWR)
sock.close()

จุดประสงค์ของการเรียกการปิดระบบบนซ็อกเก็ตแล้วปิดมันคืออะไร? หากสร้างความแตกต่างซ็อกเก็ตนี้จะถูกใช้สำหรับ IO ที่ไม่ปิดกั้น

คำตอบ:


38

นี่คือหนึ่งคำอธิบาย :

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


241

การโทรcloseและshutdownมีผลกระทบที่แตกต่างกันสองแบบในซ็อกเก็ต

สิ่งแรกที่ต้องชี้ให้เห็นก็คือซ็อกเก็ตเป็นทรัพยากรในระบบปฏิบัติการพื้นฐานและกระบวนการต่างๆสามารถมีจุดจับสำหรับซ็อกเก็ตพื้นฐานเดียวกันได้

เมื่อคุณเรียกcloseมันว่าการลดจำนวนแฮนเดิลลงทีละรายการและหากจำนวนแฮนเดิลถึงศูนย์ดังนั้นซ็อกเก็ตและการเชื่อมต่อที่เกี่ยวข้องจะดำเนินการตามขั้นตอนการปิดตามปกติ (ส่ง FIN / EOF ไปยังเพียร์อย่างมีประสิทธิภาพ) และซ็อกเก็ตจะถูกยกเลิกการจัดสรร

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

ในทางกลับกันการเรียกร้องshutdownให้อ่านและเขียนจะปิดการเชื่อมต่อพื้นฐานและส่ง FIN / EOF ไปยังเพียร์โดยไม่คำนึงถึงจำนวนกระบวนการที่จัดการกับซ็อกเก็ต อย่างไรก็ตามจะไม่ยกเลิกการจัดสรรซ็อกเก็ตและคุณยังต้องโทรปิดในภายหลัง


คำตอบที่ดีฉันไม่เคยใส่ใจที่จะหาว่าshutdown()ทำอะไร:)
Matt Joiner

2
การปิดระบบ () สำหรับการอ่านสามารถทำให้ส่งแพ็คเก็ต FIN ได้หรือไม่ คือปิด (sock_fd, 1);
ernesto

มันจะเป็นสมาร์ทที่จะเรียกเสมอ.shutdown()และในบรรทัดถัดไป.close()? หรือควรมีความล่าช้าในระหว่าง?
Luc

@Luc ขึ้นอยู่กับสิ่งที่คุณกำลังทำอยู่
Robert

2
@Luc จากนั้นเพียงแค่ปิดก็ใช้ได้ตราบเท่าที่ไม่มีกระบวนการอื่น ๆ ที่จับกับซ็อกเก็ต
Robert

17

คำอธิบายของการปิดและปิด: การปิดเครื่องอย่างสง่างาม (msdn)

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

การละเว้นการปิดเครื่องอาจทำให้ซ็อกเก็ตค้างอยู่ในสแต็ก OS จนกว่าการเชื่อมต่อจะถูกปิดอย่างสวยงาม

IMO ชื่อ 'ปิดระบบ' และ 'ปิด' ทำให้เข้าใจผิด 'ปิด' และ 'ทำลาย' จะเน้นความแตกต่าง


มันไม่ได้บ่งบอกถึงจุดสิ้นสุดว่าไม่มีความตั้งใจที่จะอ่านต่อไป การปิดเครื่องเพื่ออ่านจะไม่ส่งอะไรไปยังเครื่องเพียร์
Marquis of Lorne

7

ถูกกล่าวถึงใน Socket Programming HOWTO ( py2 / py3 )

ยกเลิกการเชื่อมต่อ

อย่างเคร่งครัดพูดที่คุณควรจะใช้shutdownในซ็อกเก็ตก่อนที่คุณจะcloseมัน shutdownเป็นที่ปรึกษาให้กับซ็อกเก็ตที่ปลายอื่น ๆ ขึ้นอยู่กับข้อโต้แย้งที่คุณส่งผ่านมันอาจหมายถึง“ ฉันจะไม่ส่งอีกต่อไป แต่ฉันจะยังฟัง ” หรือ“ ฉันไม่ฟังหรอกนะปริศนาดี! ” อย่างไรก็ตามไลบรารีซ็อกเก็ตส่วนใหญ่มักใช้กับโปรแกรมเมอร์ที่ละเลยที่จะใช้มารยาทนี้ซึ่งโดยปกติแล้ว a closeจะเหมือนกับshutdown(); close()ไฟล์. ดังนั้นในสถานการณ์ส่วนใหญ่จึงไม่จำเป็นต้องปิดระบบอย่างชัดเจน

...


ข้อมูลนี้ไม่ถูกต้อง จำเป็นเท่านั้นที่จะต้องปิดซ็อกเก็ตเพื่อเขียนหาก (1) คุณแยกกระบวนการและต้องการส่ง FIN ตอนนี้อย่างแน่นอนหรือ (2) คุณมีส่วนร่วมในโปรโตคอลอ่านต่อ EOS ซึ่งกันและกันเพื่อให้ทั้งสองคนปิด ในเวลาเดียวกัน. อย่างอื่นclose()ก็เพียงพอแล้ว เอกสาร Python ควรได้รับการแก้ไข
Marquis of Lorne

4

รหัสข้างบนนี้ไม่ผิดใช่ไหม

การโทรปิดโดยตรงหลังจากการเรียกปิดเครื่องอาจทำให้เคอร์เนลละทิ้งบัฟเฟอร์ขาออกทั้งหมดอยู่ดี

อ้างอิงจาก http://blog.netherlabs.nl/articles/2009/01/18/the-ultimate-so_linger-page-or-why-is-my-tcp-not-reliable ต้องรอระหว่างการปิดและ ปิดจนกว่าการอ่านจะส่งกลับ 0


ไม่ถูกต้อง. เคอร์เนลจะละทิ้งบัฟเฟอร์ขาออกหากการเชื่อมต่อถูกรีเซ็ตซึ่งอาจเกิดขึ้นได้หากแอปภายในเครื่องไม่ได้อ่านข้อมูลขาเข้าที่ค้างอยู่ทั้งหมดที่มาถึงแล้วหรือหากเพียร์ยุ่งกับ SO_LINGER ซึ่งไม่ควรทำ . ไม่จำเป็นต้องนอนหรือแม้แต่โทรปิดเครื่องก่อนปิด มีข้อมูลที่ผิดมากมายเกี่ยวกับเรื่องนี้
Marquis of Lorne

3

มีรสชาติของการปิดบางส่วน: http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.shutdown.aspx * nix คล้ายกัน


การปิดเครื่องมี 'รสชาติ' อย่างหนึ่งและมีสองทางเลือกซึ่งสามารถรวมกันได้ ไม่มีอะไรที่จะตอบคำถามจริง
Marquis of Lorne

1

ปิดเครื่อง (1) บังคับไม่ให้ซ็อกเก็ตส่งข้อมูลเพิ่มเติม

นี่เป็นประโยชน์ใน

1- การล้างบัฟเฟอร์

2- การตรวจจับข้อผิดพลาดแปลก ๆ

3- การป้องกันที่ปลอดภัย

ให้ฉันอธิบายเพิ่มเติมเมื่อคุณส่งข้อมูลจาก A ไป B ไม่รับประกันว่าจะถูกส่งไปยัง B แต่รับประกันว่าจะถูกส่งไปยังบัฟเฟอร์ A os เท่านั้นซึ่งจะส่งไปยังบัฟเฟอร์ B os

ดังนั้นด้วยการเรียก shutdown (1) บน A คุณจะล้างบัฟเฟอร์ของ A และข้อผิดพลาดจะเพิ่มขึ้นหากบัฟเฟอร์ไม่ว่างเปล่านั่นคือ: ข้อมูลยังไม่ถูกส่งไปยังเพียร์

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


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