linux进程间通信---本地socket套接字(二)---多进程实现一个server对应多个client

**
先给自己打个广告,本人的微信公众号正式上线了,搜索:张笑生的地盘,主要关注嵌入式软件开发,股票基金定投,足球等等,希望大家多多关注,有问题可以直接留言给我,一定尽心尽力回答大家的问题。想要获取完整源码的,关注公众号后回复“socket2”即可
在这里插入图片描述

一 why

**
一般地,我们会遇到这样一个场景,一个server需要同时并发地处理多个client的连接以及数据交互。
**

二 what

**
一个server对应多个client的连接场景如下:
在这里插入图片描述
实现原理
对于父进程server.c来说

  1. 父进程server.c用来监听每一个client,只实现accept的动作
  2. 每次server.c父进程检测到一个新的client时,就会创建一个新的server子进程,用来和对应的client实现通信(数据交互)
  3. 当对应的client退出时,对应的server子进程也需要退出(读不到数据),否则会产生僵尸进程
    close(cfd); exit();
  4. 父进程server.c检测到子进程退出后,需要回收子进程
    信号 — SIGCHLD()
    waitpid();
  5. 父进程并不需要cfd,所以在父进程中需要关闭cfd, close(cfd);
    对于server子进程来说
    对每个server子进程:
  6. 子进程只需要cfd,并不需要lfd,所以每次进入子进程之后,需要关闭lfd, close(lfd);

**

三 how

**
server.c程序示例,想要获取完整源码的,关注公众号后回复“socket2”即可


void wait_child(int signo)
{
    while (waitpid(0, NULL, WNOHANG) > 0);
    return;
}

int main(int argc, char *argv[])
{
    ......
    //创建socket
    lfd = socket(AF_LOCAL, SOCK_STREAM, 0);
    if (lfd == -1) {
        perror("socket error");
        return -1;
    }

    //如果套接字文件存在,删除套接字文件
    unlink("server.sock");

    //初始化server信息
    serv.sun_family = AF_LOCAL;
    strcpy(serv.sun_path, "server.sock");

    //绑定
    ret = bind(lfd, (struct sockaddr *)&serv, sizeof(serv));
    if (ret == -1) {
        perror("bind error");
        return -1;
    }

    //设置监听,设置能够同时和服务端连接的客户端数量
    ret = listen(lfd, 36);
    if (ret == -1) {
        perror("listen error");
        return -1;
    }

    while (1) {
        //等待客户端连接
        cfd = accept(lfd, (struct sockaddr *)&client, &len);
        if (cfd == -1) {
            perror("accept error");
            return -1;
        }
        printf("=====client bind file:%s\n", client.sun_path);

        pid = fork();  //每次连接到一个新的客户端就创建一个子进程
        /* 创建子进程同于通信,但是由于这个while循环父子进程都会进来,所以针对子进程,我们需要做退出处理
         */
        ......
    }

    if (pid == 0) {
        while (1) {
            recvlen = recv(cfd, buf, sizeof(buf), 0);
            ......
        }
    }

    ......
}

客户端client代码示例


int main(int argc, char *argv[])
{
    ......

    //创建socket
    lfd = socket(AF_LOCAL, SOCK_STREAM, 0);
    if (lfd == -1) {
        perror("socket error");
        return -1;
    }

    //如果套接字文件存在,删除套接字文件
    unlink("client.sock");

    //给客户端绑定一个套接字文件
    client.sun_family = AF_LOCAL;
    strcpy(client.sun_path, "client.sock");
    ret = bind(lfd, (struct sockaddr *)&client, sizeof(client));
    if (ret == -1) {
        perror("bind error");
        return -1;
    }

    //初始化server信息
    serv.sun_family = AF_LOCAL;
    strcpy(serv.sun_path, "server.sock");
    //连接
    connect(lfd, (struct sockaddr *)&serv, sizeof(serv));

    while (1) {
        fgets(buf, sizeof(buf), stdin);
        send(lfd, buf, strlen(buf)+1, 0);

        recv(lfd, buf, sizeof(buf), 0);
        printf("recv buf %s\n", buf);
    }

    ......
}

上面的server使用的是创建多进程的方式来实现的,当然我们也可以采用多线程方式来实现,每次连接到一个客户端时,创建一个线程,用来实现和client的数据交互
server.c程序代码


struct s_info {   //定义一个结构体,将地址结构和cfd捆绑
    struct sockaddr_un cliaddr;
    int connfd;
};

void *do_work(void *arg)
{
    int n, i;
    struct s_info *ts = (struct s_info *)arg;
    char buf[1024] = {0};
    int recvlen;

    while (1) {
        recvlen = recv(ts->connfd, buf, sizeof(buf), 0);
        if (recvlen < 0) {
            perror("recv error");
            exit(1);
        } else if (recvlen == 0) {
            printf("client %d close...\n", ts->connfd);
            close(ts->connfd);
            return 0;
        } else {
            printf("recv buf %s\n", buf);
            send(ts->connfd, buf, recvlen, 0);
        }
    }

    close(ts->connfd);

    return NULL;
}

int main(int argc, char *argv[])
{
    int lfd ,ret, cfd;
    struct sockaddr_un serv, client;
    socklen_t len = sizeof(client);
    int i;
    pthread_t tid;
    struct s_info ts[256];    //根据最大线程数创建结构体数组

    //创建socket
    lfd = socket(AF_LOCAL, SOCK_STREAM, 0);
    if (lfd == -1) {
        perror("socket error");
        return -1;
    }

    //如果套接字文件存在,删除套接字文件
    unlink("server.sock");

    //初始化server信息
    serv.sun_family = AF_LOCAL;
    strcpy(serv.sun_path, "server.sock");

    //绑定
    ret = bind(lfd, (struct sockaddr *)&serv, sizeof(serv));
    if (ret == -1) {
        perror("bind error");
        return -1;
    }

    //设置监听,设置能够同时和服务端连接的客户端数量
    ret = listen(lfd, 36);
    if (ret == -1) {
        perror("listen error");
        return -1;
    }

    while (1) {
        //等待客户端连接
        cfd = accept(lfd, (struct sockaddr *)&client, &len);
        if (cfd == -1) {
            perror("accept error");
            return -1;
        }
        printf("=====client bind file:%s\n", client.sun_path);
        /* 创建子线程用于通信
         */
    }

    close(lfd);
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值