Linux网络编程-3、多进程并发服务器 1、父进程先注册信号捕捉函数(避免循环注册), 借助SIGCHLD信号回收子进程 2、accept()成功调用并返回才能fork()创建子进程 3、nc测试

1、正常的TCP下的C/S模型

2、父进程先注册信号捕捉函数(避免循环注册), 借助SIGCHLD信号回收子进程

3、accept()成功调用并返回才能fork()创建子进程

4、父进程关闭sfd(通信socket), 子进程关闭lfd(监听socket)

5、nc 127.0.0.1 9527多开客户端测试

#include "wrap.h"
#include <arpa/inet.h>
#define PORT 9527
#define MAXNUM 128
#include <ctype.h>
#include <signal.h>
#include <sys/wait.h>

// 信号捕捉函数
void func(int signo)
{
    pid_t wpid;
    printf("catch you SIGCHLD\n");
    // 6、捕捉到信号--循环回收子进程(非阻塞)
    while ((wpid = waitpid(-1, NULL, WNOHANG)) >= 0)
    {
        if (wpid > 0)
            printf("waitpid %d\n", wpid);
    }
    return;
}

int main(int argc, char *argv[])
{
    int lfd, sfd; // 2个套接字(监听 + 通信)
    int n;        // 读的字节数
    int ret;
    pid_t pid;
    // 1、Socket()监听专用套接字
    lfd = Socket(AF_INET, SOCK_STREAM, 0); // 采用ipv4 流式协议的典型协议TCP
    // 2、Bind()绑定服务器自己的sockaddr地址结构(ip + 端口号)
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(PORT);
    saddr.sin_addr.s_addr = htonl(INADDR_ANY); // INADDR_ANY获取本机可用的ip地址(本地字节序)
    Bind(lfd, (struct sockaddr *)&saddr, sizeof(saddr));
    // 3、设置监听上限
    Listen(lfd, MAXNUM);
    // 4、阻塞等待客户端建立连接--建立连接并新建1个通信专用socket
    struct sockaddr_in caddr; // 用来接收客户端的地址结构
    socklen_t caddrlen = sizeof(caddr);
    // 6、父进程得回收子进程(wait和waitpid不能用, 借助信号捕捉)
    sigset_t myset;                       // 1、定义自己的信号集
    sigemptyset(&myset);                  // 2、清空信号集(不屏蔽任何信号)
    sigprocmask(SIG_BLOCK, &myset, NULL); // 3、自定义的信号集短暂代替阻塞集(回调函数执行时)
    struct sigaction myact;
    myact.sa_handler = func;
    myact.sa_flags = 0;
    myact.sa_mask = myset;
    ret = sigaction(SIGCHLD, &myact, NULL); // 5、注册信号捕捉函数---一旦收到信号, 内核会回调捕捉函数(自定义行为)
    if (ret == -1)
        sys_error("sigaction error");
    while (1)
    {
        sfd = Accept(lfd, (struct sockaddr *)&caddr, &caddrlen); // 一直等待客户端连接
        pid = fork();                                            // 创建子进程
        if (pid == -1)
            sys_error("fork error");
        else if (pid > 0)
        {
            // 5、父进程并不需要通信
            Close(sfd); // 关闭accept新产生的socket
            // 注册信号捕捉1遍就够了, 不能放在循环里
            continue;
        }
        else
        {
            // 5、子进程并不需要监听
            Close(lfd); // 关闭监听专用的socket
            break;
        }
    }
    // 在外面写具体子进程xxx 逻辑更清楚
    if (pid == 0)
    {
        // 打印与服务器建立连接的客户端的地址结构(网络字节序下的(二进制的整数))
        char cip[50];
        inet_ntop(AF_INET, (void *)&caddr.sin_addr.s_addr, cip, sizeof(cip));
        printf("客户端ip地址: %s\n", cip);
        printf("客户端端口号: %u\n", ntohs(caddr.sin_port));
        // 5、读客户端发的数据
        char buf[BUFSIZ]; // 接收读到的数据
        while ((n = read(sfd, buf, BUFSIZ)) != 0)
        {
            // 打印到屏幕上
            printf("客户端发来的数据: %s", buf);
            // 转大写
            for (int i = 0; i < n; ++i)
                buf[i] = toupper(buf[i]);
            // 6、写给客户端
            ret = write(sfd, buf, n);
            if (ret == -1)
                sys_error("server write error");
        }
        // 7、读客户端的数据完毕--断开连接
        Close(sfd);
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

汪呈祥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值