Webserver(4.4)多进程/多线程实现并发服务器

目录

多进程实现并发服务器

要实现TCP服务器处理并发的任务,使用多线程或者多进程来解决

一个父进程,多个子进程

父进程负责等待并接受客户端的连接

子进程:完成通信,接收一个客户端连接,就创建一个子进程用于通信

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

void recyleChild(int arg){
    while(1){
        int ret=waitpid(-1,NULL,WNOHANG);
        if(ret==-1){
            //所有子进程都回收了
            break;
        }else if(ret==0){
            //还有子进程活着
            break;
        }else if(ret>0){
            //被回收了
            printf("子进程 %d 被回收了\n",ret);
        }
    }
}
int main(){

    struct sigaction act;
    act.sa_flags=0;
    sigemptyset(&act.sa_mask);
    act.sa_handler=recyleChild;
    //注册信号捕捉
    sigaction(SIGCHLD,&act,NULL);

    //创建socket
    int lfd=socket(PF_INET,SOCK_STREAM,0);
    if(lfd==-1){
        perror("socket");
        exit(-1);
    }

    struct sockaddr_in saddr;
    saddr.sin_family=AF_INET;
    saddr.sin_port=htons(9999);
    saddr.sin_addr.s_addr=INADDR_ANY;

    //绑定
    int ret=bind(lfd,(struct sockaddr *)&saddr,sizeof(saddr));
    if(ret==-1){
        perror("bind");
        exit(0);
    }


    //监听
    ret=listen(lfd,128);
    if(ret==-1){
        perror("listen");
        exit(-1);
    }

    //不断循环,等待客户端连接
    while(1){
        struct sockaddr_in cliaddr;
        int len=sizeof(cliaddr);

        //接受连接
        int cfd=accept(lfd,(struct sockaddr*)&cliaddr,&len);
        if(cfd==-1){
            if(errno==EINTR){
                continue;
            }
            perror("accept");
            exit(-1);
        }

        //每一个连接进来,创建一个子进程跟客户端通信
        pid_t pid=fork();
        if(pid==0){
            //子进程
            //获取客户端的信息
            char cliIp[16];
            inet_ntop(AF_INET,&cliaddr.sin_addr.s_addr,cliIp,sizeof(cliIp));

            unsigned short cliPort=ntohs(cliaddr.sin_port);

            printf("client ip is:%s,port is %d\n",cliIp,cliPort);

            //接收客户端发来的数据
            char recvBuf[1024]={0};
            while(1){
                int len=read(cfd,&recvBuf,sizeof(recvBuf));
                if(len==-1){
                    perror("read");
                    exit(-1);
                }else if(len>0){
                    printf("recv client data:%s\n",recvBuf);
                }else {
                    printf("client close...");
                    break;
                }
                write(cfd,recvBuf,strlen(recvBuf));
            }
            close(cfd);
            exit(0);//退出当前子进程
        }


    }
    close(lfd);


    return 0;
}
cpp 复制代码
#include<stdio.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>

int main(){

    //1.创建套接字
    int fd=socket(AF_INET,SOCK_STREAM,0);
    if(fd==-1){
        perror("socket");
        exit(-1);
    }

    //2.连接服务器端
    struct sockaddr_in serveraddr;
    serveraddr.sin_family=AF_INET;
    inet_pton(AF_INET,"192.168.227.129",&serveraddr.sin_addr.s_addr);
    serveraddr.sin_port=htons(9999);
    int ret=connect(fd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
    if(ret==-1){
        perror("connect");
        exit(-1);
    }

    char recvBuf[1024]={0};
    int i=0;
    //3.通信
    while(1){
        sprintf(recvBuf,"data:%d\n",i++);
        //给服务器发送数据
        write(fd,recvBuf,strlen(recvBuf));
        sleep(1);
        
        int len=read(fd,recvBuf,sizeof(recvBuf));
        if(len==-1){
            perror("read");
            exit(-1);
        }else if(len>0){
            printf("recv server data:%s\n",recvBuf);
        }else if(len==0){
            //表示客户端断开连接
            printf("server closed...");
            break;
        }
    }
   

    //关闭连接
    close(fd);



    return 0;
}

同时开启两个客户端,会交叉接收信息

这样就支持了并发,多个客户端

多线程实现并发服务器

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

struct sockInfo{
    int fd;//通信的文件描述符
    struct sockaddr_in addr;
    pthread_t tid;//线程号
};

struct sockInfo sockinfos[128];

void * working(void * arg){

    //子线程和客户端通信
    //获取客户端的信息
    struct sockInfo * pinfo =(struct sockInfo *)arg;
    char cliIp[16];
    inet_ntop(AF_INET,&pinfo->addr.sin_addr.s_addr,cliIp,sizeof(cliIp));

    unsigned short cliPort=ntohs(pinfo->addr.sin_port);

    printf("client ip is:%s,port is %d\n",cliIp,cliPort);

    //接收客户端发来的数据
    char recvBuf[1024];
    while(1){
        int len=read(pinfo->fd,&recvBuf,sizeof(recvBuf));
        if(len==-1){
            perror("read");
            exit(-1);
        }else if(len>0){
            printf("recv client data:%s\n",recvBuf);
        }else if(len==0) {
            printf("client closed...\n");
            break;
        }
        write(pinfo->fd,recvBuf,strlen(recvBuf)+1);
    }
    close(pinfo->fd);

    return NULL;
}
int main(){

    //创建socket
    int lfd=socket(PF_INET,SOCK_STREAM,0);
    if(lfd==-1){
        perror("socket");
        exit(-1);
    }

    struct sockaddr_in saddr;
    saddr.sin_family=AF_INET;
    saddr.sin_port=htons(9999);
    saddr.sin_addr.s_addr=INADDR_ANY;

    //绑定
    int ret=bind(lfd,(struct sockaddr *)&saddr,sizeof(saddr));
    if(ret==-1){
        perror("bind");
        exit(0);
    }


    //监听
    ret=listen(lfd,128);
    if(ret==-1){
        perror("listen");
        exit(-1);
    }

    //初始化数据
    int max=sizeof(sockinfos)/sizeof(sockinfos[0]);
    for(int i=0;i<max;i++){
        bzero(&sockinfos[i],sizeof(sockinfos[i]));
        sockinfos[i].fd=-1;
        sockinfos[i].tid=-1;
    }

    //循环等待客户端连接
    while(1){
        struct sockaddr_in cliaddr;
        int len=sizeof(cliaddr);

        //接受连接
        int cfd=accept(lfd,(struct sockaddr*)&cliaddr,&len);
        

        struct sockInfo * pinfo;
        for(int i=0;i<max;i++){
            //从这个数组中找到一个可以用的sockInfo元素
            if(sockinfos[i].fd==-1){
                pinfo=&sockinfos[i];
                break;
            }
            if(i==max-1){
                sleep(1);
                i--;
            }
        }
        pinfo->fd=cfd;
        memcpy(&pinfo->addr,&cliaddr,len);
        //创建子线程
        pthread_create(&pinfo->tid,NULL,working,pinfo);

        pthread_detach(pinfo->tid);


    }
    close(lfd);



    return 0;
}
cpp 复制代码
#include<stdio.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>

int main(){

    //1.创建套接字
    int fd=socket(AF_INET,SOCK_STREAM,0);
    if(fd==-1){
        perror("socket");
        exit(-1);
    }

    //2.连接服务器端
    struct sockaddr_in serveraddr;
    serveraddr.sin_family=AF_INET;
    inet_pton(AF_INET,"192.168.227.129",&serveraddr.sin_addr.s_addr);
    serveraddr.sin_port=htons(9999);
    int ret=connect(fd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
    if(ret==-1){
        perror("connect");
        exit(-1);
    }

    char recvBuf[1024]={0};
    int i=0;
    //3.通信
    while(1){
        sprintf(recvBuf,"data:%d\n",i++);
        //给服务器发送数据
        write(fd,recvBuf,strlen(recvBuf));
        
        int len=read(fd,recvBuf,sizeof(recvBuf));
        if(len==-1){
            perror("read");
            exit(-1);
        }else if(len>0){
            printf("recv server data:%s\n",recvBuf);
        }else if(len==0){
            //表示客户端断开连接
            printf("server closed...\n");
            break;
        }
        sleep(1);
    }
   

    //关闭连接
    close(fd);



    return 0;
}

TCP状态转换

相关推荐
大树882 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠2 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质2 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
小宇宙Zz2 天前
Maven依赖冲突
java·服务器·maven
Inhand陈工3 天前
基于台达PLC与映翰通IG502的智慧水产养殖精准投喂与远程运维解决方案
运维·人工智能·物联网·阿里云·信息与通信
酣大智3 天前
ARP代理--工作原理
运维·网络·arp·arp代理
shushangyun_3 天前
2026年快消品B2B系统推荐:支持终端门店订货、促销政策自动化的工具?
java·运维·网络·数据库·人工智能·spring·自动化
古城小栈3 天前
Unix 与 Linux 异同小叙
linux·服务器·unix
施努卡机器视觉3 天前
SNK施努卡侧滑门锁上滑轮总成自动化装配线,从零件到组件,全流程精密制造方案
运维·自动化·制造
程序猿阿伟3 天前
《Chrome离线扩展安装的底层逻辑与场景落地指南》
服务器·网络·chrome