עודכן לפני חודש
"Connection reset by peer." השגיאה שלא אומרת לך כמעט כלום. משהו השתבש. הצד השני ניתק. אבל איך הם ניתקו — בנימוס או בפתאומיות — משנה הכל לגבי מה שקרה ואיך לתקן.
ל-TCP יש שתי דרכים לסיים חיבור. FIN אומר "סיימתי לדבר, אבל אני עדיין מקשיב." RST אומר "השיחה הזו לא קרתה מעולם."
FIN: הפרידה המסודרת
כשסוגרים שקע בצורה רגילה, TCP מתחיל לחיצת יד בת ארבעה שלבים. שני הצדדים מסכימים באופן רשמי לסיים את השיחה:
- אתה שולח FIN: "אין לי יותר מה לומר."
- הם שולחים ACK: "הבנתי."
- הם שולחים FIN: "גם לי אין יותר מה לומר."
- אתה שולח ACK: "הבנתי. סיימנו."
למה ארבעה שלבים ולא שניים? כי TCP הוא דו-כיווני מלא (full-duplex) — נתונים זורמים באופן עצמאי בשני הכיוונים. כשאתה שולח FIN, אתה רק סוגר את הכיוון שלך. לצד השני אולי עוד יש נתונים לשלוח. הם מאשרים את ה-FIN שלך, מסיימים לשלוח את הנתונים שלהם, ואז שולחים FIN משלהם כשהם מוכנים.
החיבור נכנס למצב חצי-סגור בין שלבים 2 ו-3. אתה לא יכול יותר לשלוח, אבל עדיין יכול לקבל. כך TCP מתנהג בנימוס: גם אם סיימת לדבר, אתה ממתין לשמוע אם יש להם עוד מה לומר.
בפועל, שלבים 2 ו-3 מתמזגים לרוב לחבילה אחת (ACK+FIN ביחד), מה שגורם לזה להיראות כמו סגירה בשלושה שלבים. אבל ההיגיון נשאר: כל כיוון נסגר באופן עצמאי.
RST: הקו מת
RST שונה. כשחיבור מקבל RST, זה נגמר. מיד. ללא אישור, ללא לחיצת יד, ללא הזדמנות לסיים מה שהיית עושה. נתונים ממתינים? נעלמו. מאגרים? נוקו. החיבור חדל מלהתקיים.
TCP שולח RST במצבים ספציפיים:
אין מי שמקשיב. אתה מנסה להתחבר לפורט 8080, אבל אין שם שירות פעיל. מערכת ההפעלה מגיבה עם RST: "אין אף אחד בבית." כך סורקי פורטים מזהים פורטים סגורים — הם מקבלים RST במקום SYN-ACK.
החיבור לא קיים. חבילה מגיעה עבור חיבור שכבר נסגר, או שמעולם לא היה קיים במחשב הזה. RST אומר: "אני לא יודע על מה אתה מדבר."
האפליקציה קרסה. או ביטלה את החיבור במפורש. כשתוכנה מתה באופן בלתי צפוי, מערכת ההפעלה מנקה על ידי שליחת RST לכל החיבורים הפתוחים.
משאבים אזלו. המערכת לא יכולה עוד לתחזק את החיבור. RST הוא שסתום הלחץ החירומי.
למה "Connection Reset by Peer" רודף אותך
כשאתה רואה את השגיאה הזו, קיבלת RST. משהו בצד השני — או בין אתה לבין הצד השני — החליט שהחיבור הזה צריך למות מיד. החלק המתסכל: השגיאה לא אומרת לך מי שלח אותו או למה.
חומת-האש שמשקרת
חומות-אש לרוב שולחות חבילות RST תוך כדי התחזות לבאות מהיעד שלך. אתה חושב שהשרת דחה את החיבור שלך, אבל בעצם קופסת ביניים בלתי נראית חסמה אותך.
חומות-אש עם מצב (stateful) מתחזקות טבלאות של חיבורים פעילים. כשחיבור מפר מדיניות, פנוי יותר מדי זמן, או נראה חשוד, חומת-האש מזייפת RST מ"השרת" ושולחת אותו אליך. השרת מעולם לא ידע שאתה קיים.
לכידת חבילות מגלה את האמת. אם RST מגיע מ-IP שאינו היעד שלך, או שיש לו ערכי TTL שלא מתאימים לחבילות אחרות מאותו מארח, מצאת את קופסת הביניים החוסמת.
פסק הזמן של NAT: הרוצח השקט
התקני NAT (הנתב שלך, מאזני עומס בענן, חומות-אש ארגוניות) עוקבים אחרי חיבורים בטבלאות מצב. לטבלאות האלו יש פסקי זמן — בדרך כלל 4-6 דקות עבור ספקי ענן כמו AWS ו-Azure, אם כי ציוד ארגוני עשוי לאפשר 24 שעות או יותר1.
הנה המלכודת: האפליקציה שלך חושבת שהחיבור חי. השרת חושב שהחיבור חי. אבל התקן ה-NAT שביניהם שכח שהוא קיים. כשמי מהצדדים שולח את החבילה הבאה, ה-NAT לא מזהה אותה. בהתאם לתצורה, הוא או משמיט את החבילה בשקט או שולח RST.
זו הסיבה שחיבורים ארוכי-חיים — מאגרי מסד נתונים, תורי הודעות, סשנים של SSH — מתים באופן אקראי לאחר תקופות של חוסר פעילות. החיבור היה תקין. קופסת הביניים פשוט שכחה אותו.
מאזני עומס תחת לחץ
מאזני עומס שולחים RST כששרתי עורף נעלמים, בדיקות תקינות נכשלות, או מגבלות חיבור עוברות. האיפוסים האלו לרוב מתואמים עם פריסות (שרתי עורף מתחלפים) או קפיצות בתעבורה (עמידה במגבלות).
אם אתה רואה איפוסים מזדמנים שמתקבצים סביב זמנים ספציפיים, בדוק מה עוד קרה בתשתית שלך באותם רגעים.
דיבוג RST: איפה לחפש
לכידת חבילות משני הצדדים מגלה מי באמת שלח RST. השווה כתובות IP מקור, ערכי TTL, ותזמון. אם RST מגיע ממקום בלתי צפוי, מצאת את האשם.
יומני אפליקציה מראים אם התוכנה שלך ביטלה את החיבור בכוונה. אפליקציות רבות מתעדות סיבות לשליחת RST: הפרות פרוטוקול, מדיניות אבטחה, חריגה מפסק הזמן.
יומני חומת-אש ומאזן עומס חושפים הפרות מדיניות, מגבלות חיבור, וכשלים בבדיקות תקינות שמפעילים RST.
TCP keepalive מונע מוות מפסק זמן של NAT. הפעל keepalive וקבע מרווחים קצרים יותר מפסק הזמן הקצר ביותר בנתיב הרשת שלך. בדיקות ה-keepalive מאפסות את טיימר חוסר הפעילות לפני שקופסאות הביניים שוכחות אותך.
FIN לעומת RST: בחירת היציאה שלך
לעתים נדירות אתה בוחר במפורש. הטיפול בשקע של הקוד שלך קובע איזה מהם TCP ישתמש:
סגירה רגילה (קריאה ל-close() על שקע): TCP שולח FIN ומשלים את לחיצת היד בת ארבעת השלבים. נתונים בדרך מגיעים ליעדם. שני הצדדים מסכימים שהשיחה הסתיימה.
סגירה מבוטלת (הגדרת SO_LINGER לאפס לפני הסגירה): TCP שולח RST מיד. ללא לחיצת יד, ללא המתנה, ללא ערובה שנתונים ממתינים יגיעו.
השתמש בסגירה רגילה לכמעט כל דבר. שמור סגירה מבוטלת לחירום: חיבורים שהפכו לבלתי תקפים, משאבים שחייבים להשתחרר מיד, או הפרות אבטחה שזוהו שבהן אתה רוצה לסיים ללא משא ומתן.
שאלות נפוצות על TCP FIN ו-RST
למה אני מקבל "Connection reset by peer" כשהשרת נראה תקין?
האיפוס לרוב לא מגיע מהשרת כלל. חומות-אש, מאזני עומס, והתקני NAT שולחים חבילות RST שנראות כאילו באות מהיעד שלך. לכוד חבילות משני הצדדים — אם השרת מעולם לא שלח RST, משהו באמצע שלח.
איך אני מונע מפסק זמן של NAT להרוג את החיבורים הארוכים שלי?
הפעל TCP keepalive עם מרווח קצר יותר מפסק הזמן של התקן ה-NAT שלך. בסביבות ענן, זה לרוב אומר מרווחים של פחות מ-4 דקות. בדיקות ה-keepalive מאפסות את טיימר חוסר הפעילות, ומונעות מה-NAT לשכוח שהחיבור שלך קיים.
האם כדאי להשתמש ב-RST לסגירת חיבורים מהר יותר?
לא. RST לא מספק ערבויות מסירה — נתונים בדרך עלולים ללכת לאיבוד, והצד השני לא מקבל אזהרה. השתמש בסגירה רגילה (FIN) אלא אם אתה צריך ספציפית לבטל חיבור שהפך לבלתי תקף או מסוכן.
מה המשמעות כשסריקות פורט מראות פורט כ"סגור" לעומת "מסונן"?
פורטים סגורים מגיבים עם RST — מחסנית הרשת מקשיבה, אבל אין שירות הרשום לאותו פורט. פורטים מסוננים לא נותנים תגובה — חומת-אש שמטה את הבדיקה בשקט. שניהם אומרים שלא ניתן להתחבר, אבל הם מגלים תצורות רשת שונות.
מקורות
האם דף זה היה מועיל?