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

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

周欣 ( jones_zhou@yahoo.com.cn), 软件工程师

简介: 套接口(Socket)为目前 Linux 上最为广泛使用的一种的进程间通信机制。但是它不能直接用来多进程之间的相互实时通信。本文提出一个基于 Socket 的多进程之间通信的实现方法,并给出样本程序的实现和说明。

本文的标签:  linux, socket, 上实现基于, 在, 的多进程实时通信

发布日期: 2005 年 3 月 01 日
级别: 初级
访问情况 : 8722 次浏览
评论: 1 (查看 | 添加评论 - 登录)

平均分 4 星 共 21 个评分 平均分 (21个评分)
为本文评分

套接口(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


程序清单下载:server.c,clientA.c


关于作者

周欣,北京邮电大学计算机系硕士毕业, 现在日本从事软件开发,通过 jones_zhou@yahoo.com.cn 可以和他联系。

转载自 http://www.ibm.com/developerworks/cn/linux/l-socket-ipc/index.html
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现Linux多进程本地socket通信接口,可以使用以下步骤: 1. 创建一个socket文件描述符:使用`socket()`函数创建一个本地socket文件描述符,指定协议族为`AF_UNIX`。 2. 绑定socket地址:使用`bind()`函数将socket绑定到一个本地文件路径上。 3. 监听socket:如果需要让socket接受连接请求,可以使用`listen()`函数将socket设置为监听状态。 4. 接受连接请求:如果socket是处于监听状态,可以使用`accept()`函数接受连接请求,并返回一个新的socket文件描述符。 5. 发送和接收数据:使用`send()`和`recv()`函数向连接的另一端发送和接收数据。 下面是一个简单的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/socket.h> #include <sys/un.h> #define SOCK_PATH "/tmp/mysocket" int main(void) { int s, s2, len; socklen_t t; struct sockaddr_un local, remote; char str[100]; // 创建socket if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { perror("socket"); exit(1); } // 绑定socket到本地文件路径 local.sun_family = AF_UNIX; strcpy(local.sun_path, SOCK_PATH); unlink(local.sun_path); len = strlen(local.sun_path) + sizeof(local.sun_family); if (bind(s, (struct sockaddr *)&local, len) == -1) { perror("bind"); exit(1); } // 监听socket,设置最大连接数为5 if (listen(s, 5) == -1) { perror("listen"); exit(1); } printf("Waiting for a connection...\n"); // 接受连接请求 t = sizeof(remote); if ((s2 = accept(s, (struct sockaddr *)&remote, &t)) == -1) { perror("accept"); exit(1); } printf("Connected.\n"); // 发送数据 if (send(s2, "Hello, world!", 13, 0) == -1) { perror("send"); exit(1); } // 接收数据 if ((len = recv(s2, str, 100, 0)) == -1) { perror("recv"); exit(1); } str[len] = '\0'; printf("Received: %s\n", str); // 关闭socket close(s2); close(s); return 0; } ``` 这个示例代码可以在终端中运行,将会监听本地文件路径`/tmp/mysocket`,等待连接请求,一旦有连接请求到来,就会发送一条消息,然后等待接收对方发送的消息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值