在 Linux 上实现基于 Socket 的多进程实时通信

来源:互联网 发布:迪士尼影业 知乎 编辑:程序博客网 时间:2024/06/04 18:41
 

套接口(Socket)为目前Linux上最为广泛使用的一种的进程间通信机制,与其他的Linux通信机制不同之处在于除了它可用于单机内的进程间通信以外,还可用于不同机器之间的进程间通信。但是由于Socket本身不支持同时等待和超时处理,所以它不能直接用来多进程之间的相互实时通信。

本文提出一个基于Socket的多进程之间通信的实现方法。原理是建立一个进程专门用来做为通信服务器(server)来中转各个进程之间的通信。它首先启动一个用来监视连接要求的listening Socket,并把它的描述(Descriptor)号加入到一个事先定义好的fd_set的集合中,这个fd_set的集合用来存放listening Socket和后来生成的通信Socket的描述号。Server运用system call select来实时检查是否有数据到达这个集合中的任何一个socket,如果有数据到达listening Socket,则这一定是客户端发起的连接请求,于是生成一个新的通信Socket与该客户端连接,将生成的Socket描述号加入到fd_set的集合中,将客户端的ID号和与之对应的Socket的描述号记录在ID登记表中。如果有数据到达某个通信Socket,则这一定是某个客户端发起的通信请求,读出数据并取出收信客户端ID号,在ID登记表中找到与之对应的Socket描述号,将数据通过对应Socket传送到收信客户端。

其他各进程作为客户端(client)。客户端的动作是首先建立通信Socket连接服务器端,然后通过通信Socket进行送信和收信。

下面给出具体的程序实现和说明,

首先给出Server端的程序,在这里假设有两个客户端要进行实时通信,ClientA向ClientB发送字符1,ClientB向ClientA发送字符2。

  #include  <sys/types.h>

#include <sys/socket.h>

#include <stdio.h>

#include <sys/un.h>

#include <sys/time.h>

#include <sys/ioctl.h>

#include <unistd.h>

#include <netinet/in.h>

int main()

{

  int        rcd ;

  struct sockaddr_un  server_sockaddr ;

  int        backlog ;

  ushort        ci ;

  int        watch_fd_list[3] ;

  fd_set        catch_fd_set ;

  fd_set        watchset ;

  int    new_cli_fd ;

  int   maxfd;

  int    socklen ,server_len;

  struct sockaddr_un  cli_sockaddr ;

  struct {

    char  module_id ;  /* Module ID    */

    int  cli_sock_fd ;  /* Socket ID    */

  } cli_info_t[2] ;

 

  for (ci=0;ci<=1;ci++)

    cli_info_t[ci].cli_sock_fd=-1;

   

  for (ci=0;ci<=2;ci++)

    watch_fd_list[ci]=-1; 

   

  int server_sockfd,client_sockfd;

 

  server_sockfd = socket( AF_UNIX, SOCK_STREAM, 0 ) ;

  server_sockaddr.sun_family = AF_UNIX ;

  strcpy( server_sockaddr.sun_path, "server_socket" ) ;

  server_len=sizeof(server_sockaddr);

  rcd = bind( server_sockfd, ( struct sockaddr * )&server_sockaddr, server_len ) ;

 

  backlog = 5 ;

  rcd = listen( server_sockfd, backlog ) ;

  printf("SERVER::Server is  waitting on socket=%d \n",server_sockfd);

  watch_fd_list[0]=server_sockfd;

  FD_ZERO( &watchset ) ;

  FD_SET( server_sockfd, &watchset ) ;

  maxfd=watch_fd_list[0];

 

        

 

在上面的程序中,Server生成listening Socket(server_sockfd),初始化Socket监视集合(watchset),并将listening Socket放入Socket监视集合中。

                          while (1){

                 char ch;

                 int fd;

                 int nread;

                

                 catch_fd_set=watchset;

                 rcd = select( maxfd+1, &catch_fd_set, NULL, NULL, (struct timeval *)0 ) ;

                

 

在上面的程序中,Server运用系统调用函数 select来实时检查是否有数据到达Socket监视集合中的任何一个socket。

        if ( rcd < 0 ) {

      printf("SERVER::Server 5 \n");

      exit(1);

    }

    if ( FD_ISSET( server_sockfd, &catch_fd_set ) ) {

      socklen = sizeof( cli_sockaddr ) ;

      new_cli_fd = accept( server_sockfd, ( struct sockaddr * )

        &( cli_sockaddr ), &socklen ) ;

      printf(" SERVER::open communication with  Client %s on socket %d\n",

           cli_sockaddr.sun_path,new_cli_fd); 

 

      for (ci=1;ci<=2;ci++){

        if(watch_fd_list[ci] != -1) continue;

        else{ 

          watch_fd_list[ci] = new_cli_fd;

          break;

        } 

      } 

      FD_SET(new_cli_fd , &watchset ) ;

      if ( maxfd < new_cli_fd ) {

        maxfd = new_cli_fd ;

      }

     

      for ( ci=0;ci<=1;ci++){

        if(cli_info_t[ci].cli_sock_fd == -1) {

          cli_info_t[ci].module_id=cli_sockaddr.sun_path[0];

          cli_info_t[ci].cli_sock_fd=new_cli_fd;

          break;

        }   

      }

       

      continue; 

    }

                

 

在上面的程序中,Server运用系统调用函数FD_ISSET来检查是否有客户端的连接请求到达Listening Socket, 如果返回值大于0,Server生成一个新的通信Socket (new_cli_fd)与客户端连接。将新生成的通信Socket放入Socket监视集合中(FD_SET)。将客户端的信息(ID号和Socket描述号)保存在注册表cli_info_t中

    for ( ci = 1; ci<=2 ; ci++ ) {

      int      dst_fd = -1 ;

      char      dst_module_id;

      char       src_module_id;

      int    i;

      if (watch_fd_list[ ci ]==-1) continue;

      if ( !FD_ISSET( watch_fd_list[ ci ], &catch_fd_set ) ) {

        continue ;

      }

      ioctl(watch_fd_list[ ci ],FIONREAD,&nread);

      if (nread==0){

        continue;

      } 

      read( watch_fd_list[ ci ], &dst_module_id, 1 ) ;

      for (i=0;i<=1;i++){

        if(cli_info_t[i].module_id == dst_module_id)

          dst_fd=  cli_info_t[i].cli_sock_fd; 

         if(cli_info_t[i].cli_sock_fd==watch_fd_list[ ci ])

        src_module_id=  cli_info_t[i].module_id;   

      }

      read( watch_fd_list[ ci ], &ch, 1 ) ;

      printf("SERVER::char=%c to  Client %c on socket%d\n",ch, dst_module_id,dst_fd); 

      write(dst_fd,&src_module_id, 1 ) ;

      write(dst_fd,&ch, 1 ) ;

    }

  } 

}

 

在上面的程序中,如果有数据到达某个通信Socket,Server则读出数据并取出收信客户端ID号。在ID登记表中找到收信客户端对应的Socket描述号。并将数据通过对应Socket传送到收信客户端

给出客户端 ClientA的程序

ClientB的程序只需将 char dst_module_id='B'; 改为char dst_module_id='A'; char ch='1'; 改为char char ch='2';既可。

#include  <sys/types.h>

#include <sys/socket.h>

#include <stdio.h>

#include <sys/un.h>

#include <unistd.h>

#include <netinet/in.h>

#include <arpa/inet.h>

int main(){

        

         int client_sockfd;

         int len;

         struct sockaddr_un server_sockaddr,cli_sockaddr;

         int result;

         char dst_module_id='B';

         char ch='1';

         char src_module_id;

        

        

         client_sockfd= socket(AF_UNIX,SOCK_STREAM,0);

        

         cli_sockaddr.sun_family = AF_UNIX ;

         strcpy( cli_sockaddr.sun_path, "A" ) ;

         bind(client_sockfd,(struct sockaddr * )&cli_sockaddr, sizeof( cli_sockaddr ) ) ;

         server_sockaddr.sun_family=AF_UNIX;

         strcpy( server_sockaddr.sun_path, "server_socket" ) ;

         len=sizeof(server_sockaddr);

        

         result = connect(client_sockfd,( struct sockaddr * )&server_sockaddr,len);

         if (result <0){

                 printf("ClientA::error on connecting \n"); 

                 exit(1);

         }

        

         printf("ClientA::succeed in connecting with server\n");

         sleep(10);

         write(client_sockfd,&dst_module_id,1);

         write(client_sockfd,&ch,1);

         read (client_sockfd,&src_module_id,1);

         read (client_sockfd,&ch,1);

         printf("ClientA::char from  Client %c =%c\n", src_module_id,ch);     

         close (client_sockfd);

        

}       

 

下面是样本程序的执行结果

[root@zhou test]# ./server &

[3] 4301

[root@zhou test]# SERVER::Server is  waitting on socket=3

./clientA & ./clientB &

[4] 4302

[5] 4303

ClientA::succeed in connecting with server

 SERVER::open communication with  Client A on socket 4

[root@zhou test]#  SERVER::open communication with  Client B on socket 5

ClientB::succeed in connecting with server

SERVER::char=1 to  Client B on socket5

ClientB::char from  Client A =1

SERVER::char=2 to  Client A on socket4

ClientA::char from  Client B =2

原创粉丝点击