שימוש נכון ב-TCP וב - NIO

user32

Well-known member
מנהל
גם במשחקים

כששחקן עושה פעולות קצרות ומהירות: לחיצת עכבר וכו' הרבה פעמים עדיף הודעה של 2-3 בתים על פני פרוטוקול כמו HTTP. על הדרך אתה חוסך גם את כל שאר הקשקושים ששרתי HTTP עושים.

אם אתה מממש את הסרבר בעצמך אז מימוש מלא של HTTP הוא המון עבודה. מימוש חלקי זה שוב, פרוטוקול חדש ולא סטנדרטי ואז עוד פעם צריך לנסכרן עם הקליינט מה בדיוק הוא שולח ובאיזה פיצ'רים השרת תומך.
 
בשביל זה יש את websocket

בסוף הבעיות של כולם די דומות והגדירו פרוטוקולים כבר כמעט לכל סיטוצאיה.
 

user32

Well-known member
מנהל
אכן. כשאני חושב על זה זה היה בתקופה לפני שהמציאו את WS

 

choo

Active member
מעבר לעצה שתשתמשו בפרוטוקול קיים, חסרים נתונים כדי להחליט

&nbsp
בלי לדעת לאיזה קצב הודעות בשניה אתם מצפים ולאיזו כמות נתונים בשניה אתם מצפים (שני נתונים שונים) ואמינות השידור על התווך ביניכן (מבחינת רוחב פס, latency ו-packet drop שאמורים להיתמך), ולאחר מכן כמה זכרון אתם מוכנים להשקיע בשביל ניהול התקשורת - אי אפשר לתת תשובה טובה.
&nbsp
כשמדובר בפרוטוקול שאמור להעביר מעט מאוד נתונים והודעות בשניה ביחס לרוחב הפס הזמין בין הצדדים - עדיף ללכת על פשטות מימושית (ונראה שזה מה שהלקוח שלך הניח).
&nbsp
כשמדובר בפרוטוקול שצריך להעביר כמות גדולה (או שצפויה לגדול משמעותית) של נתונים והודעות - הגישה שלך יותר הגיונית, למרות שגם היא שגויה מבחינת צריכת זכרון (כשאין חסם ברור על כמות המידע שעשויה לשבת ב-socket, שני הצדדים עלולים להזדקק לכמות זכרון גדולה כדי לשמור הודעות שטרם טופלו. לרוב ניתן להתגבר על זה על ידי שימוש בשדות בתחילת ההודעה שמתארים את גודל ההמשך שלה, ומאפשרים לאפליקציה הקוראת לא לקרוא מעבר לקצה ההודעה הנוכחי (ועדיין יש מקרי קצה כמו מצב שבו בגלל באג הודעה נשלחה באופן חלקי - אבל רוצים שהקוד יוכל להתאושש מזה אוטומטית ולדלג להודעה הבאה).
&nbsp
בכל מקרה, בפרוטוקול מעל ל-tcp אתה אמור להיות מוכן לשגעונות של tcp - אבל זה לא אומר שאסור לך לממש התנהגות נוחה יותר לאפליקציה ממה ש-tcp נקי מציע. זה כל הרעיון של פרוטוקול אפליקטיבי מעל ל-tcp - אם הוא לא מוסיף תכונות חשובות לאפליקציה, למה צריך אותו? בבואך לתכנן פרוטוקול למטרות של אפליקציה ספציפית - תכנן אותו על פי דרישות האפליקציה, ולא רק על פי ההתנהגות הטבעית של tcp, כך שהוא יתווך ביניהן
 

ilgnd

New member
תשובה

מדובר על כמה עשרות MB בשנייה. כשגודל הודעה הוא KB בודדים עד 10MB (הייתה טעות בהודעה הראשית בה כתבתי 100MB).
הרשת אמורה להיות סדירה: רשת LAN פנימית בה כל המחשבים נמצאים באותו חדר.
&nbsp
לגבי האמירה שלך על צריכת הזיכרון:החסם של 15MB נקבע לאחר כמה ניסויים בהם גילינו שהמספר הזה מספק. הטיפול בהודעות לא מהווה בעיה באפליקציה שלנו: החוצץ לא מתמלא והתורים שומרים על גודל ממוצע של 0.
כמו כן, אחד השדות מכיל את גודל ההודעה, כך שכל אחד יכול לקרוא בדיוק את מספר הבתים שהוא צריך.
&nbsp
לבסוף, אציין שהעברת הודעות על TCP הוכתבה על ידי הלקוח ולא היה לנו מה לעשות בנדון.
 

user32

Well-known member
מנהל
מתאים יותר לFTP או HTTP

על כל פנים, אם הדרישות זה פרוטוקול משלכם אז זה מה יש.
 

choo

Active member
אם לא צפוי גידול מהותי - עדיף שתעשה מה שהלקוח מבקש

&nbsp
כשאין סיבה עסקית טובה לסבך את הפרוטוקול, ודרישות הביצועים כאלו נמוכות (כמה עשרות מגה בשניה זה כמה מאות מגה ביטים בשניה) וצפויות להשאר דומות בעתיד הנראה לעין - עדיף להשאיר פרוטוקול פשוט, ולא לשלם על פרוטוקול מורכב יותר בריבוי באגים.
&nbsp
אם וכאשר הדרישות תשתננה מהותית בעתיד, תוכלו לשפר את הפרוטוקול.
&nbsp
נ.ב. לא מזיק לתמוך בשליחת מספר גרסת פרוטוקול בזמן ההתחברות, כדי שניתן יהיה לבצע בעתיד upgrade של הפרוטוקול מבלי לאלץ עדכון אטומי בו זמני של שני הצדדים.
 

d70

Well-known member
...................
מסכים עם מי שאמר שעדיף להשתמש בפרוטוקול מוכן ולא להמציא את הגלגל.
אתם חייבים להסכים על worst case של התקשורת בינכם (גודל הודעות , קצב, וכו') ולהוכיח שהמגנון עובד.
לי "קצת" מפריע שהגדרתם באפר ציקלי של 15M שלא מסוגל להחזיק הודעה אחת(100MB).אני גם תוהה איך אתם שוברים ומרכיבים מחדש את ההודעה.הרי הכותרת עם ה magic number ,length וכו' תהיה רק בכתיבה הראשונה ל באפר (15 מגה).אם הכתיבה השנייה מכילה רק DATA של גוף ההודעה, אז אתם בבעיה.
איך הצד השני מסוגל לסדר מחדש הודעה באורך 100 מגה שמגיעה בקוונטות של 15 מגה ללא תווי בקרה? גם חייבים להוסיף מזהה למקור ההודעה (רשמת שמדובר על תקשורת שיכולה לבוא ממספר מקורות).
או שתרחיב את הבאפר ל 100 מגה (רצוי יותר), או שתגדירה גודל מקסימלי של הודעה עד 15M ותוסיפו שדות שיאפשרו לפרק ולבנות את ההודעות בצורה נורמלית.

לגבי הסיפור שהלקוח לא יודע להתמודד עם יותר מהודעה אחת בבאפר... נשמע לי מוזר.במצב רגיל במערכות תקשורת באפרים מכילים הודעות מרובות ,אז נשמע לי מוזר שהוא יכול לטפל רק בהודעה אחת.
אפשרות אחת:אתה נותן השהיה בין הודעה אחת לשנייה.
אפשרות שנייה:הלקוח שולח לך מתי הוא "פנוי" לקליטת הודעה חדשה.
שהלקוח יתחייב ויבין כמה השהייה תתוסף , ולא לשכוח להריץ ניסוי ושיבין את ההשלכות.
אפשרות שלישית:לעדכן את הפרוטוקול שלכם או פשוט לתת לשכבה תקשורת גבוהה יותר לטפל בסיפור.
 

ilgnd

New member
טעות בהודעה הראשונה:

גודל הודעה מגיע ל-10MB ולא ל-100 כפי שנכתב.
אנחנו לא שוברים ומרכיבים את ההודעה. כל הודעה נכתבת במלואה לחוצץ, ואנחנו לא כותבים חלקי הודעות. אנחנו יודעים ש30 הבתים הראשונים מכילים בין השאר את גודל ההודעה. ככה אנחנו קוראים 30 בתים, ואז קוראים את כל שאר הבתים הנדרשים.
&nbsp
ההודעות לא מגיעות מכמה מקורות שונים, אבל את ההודעות שאני שולח אני יכול לשלוח לכמה לקוחות שונים. זו הסיבה שאני שומר את כולן בחוצץ שממנו אני כותב לרשת.
&nbsp
בעיקרון אין לנו השהיות בהודעות מלבד ההשהייה הטבעית ברגעים מסויימים אין לנו מה לשלוח, והשהיות ברשת שלא אמורות לקרות ב-LAN שמוקדש רק לנו.
חשוב להבהיר שההודעות מסודרות בחוצץ: הן לא מחולקות ולא שזורות אחת בשנייה.
&nbsp
&nbsp
 

d70

Well-known member
אם אתם לא שוברים הודעות,למה בצד לקוח יש שברי הודעות?
מקריאה נוספת של ההודעה הראשונה, הלקוח מניח הנחות לא נכונות:
- "הלקוח מניח שאנחנו שולחים הודעה אחת כל פעם, ולכן קורא כל מה שיש לי לשלוח. אם הוא מקבל חצי הודעה זה לא נורא כי הוא יודע שהוא יחכה לשאר ההודעה."
אתה מבין שההנחה הזו סותרת את הטענה שהלקוח אמור להתמודד עם ריבוי מקורות? תשאלו את עצמכם מה קורה אם שלחת חלק ראשון של הודעה ממקור א, ובזמן ההמתנה לחלק שני,מקור ב' שולח חלק ראשון עם הודעה משלו........
אתם חייבים למצוא מנגנון סינכרון.תנסו לשקול עבודה ב master-slave.
ה master יהיה הלקוח.רק כאשר הוא פונה אליך אתה תשלח הודעה אחת ותחכה לאישור נוסף לשליחת הודעה חדשה.זה יפתור את הבעייה המקורית שתיארת, פלוס הבעייה שהוספתי בהודעה הנוכחית.
 

ilgnd

New member
אחדד

אין אצלנו מצב של מקור א' ומקור ב': אני המקור היחיד ואני שולח את ההודעת בסדר הנכון: אני לא שולח הודעה, עד שהודעה קודמת סיימה להישלח.
גם בכיוון ההפוך אין בעיה מפני שאני יודע להתמודד עם כל תקלה בצד של השולח.
&nbsp
כל הודעה שאני מייצר עוברת סריאליזציה לבתים, ואז אני מכניס אותה לחוצץ. החוצץ מחזיק מצביע למקום האחרון ממנו קראתי אל תוך ה - socket ומצביע למקום האחרון בו יש מידע, כך שאני אכתוב ל - socket לכל היותר מהמצביע הראשון עד לשני.
ברגע שאני מקבל write-ready מה - socket, אני כותב מידע ל - socket החל מהמצביע הראשון ואז מקדם אותו על פי גודל המידע שאכן נכתב. בזמן הזה, כמובן יכול להיות שהודעות יתווספו והמצביע השני גם יתקדם.
מכאן ברור, שהחוצץ עצמו לא מכיר את המושג "הודעה" וכך גם הכתיבה. אני כותב את כל הבתים שאני יכול לכתוב ברגע נתון, וכמובן שיש סיכוי שאשלח הודעה שלמה, חלקי הודעה וכו'.
&nbsp
הלקוח מצידו מצפה שאני אשלח את ההודעות בצורה סינכרונית: אכתוב את כל ההודעה לתוך ה - socket ורק לאחר שסיימתי, אעבור להודעה הבאה.
כשהוא מקבל יותר בתים ממה שהוא ציפה להם, הוא פשוט זורק את כל מה ש"לא צריך".
אם הוא מקבל פחות בתים ממה שהוא ציפה להם, הוא יודע להמתין לבתים הבאים.
&nbsp
כך נוצר תרחיש לדוגמה:
1. כתבתי לחוצץ הודעה בגודל 100 בתים.
2. כתבתי לחוצץ הודעה שניה בגודל 100 בתים.
3. כעת המצביע start מצביע על 0 והמצביע end מצביע על 200.
4. קיבלתי write-ready מה - channel.
5. כתבתי 150 בתים (הודעה וחצי). כרגע המצביע start מצביע על 150.
6. הלקוח קרא 150 בתים. את ה-100 הראשונים הפך להודעה ואת ה-50 האחרים זרק.
7. מרגע זה הלקוח יזרוק את כל ההודעות מפני שהן תמיד יתחילו בזבל.
 

Miki Watts

New member
דרך אגב, שים לב שב TCP אין הבטחה שמה שאתה שולח

יגיעו באותה צורה לצד השני, הוא יכול להרכיב ולפצל את הבייטים לפקטים שונים או ה router שזה עובר דרכו יכול להחליט את זה, על פי הגדרות MTU ועוד. כך שיכול להיות שאתה שולח 1000 בייטים ברצף, אבל זה מגיע לצד השני בתור 3 פקטים.
גם אין שום הבטחה שאותו פקט שמגיע לצד השני יגיע בתור בפר אחד שלם, יכול להיות מצב שממך יצא פקט אחד של 1000 בייט, הצד השני קיבל את זה בתור פקט אחד, אבל כאשר הוא קורא לקריאה של הבפר, יש לו רק 500 בייט מוכנים בבפר.
היום זה פחות נפוץ כי רוב ה router המודרניים יכולים לדחוף חבילות גדולות כאלה, אבל עד לפני מספר שנים זה מה שהיה קורה.
בגלל זה ההסתמכות על הודעה אחת = פקט אחד = בפר אחד מאד בעייתית. אולי לא נתקלת ספיציפית בבעיה הזאת, אבל זה בהחלט פוטנציאל מעצם הגדרת הפרוטוקול של TCP.
 

Rשף

New member
זה

וגם אם זה פחות נפוץ זה עדיין יכול לקרות בגלל עומס כזה או אחר בדרך, אפילו במערכת ההפעלה
 

d70

Well-known member
מניסיון שלי
היו לנו מערכות (שהתבססו על פקטות UDP) ,אתה לעולם לא יודע מה יהיה מצב הרשת או מה הולך לעשות ה IT של הלקוח.מדובר בג'ונגל.יש מקרים שקיבלנו רשת מבודדת ושקטה ויש מקרים של רשת לא יציבה ועמוסה.פקטות נעלמות ,מושהות ומה לא.
דווקא הפרוטוקולים הייעודיים שרצו על bus ייעודי (לא אתרנט) עבדו כמו שצריך.
 

ilgnd

New member
זו בדיוק הסיבה שאני עובד בצורה הזו

אני לא מניח שום דבר על גדלי הפקטות ועל החלוקה ביניהן. פשוט שומר את כל הבתים עד שיש לי מספיק בשביל להרכיב הודעה.
הצד השני כן הניח הנחות ומכאן הויכוח בינינו.
 

Miki Watts

New member
כן, איך שאתה עובד זה נכון, אני יותר אומר שהצד השני עובד לא

נכון, אז לא משנה כמה תתאמץ שזה לא יקרה, זה עדיין יקרה לך מדי פעם.
 

d70

Well-known member
OK
תראה נניח שצד הלקוח מוגבל בקריאה אחת והוא לא מוכן לשנות כלום מצידו.
נשאלת השאלה אם ניתן לתקן זאת רק מהצד שלך?
ניקח את התסריט שהבאת:
1. כתבתי לחוצץ הודעה בגודל 100 בתים.
2. כתבתי לחוצץ הודעה שניה בגודל 100 בתים.
3. כעת המצביע start מצביע על 0 והמצביע end מצביע על 200.
4. קיבלתי write-ready מה - channel.
5. כתבתי 150 בתים (הודעה וחצי). כרגע המצביע start מצביע על 150.
6. הלקוח קרא 150 בתים. את ה-100 הראשונים הפך להודעה ואת ה-50 האחרים זרק.
7. מרגע זה הלקוח יזרוק את כל ההודעות מפני שהן תמיד יתחילו בזבל.

לדעתי אתה צריך לתקן את שלב 5 כך שאתה תכתוב רק הודעה אחת(100).
יש לך את כל המידע, אתה יודע כמה מקום יש בבאפרים ואתה יודע מה גודל ההודעה.
לאחר שליחה של הודעה אחת את ממתין למשך X זמן.כאשר X הינו הזמן הדרוש לצד השני להיות מוכן לקליטת הודעה חדשה.
עקום,בזבזני ולא יעיל.. אבל ממה שאני מבין עדיין עומד ב spec...
 

ilgnd

New member
אבל אז אני צריך לנהל תור של מיקומים בבאפר.

הפיתרון שלי היה לעשות מה שהמנכל אמר לי: להיכנע ללקוח ולעבוד בצורה סינכרונית כמוהו.
&nbsp
הבוס לא תמיד צודק אבל הוא תמיד הבוס.
הלקוח לא תמיד צודק אבל הוא תמיד הלקוח.
&nbsp
&nbsp
 

choo

Active member
ושוב - זה היה הדבר הנכון בתנאים שתארת

&nbsp
עושים את הדבר הכי פשוט שעונה על הדרישות. כל דבר אחר - זה אגו של מהנדסים :0
 
למעלה