多线程并发的逻辑和多进程类似,这里只不过是将线程代替了进程,多线程的好处是共享进程的内存空间,节省资源。这里主线程处理连接请求,通过新建线程来处理连接建立后的数据交互和处理。
tcp_server_thread.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 ThreadServer : public Server{
public:
ThreadServer(string ip, int port):Server(ip, port){
}
static void *threadWorker(void *args);
int start();
};
tcp_server_thread.cpp
#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 <pthread.h>
#include <string>
#include <mutex>
#include <sys/wait.h>
#include "tcp_server_thread.h"
void *ThreadServer::threadWorker(void * args){
char buff[MAXLINE];
int connfd = long(args);
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("thread:%lu,connfd:%d,recv msg:%s",pthread_self(),connfd,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);
return NULL;
}
int ThreadServer::start(){
int sockfd,connfd;
struct sockaddr_in cliaddr;
socklen_t clilen;
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){
m_log->outputLog("listen error: %s, errno:%d",strerror(errno),errno);
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;
}
pthread_t threadworker;
if(pthread_create(&threadworker,NULL,threadWorker,(void*)connfd) != 0){
m_log->outputLog("fork error:%s, error:%d", strerror(errno),errno);
}
}
close(sockfd);
}
test_server.cpp
#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>
int main(){
//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 EpollServer("111.206.73.111",12345);
//Server *m_server = new poolEventServer("111.206.73.111",12345);
m_server->init(m_log);
m_server->start();
}
这里需要说明的一点是,在C++的中,普通成员函数不能直接作为pthread_create的线程函数,只能声明为静态函数,而类中的静态函数是无法知己访问类成员的,因为静态函数不会传递类this指针作为参数。因此,这里的ThreadServer::threadWorker声明为类静态成员函数。C++11在这方面做了改进,引进了Thread类,通过Thread类绑定任务函数,这里的函数可以是普通成员函数,有兴趣的可以进一步探究。