小白一步一步学并发编程(2)—— tcp多进程并发

本文深入探讨了TCP并发服务的实现方式,重点介绍了多进程并发模型。通过创建子进程处理每个客户端连接,父进程持续监听新的连接请求,有效提高了服务器的并发处理能力。文章详细解析了代码实现过程,包括socket编程、子进程创建、信号处理等关键环节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

tcp并发服务,对于初学者来说,很容易想到多进程的并发,虽然很少有服务只是用多进程来处理并发,而且往往并不实际,因为多进程会占用很多系统资源。但现在很多服务,为了进一步提升并发,或者处理并行计算,一般考虑分布式,多进程多机器处理请求和计算。

多进程并发头文件tcp_server_process.h

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <thread>
#include <pthread.h>
#include <string>
#include <mutex>
#include "tcp_server.h"


//using namespace std;

#define MAXLINE 4096

class ProcessServer : public Server{
        public:
                //derived class constructor init with params
                ProcessServer(string ip, int port):Server(ip, port){
                }
                int start();
};

实现文件:服务端程序,每接到一个连接请求,就fork一个子进程,父进程继续等待接收TCP连接请求,子进程负责处理当前请求任务。

#include <iostream>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <thread>
#include <string>
#include <mutex>
#include <sys/wait.h>
#include "tcp_server_process.h"


int ProcessServer::start(){
        int sockfd,connfd;
        struct sockaddr_in cliaddr;
        socklen_t clilen;
        char buff[MAXLINE];
        if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
                m_log->outputLog("socket create failed: error %s, errno:%d",strerror(errno),errno);
                return 0;
        }
        m_log->outputLog("socket create %d", sockfd);
        int ret = ::bind(sockfd, (struct sockaddr*) &addr, sizeof(addr));
        if(ret == -1){
                m_log->outputLog("bind socket error: %s, errno:%d",strerror(errno),errno);
                return 0;
        }
        if((listen(sockfd,10)) == -1){
                      return 0;
        }
        //socklen_t len = sizeof(cliaddr);

        while(true){
                socklen_t len = sizeof(cliaddr);
                if((connfd = accept(sockfd,(struct sockaddr*)&cliaddr,&len)) == -1){
                        m_log->outputLog("accept error: %s, errno:%d",strerror(errno),errno);
                        continue;
                }
                pid_t pid;
                if((pid = fork()) < 0){
                        m_log->outputLog("fork error:%s, error:%d", strerror(errno),errno);
                }else if(pid == 0){ //child
                        while(true){
                                //int n = recv(connfd,buff,MAXLINE,MSG_DONTWAIT);
                                int n = recv(connfd,buff,MAXLINE,0);
                                if(n>0){
                                        buff[n] = '\0';
                                        m_log->outputLog("recv msg:%s",buff);
                                }else{
                                        if(errno == EINTR && errno == EAGAIN){
                                                m_log->outputLog("recv error: %s, errno:%d",strerror(errno),errno);
                                                continue;
                                        }else{
                                                m_log->outputLog("recv error: %s, errno:%d",strerror(errno),errno);
                                                break;
                                        }
                                }
                        }
                        close(connfd);
                        exit(0);
                }else{ //father
                        continue;
                }
        }
        close(sockfd);
}

服务端测试程序

服务端程序会绑定一个SIGCHLD信号处理函数。当子进程结束时,父进程就会收到这个信号,并进行相应的处理。func_waipid函数主要是为了回收子进程,避免出现僵尸进程。wait一次只能回收一个进程,对于连接很多客户端的服务器,会有很多子进程,而这些子进程有可能同时关闭,成为僵尸进程,那么wait只处理它首先检测到的子僵尸进程然后退出。func_waipid函数会无阻塞的执行,遍历所有子进程,回收所有僵尸进程,对于未终止的进程则跳过。

#include "logger.h"
#include "tcp_server.h"
#include "tcp_server_thread.h"
#include "tcp_server_process.h"
#include "tcp_server_epoll.h"
#include "tcp_server_libevent.h"
#include "tcp_server_threadpoolevent.h"
#include <signal.h>
#include <sys/wait.h>


using namespace std;
logger *m_log = logger::get_instance();

void func_wait(int signo){
        pid_t pid;
        int stat;
        pid = wait(&stat);
        m_log->outputLog("child %d exit\n",pid);
        return;
}

void func_waitpid(int signo){
        pid_t pid;
        int stat;
        while( (pid = waitpid(-1,&stat,WNOHANG)) > 0 ){
                m_log->outputLog("child %d exit\n",pid);
        }
        return;
}

int main(){
        signal(SIGCHLD, &func_waitpid);
        //logger *m_log = logger::get_instance();
        m_log->openLogFile("./","test_server.log",true);

        //Server *m_server = new Server("111.206.73.111",12345);
        //Server *m_server = new ThreadServer("111.206.73.111",12345);
        Server *m_server = new ProcessServer("111.206.73.111",12345);
        //Server *m_server = new poolEventServer("111.206.73.111",12345);
        m_server->init(m_log);
        m_server->start();
}

 客户端测试程序,见上篇。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值