logo

תכנות Socket ב-C

תכנות שקעים היא דרך לחבר שני צמתים ברשת כדי לתקשר זה עם זה. שקע אחד (צומת) מקשיב ליציאה מסוימת ב-IP ואילו השקע השני מושיט יד לשני כדי ליצור חיבור. השרת יוצר את שקע המאזין בזמן שהלקוח מושיט יד לשרת.
תכנות Socket נמצא בשימוש נרחב ביישומי הודעות מיידיות סטרימינג בינארי ושיתופי פעולה מסמכים פלטפורמות סטרימינג מקוונות וכו'.

דוּגמָה

בתוכנית C זו אנו מחליפים הודעת שלום אחת בין שרת ללקוח כדי להדגים את מודל הלקוח/שרת.

server.c

C
#include  #include  #include  #include  #include  #include  #define PORT 8080 int main(int argc char const* argv[]) {  int server_fd new_socket;  ssize_t valread;  struct sockaddr_in address;  int opt = 1;  socklen_t addrlen = sizeof(address);  char buffer[1024] = { 0 };  char* hello = 'Hello from server';  // Creating socket file descriptor  if ((server_fd = socket(AF_INET SOCK_STREAM 0)) < 0) {  perror('socket failed');  exit(EXIT_FAILURE);  }  // Forcefully attaching socket to the port 8080  if (setsockopt(server_fd SOL_SOCKET  SO_REUSEADDR | SO_REUSEPORT &opt  sizeof(opt))) {  perror('setsockopt');  exit(EXIT_FAILURE);  }  address.sin_family = AF_INET;  address.sin_addr.s_addr = INADDR_ANY;  address.sin_port = htons(PORT);  // Forcefully attaching socket to the port 8080  if (bind(server_fd (struct sockaddr*)&address  sizeof(address))  < 0) {  perror('bind failed');  exit(EXIT_FAILURE);  }  if (listen(server_fd 3) < 0) {  perror('listen');  exit(EXIT_FAILURE);  }  if ((new_socket  = accept(server_fd (struct sockaddr*)&address  &addrlen))  < 0) {  perror('accept');  exit(EXIT_FAILURE);  }    // subtract 1 for the null  // terminator at the end  valread = read(new_socket buffer  1024 - 1);   printf('%sn' buffer);  send(new_socket hello strlen(hello) 0);  printf('Hello message sentn');  // closing the connected socket  close(new_socket);    // closing the listening socket  close(server_fd);  return 0; } 

לקוח.ג

C
#include    #include  #include  #include  #include  #define PORT 8080 int main(int argc char const* argv[]) {  int status valread client_fd;  struct sockaddr_in serv_addr;  char* hello = 'Hello from client';  char buffer[1024] = { 0 };  if ((client_fd = socket(AF_INET SOCK_STREAM 0)) < 0) {  printf('n Socket creation error n');  return -1;  }  serv_addr.sin_family = AF_INET;  serv_addr.sin_port = htons(PORT);  // Convert IPv4 and IPv6 addresses from text to binary  // form  if (inet_pton(AF_INET '127.0.0.1' &serv_addr.sin_addr)  <= 0) {  printf(  'nInvalid address/ Address not supported n');  return -1;  }  if ((status  = connect(client_fd (struct sockaddr*)&serv_addr  sizeof(serv_addr)))  < 0) {  printf('nConnection Failed n');  return -1;  }    // subtract 1 for the null  // terminator at the end  send(client_fd hello strlen(hello) 0);  printf('Hello message sentn');  valread = read(client_fd buffer  1024 - 1);   printf('%sn' buffer);  // closing the connected socket  close(client_fd);  return 0; } 


קומפילציה



gcc client.c -o clientgcc server.c -o server


תְפוּקָה

Client:Hello message sentHello from serverServer:Hello from clientHello message sent

רכיבי תכנות Socket

1. שקעים

שקעים הם אחד ממרכיבי הליבה המשמשים את התוכנית כדי לגשת לרשת כדי לתקשר עם תהליכים/צמתים אחרים ברשת. זה פשוט שילוב של כתובת IP ומספר יציאה המשמש כנקודת קצה לתקשורת.
דוגמה: 192.168.1.1:8080 כאשר שני החלקים המופרדים על ידי המעי הגס מייצגים את כתובת IP ( 192.168.1.1 ) ואת מספר יציאה (8080).

סוגי שקעים:

  • שקע TCP (שקע זרם): מספק תקשורת מבוססת חיבור אמינה (כלומר. פרוטוקול TCP ).
  • שקע UDP (שקע דאטגרם): מספק תקשורת ללא חיבור מהירה יותר אך לא אמינה (כלומר. פרוטוקול UDP ).

2. מודל שרת-לקוח

ה מודל שרת-לקוח הכוונה לארכיטקטורה המשמשת בתכנות שקעים שבה לקוח ושרת מתקשרים זה עם זה כדי להחליף מידע או שירותים. ארכיטקטורה זו מאפשרת ללקוח לשלוח בקשות שירות ולשרת לעבד ולשלוח תגובה לבקשות שירות אלו.

דיאגרמת מצב עבור מודל שרת ולקוח

תכנות Socket ב-Cדיאגרמת מצב עבור מודל שרת ולקוח של Socket

תכנות Socket ב-C היא דרך רבת עוצמה לטפל בתקשורת ברשת.

יצירת תהליך בצד השרת

השרת נוצר באמצעות השלבים הבאים:

קודי צבע של java

1. יצירת שקעים

שלב זה כולל את יצירת השקע באמצעות הפונקציה socket() .

פרמטרים:

  • sockfd: מתאר socket מספר שלם (כמו ידית קובץ)
  • תְחוּם: מספר שלם מציין תחום תקשורת. אנו משתמשים ב-AF_ LOCAL כפי שהוגדר בתקן POSIX לתקשורת בין תהליכים באותו מארח. לתקשורת בין תהליכים במארחים שונים המחוברים באמצעות IPV4 אנו משתמשים ב-AF_INET וב-AF_I NET 6 עבור תהליכים המחוברים באמצעות IPV6.
  • סוּג: סוג תקשורת
    SOCK_STREAM: TCP (מכוון חיבור אמין)
    SOCK_DGRAM: UDP (לא אמין ללא חיבור)
  • פּרוֹטוֹקוֹל: ערך פרוטוקול עבור פרוטוקול אינטרנט (IP) שהוא 0. זהו אותו מספר שמופיע בשדה הפרוטוקול בכותרת ה-IP של חבילה. (פרוטוקולים של מאן לפרטים נוספים)
C
sockfd = socket(domain type protocol) 

2. הגדר socket opt

זה עוזר במניפולציה של אפשרויות עבור השקע שמפנה מתאר הקובץ sockfd. זה אופציונלי לחלוטין אבל זה עוזר בשימוש חוזר בכתובת וביציאה. מונע שגיאות כגון: כתובת כבר בשימוש.

C
setsockopt(sockfd level optname optval socklen_t optlen); 

3. לאגד

לאחר יצירת השקע הפונקציה bind() קושרת את השקע לכתובת ולמספר היציאה שצוינו ב-adr(מבנה נתונים מותאם אישית). בקוד לדוגמה אנו קושרים את השרת ל-localhost ולכן אנו משתמשים ב-INADDR_ANY כדי לציין את כתובת ה-IP.

C++
bind(sockfd sockaddr *addr socklen_t addrlen); 

פרמטרים:

  • sockfd : מתאר קובץ socket שנוצר באמצעות הפונקציה socket() .
  • addr : מצביע ל-struct sockaddr המכיל את כתובת ה-IP ומספר היציאה כדי לאגד את השקע.
  • addrlen : אורך מבנה ה-adr.

4. הקשיבו

בשלב זה השרת משתמש בפונקציה listen() שמכניסה את שקע השרת למצב פסיבי בו הוא ממתין שהלקוח יתקרב לשרת כדי ליצור חיבור. ה-backlog מגדיר את האורך המקסימלי אליו עשוי תור החיבורים הממתינים ל-sockfd לגדול. אם תגיע בקשת חיבור כאשר התור מלא, הלקוח עשוי לקבל שגיאה עם אינדיקציה של ECONNREFUSED.

C
listen(sockfd backlog); 

פרמטרים :

  • sockfd : מתאר קובץ socket שנוצר באמצעות הפונקציה socket() .
  • פיגור : מספר המייצג את גודל התור המחזיק את החיבורים הממתינים בזמן שהשרת ממתין לקבל חיבור.

5. קבל

בשלב זה השרת מחלץ את בקשת החיבור הראשונה מהתור של חיבורים ממתינים עבור שקע ההאזנה sockfd יוצר שקע מחובר חדש באמצעות לְקַבֵּל() function ומחזיר מתאר קובץ חדש המתייחס לאותו שקע. בשלב זה נוצר החיבור בין הלקוח לשרת והם מוכנים להעביר נתונים.

C
new_socket= accept(sockfd sockaddr *addr socklen_t *addrlen); 

פרמטרים:

  • sockfd : מתאר קובץ socket מוחזר על ידי socket() ו-bind().
  • addr : מצביע ל-struct sockaddr שיכיל את כתובת ה-IP ומספר היציאה של הלקוח.
  • addrlen : מצביע למשתנה המציין את אורך מבנה הכתובות.

6. שלח/קבל

בשלב זה השרת יכול לשלוח או לקבל נתונים מהלקוח.

לִשְׁלוֹחַ(): לשלוח נתונים ללקוח

C
send(sockfd *buf len flags); 

פרמטרים:

  • sockfd : מתאר קובץ socket שהוחזר על ידי הפונקציה socket() .
  • buff : מצביע למאגר המכיל את הנתונים שיש לשלוח.
  • רַק : מספר בתים של נתונים שיש לשלוח.
  • דגלים : מספר שלם המציין אפשרויות שונות לאופן שליחת הנתונים בדרך כלל 0 משמש להתנהגות ברירת מחדל.

קבל() : כדי לקבל את הנתונים מהלקוח.

C
recv( sockfd *buf len flags); 

פרמטרים:

  • sockfd : מתאר קובץ socket שהוחזר על ידי הפונקציה socket() .
  • buff : מצביע למאגר המכיל את הנתונים שיש לאחסן.
  • רַק : מספר בתים של נתונים שיש לשלוח.
  • דגלים : מספר שלם המציין אפשרויות שונות לאופן שליחת הנתונים בדרך כלל 0 משמש להתנהגות ברירת מחדל.

6. סגור

לאחר השלמת חילופי המידע השרת סוגר את השקע באמצעות הפונקציה close() ומשחרר את משאבי המערכת.

C
close(fd); 

פרמטרים:

  • fd: מתאר קובץ של השקע.

יצירת תהליך בצד הלקוח

בצע את השלבים הבאים ליצירת תהליך בצד הלקוח:

1. חיבור שקע

שלב זה כרוך ביצירת השקע שנעשה באותו אופן כמו זה של יצירת השקע של השרת

2. התחבר

קריאת המערכת connect() מחברת את השקע שאליו מתייחס מתאר הקובץ sockfd לכתובת שצוינה על ידי addr. הכתובת והיציאה של השרת מצוינות ב-adr.

C++
connect(sockfd sockaddr *addr socklen_t addrlen); 

פרמטרים

  • sockfd : מתאר קובץ socket שהוחזר על ידי הפונקציה socket() .
  • addr : מצביע למבנה sockaddr המכיל את כתובת ה-IP ומספר היציאה של השרת.
  • addrlen : גודל הכתובת.

3. שלח/קבל

בשלב זה הלקוח יכול לשלוח או לקבל נתונים מהשרת, מה שנעשה באמצעות הפונקציות send() ו- recieve() בדומה לאופן שבו השרת שולח/מקבל נתונים מהלקוח.

4. סגור

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

בעיות נפוצות ותיקוניהן בתכנות Socket

  • כשלים בחיבור: כדי למנוע כשלים בחיבור, עלינו לוודא שהלקוח מנסה להתחבר אל הנכון כתובת IP ויציאה .
  • שגיאות כריכת יציאה: שגיאות אלו מתרחשות כאשר יציאה כבר נמצאת בשימוש על ידי יישום אחר בתרחיש זה, הקישור ליציאה זו תיכשל. נסה להשתמש ביציאה אחרת או סגור את היישום הקודם באמצעות היציאה.
  • חסימת שקעים: כברירת מחדל, שקעים חוסמים. המשמעות היא שקריאות כמו accept() או recv() ימתינו ללא הגבלת זמן אם אין חיבור לקוח או נתונים. ניתן להגדיר את השקע למצב לא חוסם במידת הצורך.
צור חידון