zoukankan      html  css  js  c++  java
  • linux进程间的网络通信

    一、进程是如何进行网络通信的?socket通信的过程?

      同一机器上的不同进程之间的通信方式有很多种,主要使用消息传递或共享内存。而跨网络的进程是几乎都是使用socket通信,例如web服务器,QQ。

        socket即是一种特殊的文件,操作系统提供了一些socket函数就是对其进行的操作(读/写IO、打开、关闭),进程间的通信就是靠读写各自的socket完成的。

        通信的过程

        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()函数。

    二、主要知识点和系统调用介绍

      1. int socket(int domain, int type, int protocol); 创建指定类型的socket,两个进程能够通信,必须使用相同域和类型的套接字

    •  domain:主要有AF_INET,AF_INIT6,分别表示IPv4、IPv6域
    •  type:SOCK_STREAM 表示有序、可靠、双向的面向连接的字节流 ;SOCK_DGRAM 表示长度固定的、无连接的不可靠的报文传递。
    • protocol:一般是0,系统会根据前面的域名和类型,选择合适的协议如TCP、UDP协议等。

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

    • sockfd:创建scoket时返回的socket描述符。类似于文件描述符号。
    • addr:socket绑定的地址
    • addrlen:第二个参数是指针,第三个参数是长度

           地址格式:

         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()  

    三、简单易学的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
    ");
    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
    ",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
    ", 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
    ");
    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
    ",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
    ");
    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
    ",buffer);
    81    n = write(sock,"I got your message",18);
    82    if (n < 0) error("ERROR writing to socket");
    83 }
    View Code

      四、总结

    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

  • 相关阅读:
    6.VUE事件处理
    springmvc在使用@ModelAttribute注解获取Request和Response会产生线程并发不安全问题
    IDEAhttp://lookdiv.com/index/index/indexcodeindex.html
    不四舍五入保留...4(round(273.86015,4,1);)
    spring security中@PreAuthorize、@PostAuthorize、@PreFilter和@PostFilter四者的区别
    @RepeatSubmit spring boot 防止重复提交
    权限设计的杂谈
    vue设置全局样式变量 less
    坐标轴刻度取值算法-基于魔数数组-源于echarts的y轴刻度计算需求
    less使用
  • 原文地址:https://www.cnblogs.com/wxquare/p/5027948.html
Copyright © 2011-2022 走看看