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;
}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
设计思路: 本文将介绍如何使用C/C++编写一个多客户端并发访问服务器的网络服务能力测试工具。该工具可以模拟多个客户端同时向服务器发送请求,测试服务器的并发处理能力。 1.服务器端: (1)创建套接字 (2)绑定地址和端口 (3)监听连接 (4)接受连接请求 (5)创建子进程,子进程与客户端通信 (6)关闭套接字 2.客户端: (1)创建套接字 (2)连接服务器 (3)发送请求 (4)接收响应 (5)关闭套接字 实现步骤: 1.服务器端: (1)创建套接字: 使用socket函数创建套接字。代码如下: ``` int server_socket = socket(AF_INET, SOCK_STREAM, 0); ``` (2)绑定地址和端口: 使用bind函数将套接字与指定的地址和端口绑定。代码如下: ``` struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = htonl(INADDR_ANY); server_addr.sin_port = htons(PORT); bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)); ``` (3)监听连接: 使用listen函数开始监听连接请求。代码如下: ``` listen(server_socket, 5); ``` (4)接受连接请求: 使用accept函数接受连接请求,并创建子进程处理连接。代码如下: ``` while(1) { int client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &client_len); if(fork() == 0) { // 子进程处理连接 close(server_socket); // ... exit(0); } close(client_socket); } ``` (5)创建子进程,子进程与客户端通信: 在子进程中,使用recv函数接收客户端发送的请求,使用send函数向客户端发送响应。代码如下: ``` while(1) { char buffer[BUFFER_SIZE]; int len = recv(client_socket, buffer, BUFFER_SIZE, 0); if(len == 0) break; // 处理请求 send(client_socket, response, strlen(response), 0); } ``` (6)关闭套接字: 在服务器结束时,使用close函数关闭套接字。代码如下: ``` close(server_socket); ``` 2.客户端: (1)创建套接字: 使用socket函数创建套接字。代码如下: ``` int client_socket = socket(AF_INET, SOCK_STREAM, 0); ``` (2)连接服务器: 使用connect函数连接服务器。代码如下: ``` struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = inet_addr(SERVER_IP); server_addr.sin_port = htons(PORT); connect(client_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)); ``` (3)发送请求: 使用send函数向服务器发送请求。代码如下: ``` send(client_socket, request, strlen(request), 0); ``` (4)接收响应: 使用recv函数接收服务器的响应。代码如下: ``` char buffer[BUFFER_SIZE]; recv(client_socket, buffer, BUFFER_SIZE, 0); ``` (5)关闭套接字: 在客户端结束时,使用close函数关闭套接字。代码如下: ``` close(client_socket); ``` 完整代码: 服务器端: ``` #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <unistd.h> #define PORT 8080 #define BUFFER_SIZE 1024 int main() { int server_socket = socket(AF_INET, SOCK_STREAM, 0); if(server_socket == -1) { perror("socket"); exit(1); } struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = htonl(INADDR_ANY); server_addr.sin_port = htons(PORT); if(bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) { perror("bind"); exit(1); } if(listen(server_socket, 5) == -1) { perror("listen"); exit(1); } while(1) { struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr); int client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &client_len); if(client_socket == -1) { perror("accept"); exit(1); } if(fork() == 0) { close(server_socket); while(1) { char buffer[BUFFER_SIZE]; int len = recv(client_socket, buffer, BUFFER_SIZE, 0); if(len == 0) break; // 处理请求 char* response = "Hello, World!"; send(client_socket, response, strlen(response), 0); } close(client_socket); exit(0); } close(client_socket); } close(server_socket); return 0; } ``` 客户端: ``` #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <unistd.h> #include <arpa/inet.h> #define SERVER_IP "127.0.0.1" #define PORT 8080 #define BUFFER_SIZE 1024 int main() { int client_socket = socket(AF_INET, SOCK_STREAM, 0); if(client_socket == -1) { perror("socket"); exit(1); } struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = inet_addr(SERVER_IP); server_addr.sin_port = htons(PORT); if(connect(client_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) { perror("connect"); exit(1); } char* request = "Hello!"; send(client_socket, request, strlen(request), 0); char buffer[BUFFER_SIZE]; recv(client_socket, buffer, BUFFER_SIZE, 0); printf("%s\n", buffer); close(client_socket); return 0; } ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值