9.10. TCP – זרם אמין של בתים¶
TCP, ראשי תיבות של Transmission Control Protocol, הוא שירות שכבת התעבורה השני מעל IP. בעוד שאת UDP אפשר לתאר במיטבו כ“שלח חבילה וקווה לטוב“, TCP הוא ”פתח חיבור בין שתי נקודות קצה והתייחס אליו כאל צינור דו-כיווני של בתים שהצד השני בוודאות מקבל, לפי הסדר, ובדיוק פעם אחת“. רוב התעבורה באינטרנט משתמשת בו, וגם רוב מה שהמצלמה עושה ברשת משתמש בו.
9.10.1. מה TCP מוסיף ל-IP¶
TCP עושה הרבה יותר מ-UDP. הוא מוסיף:
חיבור. לפני שזורם מידע כלשהו, שתי נקודות הקצה מחליפות לחיצת יד קצרה כדי להסכים שהן משוחחות. לחיבור יש מצב – ”פתוח“, ”סגור-למחצה“, ”סגור“ – ששני הצדדים עוקבים אחריו.
מסירה אמינה. כל בית שהשולח מכניס מאושר על ידי המקבל. כל דבר שלא אושר בתוך פרק זמן מוגדר נשלח שוב. היישום לעולם לא רואה בית אבוד; הוא רואה השהיה בזמן שהפרוטוקול שולח מחדש.
מסירה לפי הסדר. בתים מגיעים באותו סדר שבו נשלחו. גם אם חבילות מגיעות אל המקבל מחוץ לסדר, TCP מסדר אותן מחדש לפני שהיישום קורא.
בקרת זרימה. אם המקבל איטי, השולח מתבקש להאט; החיבור מסתגל לקצב של הצד החלש יותר.
בקרת גודש. אם הרשת באמצע מתחילה להשליך חבילות, השולח נסוג עד שהמצב מתאושש. כך נמנע ממצב שבו חיבור בודד מפיל קישור רווי.
כל זה אוטומטי. ממשק ה-Python שהיישום משתמש בו הוא פשוט 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 מכל צד). כל קצה יכול לסגור את החצי שלו של החיבור באופן עצמאי; מצב סגור-למחצה שבו צד אחד סיים לשלוח אך השני עדיין משוחח הוא חוקי, אם כי אינו נפוץ. היישום בדרך כלל פשוט קורא ל-close() ומאפשר לפרוטוקול לטפל ברצף הסגירה.
9.10.3. מה TCP לא מבטיח¶
כמה דברים שלעיתים מניחים ש-TCP עושה אך אינו עושה:
גבולות הודעה. היישום שולח זרם של בתים, לא זרם של הודעות. שתי קריאות
send(b"hello")עשויות להגיע כ-recv()בודד שלb"hellohello", או כשתיrecv()s בגדלים משתנים. אם היישום רוצה מסגור הודעות, עליו להוסיף את המסגור בעצמו (תו שורה חדשה, קידומת אורך, וכל מה שיהיה). שליחת מסמכי JSON על גבי TCP, למשל, מצריכה הפרדה של כל מסמך בתו שורה חדשה או סמן אחר.הצפנה. TCP נושא את הבתים שהיישום נתן לו, בגלוי, לכל אורך הדרך. אם התוכן צריך להישאר חסוי, על היישום לעטוף את החיבור ב-TLS (ראו סוקטים מוצפנים ו-TLS).
אימות. TCP מוודא שהבתים הגיעו בשלמותם. הוא אינו אומר דבר על מי שלח אותם. גם אימות הוא עניין של שכבה גבוהה יותר.
חיוּת בחיבור שקט. אם אף צד לא שולח מידע במשך זמן רב, החיבור עדיין פתוח טכנית אך אינו יכול לזהות שהקצה השני קרס או נעלם. ניתן להפעיל בדיקות Keepalive בשקע כדי לתקן זאת כשזה חשוב.
9.10.4. מתי להשתמש ב-TCP¶
TCP הוא התשובה הנכונה כמעט לכל שיחה שמתאימה לתבנית ”client פותח חיבור אל server, הם מחליפים כמה בתים, החיבור נסגר כשמסתיים“. בקשות HTTP ו-HTTPS, הפעלות SSH, שאילתות מסד נתונים, העברות קבצים, העלאות תמונות – הכל על גבי TCP.
פנו ל-UDP רק כאשר השיחה אינה מתאימה לתבנית הזו: הודעות עצמאיות ומכילות-עצמן שבהן אובדן מקובל, תעבורת multicast, סנכרון זמן, חיפושי שמות, או מקרים רגישים-להשהיה באופן קיצוני שבהם עלות לחיצת היד אסורה.
כעת כשפורטים, UDP ו-TCP כולם על השולחן, סיפור שכבת התעבורה הושלם. ממשק ה-Python שחושף את שניהם נמצא בעמוד הבא: אובייקטי Socket.