Unix Socket - Ví dụ về Máy chủ

Để đặt một quy trình thành một máy chủ TCP, bạn cần làm theo các bước dưới đây:

  • Tạo một ổ cắm với lệnh gọi hệ thống socket () .

  • Liên kết socket với một địa chỉ bằng lệnh gọi hệ thống bind () . Đối với ổ cắm máy chủ trên Internet, địa chỉ bao gồm số cổng trên máy chủ.

  • Lắng nghe các kết nối với cuộc gọi hệ thống nghe () .

  • Chấp nhận kết nối với lệnh gọi hệ thống accept () . Cuộc gọi này thường chặn cho đến khi một máy khách kết nối với máy chủ.

  • Gửi và nhận dữ liệu bằng các lệnh gọi hệ thống read ()write () .

Bây giờ chúng ta hãy đặt các bước này dưới dạng mã nguồn. Đặt mã này vào tệp server.c và biên dịch nó bằng trình biên dịch gcc .

#include <stdio.h>
#include <stdlib.h>

#include <netdb.h>
#include <netinet/in.h>

#include <string.h>

int main( int argc, char *argv[] ) {
   int sockfd, newsockfd, portno, clilen;
   char buffer[256];
   struct sockaddr_in serv_addr, cli_addr;
   int  n;
   
   /* First call to socket() function */
   sockfd = socket(AF_INET, SOCK_STREAM, 0);
   
   if (sockfd < 0) {
      perror("ERROR opening socket");
      exit(1);
   }
   
   /* Initialize socket structure */
   bzero((char *) &serv_addr, sizeof(serv_addr));
   portno = 5001;
   
   serv_addr.sin_family = AF_INET;
   serv_addr.sin_addr.s_addr = INADDR_ANY;
   serv_addr.sin_port = htons(portno);
   
   /* Now bind the host address using bind() call.*/
   if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
      perror("ERROR on binding");
      exit(1);
   }
      
   /* Now start listening for the clients, here process will
      * go in sleep mode and will wait for the incoming connection
   */
   
   listen(sockfd,5);
   clilen = sizeof(cli_addr);
   
   /* Accept actual connection from the client */
   newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen);
	
   if (newsockfd < 0) {
      perror("ERROR on accept");
      exit(1);
   }
   
   /* If connection is established then start communicating */
   bzero(buffer,256);
   n = read( newsockfd,buffer,255 );
   
   if (n < 0) {
      perror("ERROR reading from socket");
      exit(1);
   }
   
   printf("Here is the message: %s\n",buffer);
   
   /* Write a response to the client */
   n = write(newsockfd,"I got your message",18);
   
   if (n < 0) {
      perror("ERROR writing to socket");
      exit(1);
   }
      
   return 0;
}

Xử lý nhiều kết nối

Để cho phép máy chủ xử lý nhiều kết nối đồng thời, chúng tôi thực hiện các thay đổi sau trong đoạn mã trên:

  • Đặt câu lệnh accept và đoạn mã sau vào một vòng lặp vô hạn.

  • Sau khi kết nối được thiết lập, hãy gọi fork () để tạo một quy trình mới.

  • Tiến trình con sẽ đóng sockfd và gọi hàm doprocessing , chuyển bộ mô tả tệp socket mới làm đối số. Khi hai quá trình đã hoàn thành cuộc trò chuyện của chúng, như được chỉ ra bằng cách trả về doprocessing () , quá trình này chỉ cần thoát.

  • Tiến trình mẹ đóng newsockfd . Vì tất cả mã này nằm trong một vòng lặp vô hạn, nó sẽ quay trở lại câu lệnh chấp nhận để chờ kết nối tiếp theo.

#include <stdio.h>
#include <stdlib.h>

#include <netdb.h>
#include <netinet/in.h>

#include <string.h>

void doprocessing (int sock);

int main( int argc, char *argv[] ) {
   int sockfd, newsockfd, portno, clilen;
   char buffer[256];
   struct sockaddr_in serv_addr, cli_addr;
   int n, pid;
   
   /* First call to socket() function */
   sockfd = socket(AF_INET, SOCK_STREAM, 0);
   
   if (sockfd < 0) {
      perror("ERROR opening socket");
      exit(1);
   }
   
   /* Initialize socket structure */
   bzero((char *) &serv_addr, sizeof(serv_addr));
   portno = 5001;
   
   serv_addr.sin_family = AF_INET;
   serv_addr.sin_addr.s_addr = INADDR_ANY;
   serv_addr.sin_port = htons(portno);
   
   /* Now bind the host address using bind() call.*/
   if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
      perror("ERROR on binding");
      exit(1);
   }
   
   /* Now start listening for the clients, here
      * process will go in sleep mode and will wait
      * for the incoming connection
   */
   
   listen(sockfd,5);
   clilen = sizeof(cli_addr);
   
   while (1) {
      newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
		
      if (newsockfd < 0) {
         perror("ERROR on accept");
         exit(1);
      }
      
      /* Create child process */
      pid = fork();
		
      if (pid < 0) {
         perror("ERROR on fork");
         exit(1);
      }
      
      if (pid == 0) {
         /* This is the client process */
         close(sockfd);
         doprocessing(newsockfd);
         exit(0);
      }
      else {
         close(newsockfd);
      }
		
   } /* end of while */
}

Đoạn mã sau đây cho thấy một cách triển khai đơn giản của hàm doprocessing .

void doprocessing (int sock) {
   int n;
   char buffer[256];
   bzero(buffer,256);
   n = read(sock,buffer,255);
   
   if (n < 0) {
      perror("ERROR reading from socket");
      exit(1);
   }
   
   printf("Here is the message: %s\n",buffer);
   n = write(sock,"I got your message",18);
   
   if (n < 0) {
      perror("ERROR writing to socket");
      exit(1);
   }
	
}