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();
}
客户端测试程序,见上篇。