שאלה כללית על socketים.

selalerer

New member
שאלה כללית על socketים.

למה בברירת מחדל הsocket הוא חוסם (blocking), הרי כל אחד שעושה תוכנה, רוצה לאפשר יציאה גם בזמן קריאה ל accept ו recv, אפילו אם הן רצות בthread נוסף, אתה לא רוצה פשוט לרצוח את הthread אלא לתת לו לצאת בצורה מסודרת. יש למישהו איזה הסבר בשבילי? מישהו עשה תוכנה בה הוא לא שינה את המצב של הthread (בעזרת fcntl או iocntl)? אם כן, אז איך הוא הפסיק את הקריאות לaccept או מה שלא יהיה כשהוא רצה לצאת מהתוכנה? תודה, סלע.
 

codec

New member
timeout על csocket

יש דוגמה כזאת ב-MSDN. זה עובד לא רע בכלל...
 

selalerer

New member
CSocket וMSDN הם נחמדים מאוד אבל

לא קשורים לעניין, אני מתכנת על לינוקס.
 

vinney

Well-known member
אז תשתמש בSIGNAL

אתה יכול לשים סיגנל שיפריע לך ביציאה, ויוציא אותך מהBLOCK, וכך תצא מסודר
 

selalerer

New member
אני לא כלכך מבין איך סיגנל עוזר להפ

סיק את accept באמצע? כלומר הוא מחכה להתקשרות ואני לא רוצה פשוט להרוג את הthread שהוא נקרא ממנו. פתרון עדיף נראה לי להפוך את הsocket לNONBLOCK ולבדוק כל פעם אם דגל היציאה הודלק ואם יש התקשרות בתור, בלולאה (אולי עם איזה השעיה באמצע).
 

voguemaster

New member
לא רעיון טוב

לבצע לולאה כזו יעלה לך את ה-CPU USAGE לאחוזים גבוהים מאוד. אסור לעשות לולאות BUSY WAIT אם אפשר להימנע מהן. הפיתרון הנכון רוב הזמן הוא לחכות עם ACCEPT ב-TIMEOUT מסוים. איך עושים את זה ? עם SELECT כמובן. יש פיתרון מצוין אצל סטיבנס ב-UNIX NETWORK PROGRAMMING (יש כמה דגשים שצריך לשים לב אליהם לעומת זאת).
 

selalerer

New member
שאלה נוספת: קביעת non-blocking sock

ניסיתי לעשות כך:
int listenSock=socket(PF_INET/*IP*/,SOCK_STREAM/*tcp*/,0); if(0>listenSock)//check we got a socket descriptor back. { cout<<"accepter:socket returned "<<listenSock<<", exiting."<<endl; return NULL; } if(fcntl(listenSock,F_SETFL,O_NOBLOCK)&O_NOBLOCK==0) { cout<<"accepter:could not set listening socket to be non-blocking, exiting."<<endl; return NULL; }​
והוא אומר לי שהוא לא מזהה את O_NOBLOCK (וגם את EAGAIN במקום אחר בקוד). למה הוא לא מזהה לי את הקבועים האלה? בman רשום:
F_SETFL Set the file status flags part of the descriptor’s flags to the value specified by arg. Remaining bits (access mode, file cre- ation flags) in arg are ignored. On Linux this command can only change the O_APPEND, O_NONBLOCK, O_ASYNC, and O_DIRECT flags.​
כמובן שעשיתי את הincludeים המתאימים (שמצויינים בman).
 

selalerer

New member
קבלו ביטול ../images/Emo9.gif טעות כתיב, אבל לגבי

EAGAIN זה עדיין תקף, הוא לא מזהה. מתוך הman:
If no pending connections are present on the queue, and the socket is not marked as non-blocking, accept blocks the caller until a connec- tion is present. If the socket is marked non-blocking and no pending connections are present on the queue, accept returns EAGAIN.​
 

voguemaster

New member
יש קבוע שמחליף אותו

לא זוכר אפילו, אולי זה היה EWOULDBLOCK. אני יכול לבדוק אחורי זה. איזו גרסת לינוקס יש לך שהקבוע הזה לא מוגדר לך ?? הוא אמור להיות מוגדר עבור תאימות אחורה!
 

selalerer

New member
הוא מוגדר ב errno.h ולכן התוכנית ש

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

selalerer

New member
אה, בעצם יכול להיות שהקוד כבר מקומפ

ל ואז הוא לא משתמש בקבוע (זה מוחלף בפרקומפילציה) אבל אז במידה ומשנים את הקבועים בheader אז יש צורך לקמפל מחדש.
 

galh

New member
תעבוד עם select.

סיבות היסטוריות שקשורות לעובדה שלא היו threads ביוניקס. ברגע שעובדים עם select אפשר "לשבת" על שני file descriptors. אחד ה- socket והשני pipe (לדוגמא). ברגע שמגיע משהו מה- pipe אז אתה יודע שצריך לצאת.
 

selalerer

New member
זה טוב בערך, מה אם אני רוצה לטפל בק

ליטת נתונים מהמשתמש (stdin) בthread אחר, אבל פשוט לא רוצה שהthread הזה יחסם, בכדי שאני אוכל לסמן לו לצאת בצורה מסודרת. בכל מקרה למדתי פונקציה חדשה, תודה
.
 

galh

New member
לא הבנתי מה אתה רוצה.

אבל חוץ מ- select, יש גם את poll שמאפשרת לך לעבוד בצורה של "אירועים" (או בלעז events).
 

selalerer

New member
כרגע הפתרון שהכי נראה לי הוא להפוך

את הsocket לnon-blocking ככה:
int listenSock=socket(PF_INET/*IP*/,SOCK_STREAM/*tcp*/,0); fcntl(listenSock,F_SETFL,O_NOBLOCK);​
ואז כל פעם שאני עושה accept הוא חוזר מייד ואני צריך לבדוק אם הוא החזיר לי EAGAIN שזה פשוט אומר שאין לו התחברות בתור, וככה אני יכול לבדוק בלולאה, אם יש התחברות בתור ואם הודלק הדגל יציאה. הבעיה היא שכרגע הוא לא מזהה לי את הקבוע EAGAIN, למרות שזה מה שמצאתי בman:
If no pending connections are present on the queue, and the socket is not marked as non-blocking, accept blocks the caller until a connec- tion is present. If the socket is marked non-blocking and no pending connections are present on the queue, accept returns EAGAIN.​
ואני לא מבין למה הוא לא מזהה לי את הקבוע הזה. תודה, סלע.
 

voguemaster

New member
כמה בעיות קטנות

1. בצורה שבה הפעלת את הדגל מחקת את כל הדגלים שהיו SET קודם לכן. אתה קובע פה בצורה אבסולוטית את הדגלים עבור ה-SOCKET. השיטה הנכונה היא קודם לקרוא ל-fcntl עם F_GETFL ואז עם התוצאה לעשות logical or. 2. תעבוד עם select ותפסיק להציק
. אנחנו עוד לא הבנו מה אתה רוצה לעשות אבל תהיה סמוך ובטוח שהפיתרון של לבדוק בלולאה הוא גרוע!
 

selalerer

New member
הממממממממ....

1. צודק. 2. הבעיה שלי עם select זה שבמידה ואני רוצה להפריד את הקלט מהמשתמש (לגבי מתיי לצאת מהתוכנית למשל) ובין הקלט מהsocket אז במקום בו אני קורא לaccept אין fd (או לפחות לא אמור להיות לי) של הקלט, ואני גם לוגית לא אמור לטפל בו שם. הרעיון שלי הוא משהו כזה:
void *acceptThread(void *args) { const bool *quit=(const bool *)args; int sd=socket(PF_INET,SOCK_STREAM,0); int flags=fcntl(sd,FGET_FL); flags|=O_NONBLOCK; fcntl(sd,FSET_FL); //....... //........ while(!*quit) { int rv=accept(sd,(struct sockaddr *)&clientAddr,&addrSize); if(rv!=EAGAIN) { //Make new thread to handle data from this client... } else { sleep(1); } } }​
ואני לא רואה למה הוא גרוע, אשמח אם תאיר את עיניי. תודה, סלע.
 

voguemaster

New member
גל כבר אמר לך

אתה יכול לבצע block בתוך SELECT גם על הסוקט וגם על קלט מהמשתמש. יש ? ככה יוצא שאתה מחכה ל-EVENT כלשהו, או מהרשת או מהמשתמש. למה זה לא טוב לעשות לולאה כזו ? כי זה אוכל משאבי מעבד, זה למה. תבדוק ותראה..
 

selalerer

New member
הבעיה עם זה היא...

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

voguemaster

New member
מעניין

ישנן שתי אופציות שחשבתי עליהן: 1. לעבוד עם SIGNAL שיפסיק את ACCEPT או SELECT (תלוי באיזו צורה תעבוד). הצורה הפחות מועדפת לדעתי אבל היא תפעל. 2. אתה רוצה encapsulation של קבלת התקשרויות חדשות במחלקה ייעודית לזה ? אוקיי. אני לא לגמרי מסכים עם ה-DESIGN הזה אבל נניח. אתה צריך שהמחלקה שלך תמתין להתקשרויות ב-thread נפרד משל עצמה. כשיתקבל event כלשהו ב-thread הראשי של התוכנית (נניח ע"י הכנסת תו או קליק של עכבר באפליקציה עם GUI), תוכל לעשות טריק קטן וחביב אם ה-file descriptor משותף לשני ה-threads. הטריק הוא לעשות SET ל-ERROR ברמת ה-SOCKET. אפשר לעשות את זה נורא בקלות ע"י setsockopt. אם אתה ממתין עם SELECT היא חוזרת מיידית (ולא עם ערך שלילי!) ואתה צריך לבדוק שאכן קיבלת את ה-ERROR המתאים. במידה וכן, אתה יודע שאתה לא צריך לחזור לישון. זה כמו ERROR יזום בשבילך, אם תרצה. לעומת זאת, אולי כדאי לרכז את כל ה-IO שנובע מ-file descriptors במחלקה אחת, או לפחות את הטיפול ב-eventים של file descriptors (כלומר: שהם מוכנים לקריאה/כתיבה)
 
למעלה