Linux网络编程---多进/线程并发服务器

一、多进程并发服务器

实现一个服务器可以连接多个客户端,每当accept函数等待到客户端进行连接时 就创建一个子进程

思路分析:

核心思路:让accept循环阻塞等待客户端,每当有客户端连接时就fork子进程,让子进程去和客户端进行通信,父进程用于监听并使用信号捕捉回收子进程(子进程关闭用于监听的套接字lfd,父进程关闭用于通信的cfd)

服务端server.c代码如下:

cpp 复制代码
#include<stdio.h>
#include<stdlib.h>
#include<arpa/inet.h>
#include<ctype.h>
#include<sys/socket.h>
#include<unistd.h>
#include<string.h>
#include<strings.h>
#include<errno.h>
#include<pthread.h>
#include<signal.h>
#include<sys/wait.h>

#define SERV_PORT 9527

void sys_err(const char *str)
{
        perror(str);
        exit(1);
}

void catch_child(int signum)
{
        while(waitpid(0,NULL,WNOHANG) > 0);
        return ;
}

int main(int argc,char *argv[])
{
        int lfd = 0,cfd = 0;
        pid_t pid;
        int ret;
        char buf[BUFSIZ],client_IP[1024];//4096

        struct sockaddr_in serv_addr,clit_addr;
        socklen_t clit_addr_len;

        bzero(&serv_addr,sizeof(serv_addr));//将地址结构清零
        serv_addr.sin_family = AF_INET;
        serv_addr.sin_port = htons(SERV_PORT);
        serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);

        lfd = socket(AF_INET,SOCK_STREAM,0);
        if (lfd == -1)
        {
                sys_err("socket errno");
        }

        bind(lfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));

        listen(lfd,128);

        clit_addr_len = sizeof(clit_addr);

        while(1)
        {
                cfd = accept(lfd,(struct sockaddr *)&clit_addr,&clit_addr_len);
                if (cfd == -1)
                {
                        sys_err("accept error");         
                }
                printf("client ip is:%s port:%d\n",inet_ntop(AF_INET,&clit_addr.sin_addr.s_addr,client_IP,sizeof(client_IP)),ntohs(clit_addr.sin_port));

                pid = fork();
                if (pid < 0)
                {
                        sys_err("fork error");
                }
                else if (pid == 0)
                {
                        close(lfd);
                        break;
                }
                else
                {
                        struct sigaction act;

                        act.sa_handler = catch_child;
                        sigemptyset(&(act.sa_mask));
                        act.sa_flags = 0;

                        ret = sigaction(SIGCHLD,&act,NULL);
                        if (ret != 0)
                        {
                                sys_err("sigaction error");
                        }
                        close(cfd);
                        continue;
                }
        }

        if (pid == 0)
        {
                for(;;)
                {
                        ret = read(cfd,buf,sizeof(buf));
                        if (ret == 0)
                        {
                                close(cfd);
                                exit(1);
                        }

                        for (int i = 0;i<ret;i++)
                        {
                                buf[i] = toupper(buf[i]);
                        }

                        write(cfd,buf,ret);
                        write(STDOUT_FILENO,buf,ret);
                }
        }
        
        return 0;
}

一个服务端三个客户端执行多进程服务器并发输出如下:

补充:

二、多线程并发服务器:

思路分析:

核心思路:设置监听循环由主线程阻塞等待客户端连接,每当接受到新的客户端连接时,主线程会创建一个新的子线程来处理与该客户端的通信,而主线程继续执行监听循环等待其他客户端(主线程专注于接受新连接,子线程专注于处理客户端请求)

服务端server.c代码如下:

cpp 复制代码
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>

#include "wrap.h"

#define MAXLINE 8192
#define SERV_PORT 8000

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

void *do_work(void *arg)
{
    int n,i;
    struct s_info *ts = (struct s_info*)arg;
    char buf[MAXLINE];
    char str[INET_ADDRSTRLEN];      //#define INET_ADDRSTRLEN 16  可用"[+d"查看

    while (1) {
        n = Read(ts->connfd, buf, MAXLINE);  //读客户端
        if (n == 0) {
            printf("the client %d closed...\n", ts->connfd);
            break;                           //跳出循环,关闭cfd
        }
        printf("received from %s at PORT %d\n",
                inet_ntop(AF_INET, &(*ts).cliaddr.sin_addr, str, sizeof(str)),
                ntohs((*ts).cliaddr.sin_port));  //打印客户端信息(IP/PORT)

        for (i = 0; i < n; i++) 
            buf[i] = toupper(buf[i]);            //小写-->大写

        Write(STDOUT_FILENO, buf, n);           //写出至屏幕
        Write(ts->connfd, buf, n);              //回写给客户端
    }
    Close(ts->connfd);

    return (void *)0;            //pthread_exit(0);
}

int main(void)
{
    struct sockaddr_in servaddr, cliaddr;
    socklen_t cliaddr_len;
    int listenfd, connfd;
    pthread_t tid;

    struct s_info ts[256];      //创建结构体数组.
    int i = 0;

    listenfd = Socket(AF_INET, SOCK_STREAM, 0);        //创建一个socket, 得到lfd

    bzero(&servaddr, sizeof(servaddr));               //地址结构清零
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);     //指定本地任意IP
    servaddr.sin_port = htons(SERV_PORT);             //指定端口号 

    Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));   //绑定

    Listen(listenfd, 128);             //设置同一时刻链接服务器上限数

    printf("Accepting client connect ...\n");

    while (1) {
        cliaddr_len = sizeof(cliaddr);
        connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len); //阻塞监听客户端链接请求
        ts[i].cliaddr = cliaddr;    //把文件描述符和客户端地地址结构组织到一个结构体变量中
        ts[i].connfd = connfd;

        pthread_create(&tid, NULL, do_work, (void*)&ts[i]);//表结构体变量作为参数传递到子线程
        pthread_detach(tid);                //子线程分离,防止僵线程产生.
        i++;
    }

    return 0;
}

执行输出如下:

相关推荐
ulias2124 小时前
Linux系统中的权限问题
linux·运维·服务器
青花瓷5 小时前
Ubuntu下OpenClaw的安装(豆包火山API版)
运维·服务器·ubuntu
问简5 小时前
docker 镜像相关
运维·docker·容器
Dream of maid6 小时前
Linux(下)
linux·运维·服务器
齐鲁大虾6 小时前
统信系统UOS常用命令集
linux·运维·服务器
Benszen6 小时前
Docker容器化技术实战指南
运维·docker·容器
ZzzZZzzzZZZzzzz…6 小时前
Nginx 平滑升级:从 1.26.3 到 1.28.0,用户无感知
linux·运维·nginx·平滑升级·nginx1.26.3·nginx1.28.0
一叶知秋yyds8 小时前
Ubuntu 虚拟机安装 OpenClaw 完整流程
linux·运维·ubuntu·openclaw
专吃海绵宝宝菠萝屋的派大星8 小时前
使用Dify对接自己开发的mcp
java·服务器·前端
斯普信云原生组8 小时前
Prometheus 环境监控虚机 Redis 方案(生产实操版)
运维·docker·容器