Previous Up Next

דפדפן זעיר

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

איתחול סביבת התקשורת: בחלונות צריך לאתחל ולסגור את סביבת התקשורת,

#include <winsock2.h>
...
int WSAStartup(WORD version, WSAData* data);
int WSACleanup();
int WSAGetLastError();
בהשגרה הראשונה מאתחלת את סביבת התקשורת, השנייה סוגרת אותה, והשלישית מחזירה את קוד השגיאה האחרון, שניתן להעביר ל--FormatMessage על מנת לקבל מחרוזת שמתארת את השגיאה. הארגומנט הראשון של שגרת האיתחול מאפשר לתוכנית לוודא שהגרסה של שגרות התקשורת עדכנית; השתמשו בערך MAKEWORD(2,2). כמו כן, בסביבת הפיתוח יש לוודא שהתוכנית משתמשת בספריה wsock32.lib (בדרך כלל תחת project properties > linker > input > additional dependencies).

יצירת וסגירת שקע תקשורת:  

#include <winsock2.h>
...
SOCKET socket(int domain, int type, int protocol);
int    closesocket(SOCKET s);
הקריאה socket יוצרת שקע ומחזירה מזהה מספרי (כמו open עבור קבצים) או -1 (בחלונות הקבוע INVALID_SOCKET) במקרה של כשל. הפרמטר הראשון מציין את ''עולם התקשורת'' שבו יפעל השקע. לתקשורת בפרוטוקולי האינטרנט ערכו צריך להיות הקבוע הסימבולי AF_INET. הארגומנט השני מציין את סוג השירות שהשקע יספק. ליצירת קשר סדור ואמין (למשל קשר tcp) יש להעביר את הערך SOCK_STREAM. הארגומנט השלישי מציין את הפרוטוקול הספיציפי ועבור tcp ערכו צריך להיות IPPROTO_TCP. את השקע סוגרים בעזרת קריאת המערכת close או closesocket.

חיבור שקע קיים לשרת:  

#include <winsock2.h>
...
int connect(SOCKET           sockfd, 
            struct sockaddr* server_name, 
            int              server_name_len);
הקריאה מחברת את השקע לשרת. במקרה של כשל מוחזר הערך -1. הארגומנט הראשון הוא מספר השקע שהוחזר בקריאה ל--socket. הארגומנטים השני והשלישי מציינים את השרת שאליו רוצים להתחבר ואת מספר השער (port). איתחול מבנה זה מוסבר בהמשך. הארגומנט השלישי מציין את אורך הארגומנט השני.

מרגע שהשקע מחובר לשרת ניתן לקרוא ולכתוב ממנו ואליו על ידי שימוש ב--read ו--write ממש כמו מקובץ או צינור (מספר השקע משמש כמזהה של קובץ פתוח). על מנת לסגור את הקשר יש לקרוא ל--close, ממש כמו לקובץ פתוח.

בניית כתובת אינטרנט. המבנה מסוג struct sockaddr הוא מבנה פולימורפי שיכול ליצג כתובות במשפחות פרוטוקולים שונות. במקרה שלנו יש להשתמש במבנה שמייצג כתובות אינטרנט. שם המבנה הספיציפי הזה הוא struct sockaddr_in (יש לבצע casting למצביע בזמן הקריאה ל--connect). במבנה זה שלושה שדות: (1) sin_family, המציין את סוג הכתובת ובמקרה של כתובות אינטרנט עליו להכיל את הקבוע AF_INET, (2) sin_port, מטיפוס short, שצריך להכיל את מספר השער כאשר הבתים מסודרים ב--network order, (3) sin_addr שמכיל את כתובת השרת, כפי שהיא מוחזרת מהקריאה gethostbyname או gethostbyaddr שיתוארו בהמשך. השגרה htons ממירה משתנה מטיפוס short מסידור בתים של המחשב לסידור הקאנוני של הרשת (מוגדרת ב--netinet/in.h).

את ארגומנט אורך המבנה בקריאה ל--connect (הארגומנט השלישי) יש לחשב בעזרת sizeof של struct sockaddr_in.

#include <winsock2.h>
...
struct hostent* gethostbyname(const char *name);
struct hostent* gethostbyaddr(const char *addr, int len, int type);
שתי השגרות הללו הופכות מחרוזת המכילה שם שרת או את כתובת ה--ip שלו למשתנה שניתן להעתיק (בעזרת memcpy) לשדה sin_addr במבנה מסוג struct sockaddr_in. שתי השגרות מחזירות NULL אם הן נכשלות. במקום לנסות לגלות האם המשתמש העביר לתוכנית שם שרת או כתובת מקובל פשוט לקרוא לשגרה הראשונה ואם היא נכשלת לקרוא לשניה. הארגומט הראשון בשתי השגרות הוא המחרוזת המכילה שם או כתובת שרת. הארגומנט השני ל--gethostbyaddr הוא פשוט אורך המחרוזת והארגומנט השלישי צריך להיות AF_INET.

השגרות מחזירות כתובת של מבנה שמכיל כתובת (או מספר כתובות אם לשרת יש מספר כתובות). אורך כל כתובת מצויין בשדה h_length. האורך הוא מספר הבתים שיש להעתיק לחבר sin_addr במבנה מסוג struct sockaddr_in. הכתובות עצמן שמורות בשדה שהוא מערך של מצביעים לכתובות ששמו h_addr_list. ניתן פשוט להשתמש בכתובת הראשונה במערך.

פרוטוקול http. על מנת לקבל שירות משרת, הלקוח מתחבר בקשר tcp לשרת, בדרך כלל לשער מספר 08, ושולח בקשה. הבקשה מכילה מספר שורות ולאחריהן שורה ריקה המסמנת את סוף הבקשה. אנו נשתמש במבנה בקשה פשוט הכולל שלוש שורות. השורה הראשונה מכילה פקודה. אנו נשתמש בפקודה GET. באותה שורה, אחרי הפקודה, יופיע מזהה הקובץ שרוצים להוריד (ה--url), ולאחריו מזהה הפרוטוקול, מופרדים ברווחים. אנחנו נשתמש בגרסה הראשונה של פרוטוקול http שהמזהה שלו הוא HTTP/1.0. השורה השנייה תכיל את התג Host: ולאחריו (מופרד ברווח) שם שרת שמסוגל לספק את הקובץ המבוקש. זה לא חייב להיות השרת שהתחברנו אליו או השרת שמצוין ב--url, אם כי השרת המצויין ב--url הוא השרת ההגיוני ביותר לשדה זה. השורה השלישית תהיה שורה ריקה. כל שורה צריכה להסתיים בתוים \r\n (ולא \n בלבד). הנה בקשה לדוגמא:

GET http://www.math.tau.ac.il/~sivan/ HTTP/1.0\r\n
Host: www.tau.ac.il\r\n
\r\n
השרת עונה במספר שורות מידע לגבי השרת והקובץ ולאחריהן שורה ריקה, ולאחריה הקובץ (או תחילתו במקרה של קובץ גדול). שורת המידע הראשונה היא החשובה ביותר מכיוון שהיא מכילה אינדיקציה להצלחת או כשלון השירות. השורה המכילה את גודל הקובץ שיישלח גם היא חשובה. להלן דוגמא אופיינית לשורות המידע כפי שהתקבלו כתגובה לבקשה שהצגנו:

HTTP/1.0 200 OK 
Date: Tue, 25 Apr 2000 09:50:57 GMT 
Server: Apache/1.3.3 (Unix) 
Last-Modified: Wed, 22 Mar 2000 13:44:01 GMT 
ETag: "cf999-1007-38d8ce21" 
Accept-Ranges: bytes 
Content-Length: 4103 
Content-Type: text/html
את האפיון המלא של פרוטוקול http ניתן למצוא באתר www.w3.org.

התרגיל. עליך לכתוב תכנית בשם ex-browse שתפקידה לקבל קובץ משרת http ולהדפיס אותו ואת שורות המידע ששולח השרת לפלט. לתכנית יש ארבעה ארגומנטים: שם/כתובת שרת שאליו יש להתחבר, מספר שער, שם השרת שיספק את הקובץ, וה--url של הקובץ עצמו. למשל, ההתחברות בדוגמה היא תוצאה של הפקודה

% ex-browse www.tau.ac.il 80 \
  www.math.tau.ac.il \
  http://www.math.tau.ac.il/~sivan/
הסבר קצר לגבי שרתים מוּרשים (proxy servers): ספקי אינטרנט רבים (כולל אוניברסיטות וחברות שמספקות שירותי אינטרנט למחשבים בתוך הארגון) חוסמים התחברויות בערוצי tcp לשער מספר 08 של שרתים חיצוניים, וזאת על מנת ליעל את תעבורת ה--http. על מנת להוריד קבצים כאשר המחשב מחובר לספק אינטרנט כזה, יש לבקש את הקבצים משרת מוּרשה (proxy) ששייך לספק ושרק לו יש יכולת ליצור חיבורים חיצוניים לשער 08. בדרך כלל, השליח מבקש עבורנו את הקובץ מהשרת החיצוני. אולם אם יש לשליח עותק עדכני של הקובץ מכיון שלקוח כלשהו הוריד אותו לאחרונה, הוא יחזיר לנו את העותק ששמור אצלו, ובכך יחסוך לספק האינטרנט תעבורת ip. ספק האינטרנט או אנשי התמיכה באוניברסיטה או בחברה יוכלו לומר לכם את שם השרת המורשה, אם נעשה שימוש בשרת כזה.

ניתן ללמוד רבות על התנהגות שרתי אינטרנט מניסויים עם הדפדפן הזעיר, למשל:
  1. אם הספק שלכם משתמש בשרת מורשה, נסה/נסי להוריד את דף הבית של אתר כמו www.cnn.com בבקשה ישירה ל--www.cnn.com וגם דרך השרת המורשה. באיזה מהדרכים הצלחת להוריד את הדף? אם נתקלתם בכשל, מה בדיוק קרה?
  2. נסו להוריד את דף האינטרנט של הספר, למשל, עם וגם בלי "/" אחרי המילה osbook. באחד המקרים תקבלו ככל הנראה הודעת שגיאה. מה היתה הודעת השגיאה? איך מתנהג דפדפן מקצועי כאשר הוא מקבל הודעת שגיאה כזו (נסו)?
Copyright Sivan Toledo 2004
Previous Up Next