לדלג לתוכן

11.2 שרת TCP מתקדם תרגול

תרגול - שרת TCP מתקדם - advanced TCP server

תרגול 1 - שרת הד עם fork

  1. כתבו שרת TCP שמשתמש ב-fork כדי לטפל בכמה לקוחות במקביל.
  2. השרת מקבל חיבור, עושה fork, והילד מטפל בלקוח (echo - שולח בחזרה מה שקיבל).
  3. תהליך האב מדפיס את ה-PID של כל ילד שנוצר.
  4. טפלו ב-SIGCHLD כדי למנוע תהליכי zombie.
  5. בדקו שאתם יכולים לחבר כמה לקוחות (עם nc) במקביל.

רמז: אל תשכחו שהאב צריך לסגור את client_fd והילד צריך לסגור את server_fd.


תרגול 2 - שרת צ'אט עם threads

  1. כתבו שרת TCP שמשתמש ב-threads. השרת מנהל רשימה גלובלית של כל הלקוחות המחוברים.
  2. כשלקוח שולח הודעה, השרת שולח אותה לכל שאר הלקוחות (broadcast).
  3. השתמשו ב-mutex כדי להגן על הרשימה הגלובלית מגישה מקבילית.
  4. כשלקוח מתנתק, הסירו אותו מהרשימה.

מבנה מוצע:

#define MAX_CLIENTS 100

int client_fds[MAX_CLIENTS];
int client_count = 0;
pthread_mutex_t clients_mutex = PTHREAD_MUTEX_INITIALIZER;

רמז: כשעושים broadcast, צריך לנעול את המוטקס, לעבור על כל הלקוחות ולשלוח, ואז לשחרר. שימו לב לא לשלוח ללקוח שמאיר - הוא שלח את ההודעה.


תרגול 3 - שרת הד עם select

  1. כתבו שרת TCP שמשתמש ב-select כדי לטפל בכמה לקוחות ב-thread אחד.
  2. השרת מנטר גם את סוקט השרת (לחיבורים חדשים) וגם את כל סוקטי הלקוחות (לנתונים).
  3. כשלקוח שולח נתונים, השרת שולח אותם בחזרה עם הprefix [fd N] (כש-N הוא מספר הfd).
  4. הדפיסו הודעה כשלקוח מתחבר ומתנתק.

רמז: זכרו ש-select משנה את ה-fd_set. צריך לבנות אותה מחדש בכל סיבוב של הלולאה.


תרגול 4 - שרת הד עם epoll

  1. כתבו שרת TCP שמשתמש ב-epoll כדי לטפל בכמה לקוחות.
  2. השרת מבצע echo בדיוק כמו בתרגול הקודם, אבל עם epoll במקום select.
  3. הוסיפו ספירה של כמה בתים כל לקוח שלח. כשלקוח מתנתק, הדפיסו את הסטטיסטיקה שלו.

לצורך שמירת הסטטיסטיקה, תצטרכו מבנה נתונים שמקשר fd למידע על הלקוח:

struct client_data {
    int fd;
    long bytes_received;
    char ip[INET_ADDRSTRLEN];
    int port;
};

רמז: אפשר להשתמש ב-event.data.ptr במקום event.data.fd כדי לשמור מצביע ל-struct client_data. ככה כשepoll מחזיר אירוע, יש לכם גישה ישירה לכל המידע על הלקוח.


תרגול 5 - השוואת ביצועים

  1. קחו את שלושת השרתים שכתבתם (fork, select, epoll) ובדקו את הביצועים שלהם.
  2. כתבו תוכנית בדיקה (benchmark client) שפותחת N חיבורים במקביל ושולחת M הודעות בכל חיבור.
  3. מדדו את הזמן הכולל עם clock_gettime(CLOCK_MONOTONIC, ...).
  4. הריצו את הבדיקה עם N=10, N=100, N=500 ותעדו את התוצאות.

מבנה מוצע לתוכנית הבדיקה:

/* עבור כל חיבור, צרו thread שמתחבר לשרת ושולח הודעות */
void *benchmark_thread(void *arg) {
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    /* connect... */
    for (int i = 0; i < messages_per_client; i++) {
        write(sockfd, msg, msg_len);
        read(sockfd, buffer, sizeof(buffer));
    }
    close(sockfd);
    return NULL;
}

רמז: השתמשו ב-pthread_create ליצירת threads של הלקוחות ו-pthread_join לחכות שכולם יסיימו.