9.10. TCP -- สตรีมไบต์ที่เชื่อถือได้¶
TCP หรือ Transmission Control Protocol คือบริการเลเยอร์การส่งอีกประเภทหนึ่งที่ทำงานบน IP ถ้า UDP ถูกอธิบายได้ดีที่สุดว่า "ส่งแพ็กเก็ตแล้วหวังว่าจะถึง" TCP คือ "เปิดการเชื่อมต่อระหว่างสองจุดปลายทางแล้วถือว่าเป็นท่อไบต์สองทางที่อีกฝั่งได้รับแน่นอน เรียงลำดับ และได้รับเพียงครั้งเดียว" การสื่อสารทางอินเทอร์เน็ตส่วนใหญ่ใช้ TCP และงานเครือข่ายส่วนใหญ่ของ กล้อง ก็ใช้ TCP เช่นกัน
9.10.1. สิ่งที่ TCP เพิ่มเติมจาก IP¶
TCP ทำงานได้มากกว่า UDP มาก โดยเพิ่มสิ่งต่อไปนี้:
การเชื่อมต่อ ก่อนที่ข้อมูลใดจะไหลผ่าน สองจุดปลายทางจะแลกเปลี่ยนการจับมือกันสั้น ๆ เพื่อยืนยันว่ากำลังสื่อสารกัน การเชื่อมต่อมีสถานะ เช่น "เปิด" "ปิดครึ่งหนึ่ง" "ปิด" ซึ่งทั้งสองฝั่งติดตาม
การส่งที่เชื่อถือได้ ทุกไบต์ที่ผู้ส่งใส่เข้าไปจะได้รับการยืนยันจากผู้รับ สิ่งใดที่ไม่ได้รับการยืนยันภายในเวลาหมดอายุจะถูกส่งใหม่ แอปพลิเคชันจะไม่มีวันเห็นไบต์ที่สูญหาย แต่จะเห็นความล่าช้าขณะที่โปรโตคอลส่งใหม่
การส่งตามลำดับ ไบต์มาถึงในลำดับเดียวกับที่ส่ง แม้ว่าแพ็กเก็ตจะมาถึงผู้รับไม่เป็นลำดับ TCP จะเรียงลำดับใหม่ก่อนที่แอปพลิเคชันจะอ่าน
การควบคุมการไหล ถ้าผู้รับช้า ผู้ส่งจะถูกบอกให้ลดความเร็วลง การเชื่อมต่อจะปรับตัวตามอัตราของฝั่งที่อ่อนแอกว่า
การควบคุมความแออัด ถ้าเครือข่ายกลางเริ่มทิ้งแพ็กเก็ต ผู้ส่งจะถอยกลับจนกว่าสถานการณ์จะดีขึ้น ซึ่งป้องกันไม่ให้การเชื่อมต่อใดทำให้ลิงก์ที่อิ่มตัวล่ม
ทั้งหมดนี้เป็นอัตโนมัติ Python API ที่แอปพลิเคชันใช้คือเพียงแค่ send(bytes) และ recv(n) เท่านั้น TCP จัดการทุกอย่างที่เหลือภายใต้นั้น
9.10.2. การจับมือกัน¶
การเชื่อมต่อ TCP เปิดด้วยการแลกเปลี่ยนสามทางก่อนที่จะอนุญาตให้ข้อมูลใดผ่านได้:
การจับมือกันสามทาง เมื่อทั้งสองฝั่งยืนยันแล้ว การเชื่อมต่อจะเปิดและข้อมูลสามารถไหลได้¶
client ส่งแพ็กเก็ต SYN (synchronise) เพื่อขอเปิด server ตอบด้วย SYN-ACK (synchronise + acknowledge) เพื่อยอมรับ client ส่ง ACK สุดท้ายเพื่อยืนยัน หลังจากการส่งกลับมาครั้งนี้ ทั้งสองฝั่งเห็นด้วยว่าการเชื่อมต่อเปิดอยู่และได้ซิงโครไนซ์ตัวนับสำหรับติดตามไบต์ที่ส่งและรับแล้ว
การจับมือกันมีค่าใช้จ่ายหนึ่ง round-trip-time ของเวลาแฝงก่อนที่ไบต์แรกที่มีประโยชน์จะผ่าน สำหรับเครือข่ายท้องถิ่นนั้นคือหนึ่งมิลลิวินาที สำหรับการเชื่อมต่อข้ามทวีปนั้นประมาณ 100 ms นี่คือต้นทุนหลักของ TCP และเหตุผลที่ข้อความสั้นแบบครั้งเดียวบางครั้งทำงานได้ดีกว่าเมื่อใช้ UDP แทน
9.10.2.1. การจับมือกันเพื่อปิด¶
การเชื่อมต่อ TCP ยังปิดด้วยการแลกเปลี่ยน (โดย FIN จากแต่ละฝั่ง) ฝั่งใดก็ได้สามารถปิดครึ่งหนึ่งของการเชื่อมต่อได้อย่างอิสระ สถานะ half-closed ที่ฝั่งหนึ่งหยุดส่งแต่อีกฝั่งยังคงพูดอยู่นั้นถูกต้องตามกฎหมาย แม้จะไม่ค่อยพบเห็น แอปพลิเคชันปกติเพียงแค่เรียก close() และปล่อยให้โปรโตคอลจัดการลำดับการปิด
9.10.3. สิ่งที่ TCP ไม่ รับประกัน¶
สิ่งสองสามอย่างที่บางครั้งถูกสมมติว่า TCP ทำแต่ไม่ได้ทำ:
ขอบเขตของข้อความ แอปพลิเคชันส่ง สตรีมของไบต์ ไม่ใช่สตรีมของข้อความ การเรียก
send(b"hello")สองครั้งอาจมาถึงเป็นrecv()เดียวของb"hellohello"หรือเป็นrecv()สองครั้งที่มีขนาดต่างกัน ถ้าแอปพลิเคชันต้องการการกำหนดกรอบข้อความ ต้องเพิ่มการกำหนดกรอบเองเช่น newline prefix ความยาว หรืออะไรก็ได้ การส่งเอกสาร JSON ผ่าน TCP เช่น ต้องการแต่ละเอกสารที่คั่นด้วย newline หรือเครื่องหมายอื่นการเข้ารหัส TCP ส่งไบต์ที่แอปพลิเคชันให้มาในรูปแบบธรรมดาตลอดทาง ถ้าเนื้อหาต้องเป็นความลับ แอปพลิเคชันต้องห่อการเชื่อมต่อด้วย TLS (ดู ซ็อกเก็ตที่เข้ารหัสและ TLS)
การพิสูจน์ตัวตน TCP ตรวจสอบว่าไบต์มาถึงครบถ้วน แต่ไม่ได้บอกว่า ใคร ส่งมา การพิสูจน์ตัวตนเป็นเรื่องของเลเยอร์ที่สูงกว่า
ความมีชีวิตบนการเชื่อมต่อที่เงียบ ถ้าไม่มีฝั่งใดส่งข้อมูลเป็นเวลานาน การเชื่อมต่อยังเปิดอยู่ในทางเทคนิคแต่ไม่สามารถตรวจจับได้ว่าปลายอีกด้านล่มหรือหายไปแล้ว โพรบ Keepalive สามารถเปิดใช้งานบน socket เพื่อแก้ไขปัญหานี้เมื่อจำเป็น
9.10.4. เมื่อใดควรใช้ TCP¶
TCP เป็นคำตอบที่ถูกต้องสำหรับการสนทนาเกือบทุกประเภทที่มีรูปแบบ "client เปิดการเชื่อมต่อกับ server พวกเขาแลกเปลี่ยนไบต์บางส่วน การเชื่อมต่อปิดเมื่อเสร็จสิ้น" คำขอ HTTP และ HTTPS session SSH คำสั่ง database การถ่ายโอนไฟล์ การอัปโหลดภาพ -- ทั้งหมดผ่าน TCP
ใช้ UDP เฉพาะเมื่อการสนทนาไม่เข้ากับรูปแบบนั้น ได้แก่ ข้อความอิสระที่บรรจุในตัวเองซึ่งการสูญหายเป็นที่ยอมรับได้ การสื่อสารแบบ multicast การซิงโครไนซ์เวลา การค้นหาชื่อ หรือกรณีที่ไวต่อเวลาแฝงสูงมากที่ค่าใช้จ่ายของการจับมือกันนั้นสูงเกินไป
เมื่อมี port, UDP และ TCP ทั้งหมดอยู่ในมือ เรื่องราวของเลเยอร์การส่งก็จบแล้ว Python API ที่เปิดเผยทั้งสองอยู่ในหน้าถัดไป: อ็อบเจกต์ Socket