linux進程間的網絡通訊

1、進程是如何進行網絡通訊的?socket通訊的過程?

  同一機器上的不一樣進程之間的通訊方式有不少種,主要使用消息傳遞或共享內存。而跨網絡的進程是幾乎都是使用socket通訊,例如web服務器,QQ。html

    socket便是一種特殊的文件,操做系統提供了一些socket函數就是對其進行的操做(讀/寫IO、打開、關閉),進程間的通訊就是靠讀寫各自的socket完成的。linux

    通訊的過程web

    server:編程

  1. 使用socket()系統調用建立一個指定類型和協議套接字
  2. 使用bind()系統調用給建立的socket命名,這個名字就是一般所說的服務器地址(ip地址+端口號),例如服務器的80端口
  3. 使用listen()系統調用,監聽來自客戶端的鏈接。
  4. 使用accept()系統調用,接受來自客戶端的鏈接,這個調用一直處於阻塞狀態,直到有客戶端的鏈接。
  5. 向鏈接創建的socket裏面讀寫數據(通訊)

  注意:server最初會建立一個socktet,收到鏈接請求後(accept())以後會建立一個與原有的命名套接字不一樣的套接字。這個新的套接字只與這個特定的client通訊,而命名套接字會保留下來繼續處理來自client的鏈接。服務器

    client:網絡

  1. 使用socket()系統調用建立一個指定類型和協議的套接字。
  2. 使用connect()將1中建立的socket鏈接到服務器的地址。
  3. 使用系統調用發送和接受數據,最簡單的是read( )和write()函數。

2、主要知識點和系統調用介紹

  1. int socket(int domain, int type, int protocol); 建立指定類型的socket,兩個進程可以通訊,必須使用相同域和類型的套接字dom

  •  domain:主要有AF_INET,AF_INIT6,分別表示IPv四、IPv6域
  •  type:SOCK_STREAM 表示有序、可靠、雙向的面向鏈接的字節流 ;SOCK_DGRAM 表示長度固定的、無鏈接的不可靠的報文傳遞。
  • protocol:通常是0,系統會根據前面的域名和類型,選擇合適的協議如TCP、UDP協議等。

    2.  int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);socket

  • sockfd:建立scoket時返回的socket描述符。相似於文件描述符號。
  • addr:socket綁定的地址
  • addrlen:第二個參數是指針,第三個參數是長度

       地址格式:ide

     struct sockaddr_in {函數

    sa_family_t    sin_family; /* address family: AF_INET */
    in_port_t      sin_port;   /* port in network byte order */
    struct in_addr sin_addr;   /* internet address */
 };

        注意:一般服務器在啓動的時候都會綁定一個衆所周知的地址(如ip地址+端口號),用於提供服務,客戶就能夠經過它來接連服務器;而客戶端就不用指定,有系統自動分配一個端口號和自身的ip地址組合。

                  這就是爲何一般服務器端在listen以前會調用bind(),而客戶端就不會調用,而是在connect()時由系統隨機生成一個。

   3.其它

  •   int listen(int sockfd, int backlog); //監聽socket的描述符,backlog表示最到鏈接數
  •  int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); //客戶端鏈接服務器的地址
  •   int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); //服務器接受鏈接
  •   read()/write()
  •   recv()/send()
  •   readv()/writev()
  •   recvmsg()/sendmsg()
  •   recvfrom()/sendto()  

3、簡單易學的socket程序示例

一個簡單的示例,建立AF_INET型域和SOCK_STREAM面向鏈接的socket字,server開啓服務,client請求鏈接,向server發送消息,server收到消息後,迴應client,結束鏈接,也關閉服務器。
代碼中有註釋,基本上是安裝前面socket通訊的步驟寫的代碼。
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <string.h>
 4 #include <unistd.h>
 5 #include <sys/types.h> 
 6 #include <sys/socket.h>
 7 #include <netinet/in.h>
 8 
 9 void error(const char *msg)
10 {
11     perror(msg);
12     exit(1);
13 }
14 
15 int main(int argc, char *argv[])
16 {
17      int sockfd, newsockfd, portno;
18      socklen_t clilen;
19      char buffer[256];
20      struct sockaddr_in serv_addr, cli_addr;
21      int n;
22      if (argc < 2) {
23          fprintf(stderr,"ERROR, no port provided\n");
24          exit(1);
25      }
26 
27      //create a socktet
28      sockfd = socket(AF_INET, SOCK_STREAM, 0);
29      if (sockfd < 0) 
30         error("ERROR opening socket");
31 
32     //bind an address to that socktet
33      bzero((char *) &serv_addr, sizeof(serv_addr));
34      portno = atoi(argv[1]);
35      serv_addr.sin_family = AF_INET;
36      serv_addr.sin_addr.s_addr = INADDR_ANY;
37      serv_addr.sin_port = htons(portno);
38      if (bind(sockfd, (struct sockaddr *) &serv_addr,
39               sizeof(serv_addr)) < 0) 
40               error("ERROR on binding");
41 
42      // listen to the socktet
43      listen(sockfd,5);
44 
45      //accept connection and create a corresponding new socket
46      clilen = sizeof(cli_addr);
47      newsockfd = accept(sockfd, 
48                  (struct sockaddr *) &cli_addr, 
49                  &clilen);
50      if (newsockfd < 0) 
51           error("ERROR on accept");
52 
53 
54       //communication with the new sockfd(read and write data)
55      bzero(buffer,256);
56      n = read(newsockfd,buffer,255);
57      if (n < 0) error("ERROR reading from socket");
58      printf("Here is the message: %s\n",buffer);
59      n = write(newsockfd,"I got your message",18);
60      if (n < 0) error("ERROR writing to socket");
61 
62      //ends the connection
63      close(newsockfd);
64 
65      //ends server
66      close(sockfd);
67      return 0; 
68 }
View Code
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <string.h>
 5 #include <sys/types.h>
 6 #include <sys/socket.h>
 7 #include <netinet/in.h>
 8 #include <netdb.h> 
 9 
10 void error(const char *msg)
11 {
12     perror(msg);
13     exit(0);
14 }
15 
16 int main(int argc, char *argv[])
17 {
18     int sockfd, portno, n;
19     struct sockaddr_in serv_addr;
20     struct hostent *server;
21 
22     char buffer[256];
23     if (argc < 3) {
24        fprintf(stderr,"usage %s hostname port\n", argv[0]);
25        exit(0);
26     }
27     portno = atoi(argv[2]);
28 
29     //create a socket
30     sockfd = socket(AF_INET, SOCK_STREAM, 0);
31     if (sockfd < 0) 
32         error("ERROR opening socket");
33 
34 
35     server = gethostbyname(argv[1]);
36     if (server == NULL) {
37         fprintf(stderr,"ERROR, no such host\n");
38         exit(0);
39     }
40     bzero((char *) &serv_addr, sizeof(serv_addr));
41     serv_addr.sin_family = AF_INET;
42     bcopy((char *)server->h_addr, 
43          (char *)&serv_addr.sin_addr.s_addr,
44          server->h_length);
45     serv_addr.sin_port = htons(portno);
46 
47     //connect
48     if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0) 
49         error("ERROR connecting");
50     printf("Please enter the message: ");
51     bzero(buffer,256);
52     fgets(buffer,255,stdin);
53 
54 
55     //write and read data
56     n = write(sockfd,buffer,strlen(buffer));
57     if (n < 0) 
58          error("ERROR writing to socket");
59     bzero(buffer,256);
60     n = read(sockfd,buffer,255);
61     if (n < 0) 
62          error("ERROR reading from socket");
63     printf("%s\n",buffer);
64 
65     
66     //end this connection
67     close(sockfd);
68     return 0;
69 }
View Code

 使用方法:./server 3000 ./client localhost 3000 通常使用2000~65536之間的端口號

 上面的代碼雖然能讓人很快理解,進程之間的網絡通訊是怎麼進行的。可是server只是接收一次消息,就立刻結束退出。

   而實際中的server是一直在運行的,而且可以同時接收多個client的訪問,典型的作法是server每次收到鏈接請求式都fork一個新的子進程來處理鏈接請求。

   爲了不產生zombie進程,須要在程序中使用signal(SIGCHLD,SIG_IGN);使得父進程無視子進程的die。

改進後的代碼:

 1 /* A simple server in the internet domain using TCP
 2    The port number is passed as an argument 
 3    This version runs forever, forking off a separate 
 4    process for each connection
 5 */
 6 #include <stdio.h>
 7 #include <unistd.h>
 8 #include <stdlib.h>
 9 #include <string.h>
10 #include <sys/types.h> 
11 #include <sys/socket.h>
12 #include <netinet/in.h>
13 #include <signal.h>
14 
15 void dostuff(int); /* function prototype */
16 void error(const char *msg)
17 {
18     perror(msg);
19     exit(1);
20 }
21 
22 int main(int argc, char *argv[])
23 {
24      int sockfd, newsockfd, portno, pid;
25      socklen_t clilen;
26      struct sockaddr_in serv_addr, cli_addr;
27 
28      if (argc < 2) {
29          fprintf(stderr,"ERROR, no port provided\n");
30          exit(1);
31      }
32      sockfd = socket(AF_INET, SOCK_STREAM, 0);
33      if (sockfd < 0) 
34         error("ERROR opening socket");
35 
36      bzero((char *) &serv_addr, sizeof(serv_addr));
37      portno = atoi(argv[1]);
38      serv_addr.sin_family = AF_INET;
39      serv_addr.sin_addr.s_addr = INADDR_ANY;
40      serv_addr.sin_port = htons(portno);
41      if (bind(sockfd, (struct sockaddr *) &serv_addr,
42               sizeof(serv_addr)) < 0) 
43               error("ERROR on binding");
44 
45      listen(sockfd,5);
46      clilen = sizeof(cli_addr);
47      signal(SIGCHLD,SIG_IGN);
48      while (1) {
49          newsockfd = accept(sockfd, 
50                (struct sockaddr *) &cli_addr, &clilen);
51          if (newsockfd < 0) 
52              error("ERROR on accept");
53          pid = fork();
54          if (pid < 0)
55              error("ERROR on fork");
56          if (pid == 0)  {
57              //close(sockfd);
58              dostuff(newsockfd);
59              exit(0);
60          }
61          else close(newsockfd);
62      } /* end of while */
63      close(sockfd);
64      return 0; /* we never get here */
65 }
66 
67 /******** DOSTUFF() *********************
68  There is a separate instance of this function 
69  for each connection.  It handles all communication
70  once a connnection has been established.
71  *****************************************/
72 void dostuff (int sock)
73 {
74    int n;
75    char buffer[256];
76       
77    bzero(buffer,256);
78    n = read(sock,buffer,255);
79    if (n < 0) error("ERROR reading from socket");
80    printf("Here is the message: %s\n",buffer);
81    n = write(sock,"I got your message",18);
82    if (n < 0) error("ERROR writing to socket");
83 }
View Code

  4、總結

socket幾乎是網絡間進程通訊的惟一手段,它的通訊是很是簡單而且重要的,須要記住。雖然在實際的大型的網絡服務器中,不會使用每次都使用socke系統調用t編程,而是使用包裝過的庫,可是仍是須要了解。

當讓要想成爲一個C++高手仍是須要熟悉某種網絡庫,能力強的話能夠本身包裝實現一個網絡庫。可是我以爲新手仍是先看看人家的庫~~如:好比boost-asio、好比libevent,boost-asio

 參考:

  1.http://www.linuxhowtos.org/C_C++/socket.htm?userrate=2

  2.http://www.cnblogs.com/skynet/archive/2010/12/12/1903949.html

相關文章
相關標籤/搜索