网络编程套接字(四)
一、实现tcp服务器多用户版本(多进程、多线程、线程池版本)
- tcp_process_server.hpp
#pragma once
#include "tcp_socket.hpp"
#include <functional>
#include <signal.h>
typedef std::function<void (const std::string& req,std::string* resp)> Handler;
class TcpProcessServer
{
public:
TcpProcessServer(const std::string& ip,uint16_t port)
:ip_(ip)
,port_(port)
{
//对于多进程版本服务器,在请求到来时生成子进程处理请求,父进程继续accept。如果父进程不等待
//子进程结束,子进程将成为僵尸进程(zombie)从而占用系统资源。如果父进程等待子进程结束
//将增加父进程的负担,影响服务器进程的并发性能。在Linux下可以简单地将 SIGCHLD信号的操作设为SIG_IGN。
//将SIGCHLD(子进程结束自动像父进程发送的信号)此信号设为忽略SIG_IGN
signal(SIGCHLD,SIG_IGN);
}
void ProcessConnect(TcpSocket& new_sock,const std::string& ip,uint16_t port,Handler handler)
{
int ret = fork();
if(ret > 0)
{
//father
//父进程不需要做额外操作,只需要循环accept即可,就直接返回
//此处如果采用wait操作,那么就达不到采用多进程的目的,采用多进程为了可以同时处理多个用户
//如果在此处wait,那么父进程就会等待子进程处理客户端请求结束,特别耗费时间
new_sock.Close();
return;
}
else if(ret == 0)
{
//child
//处理具体的链接过程即根据请求计算响应
while(1)
{
std::string req;
bool ret = new_sock.Recv(&req);
if(!ret)
{
printf("[client %s : %d] disconnect!\n",ip.c_str(),port);
exit(0);
}
std::string resp;
handler(req,&resp);
new_sock.Send(resp);
printf("[client %s : %d ] req : %s, resp : %s\n",ip.c_str(),port,req.c_str(),resp.c_str());
}
}
else
{
perror("fork is error");
}
}
bool Start(Handler handler)
{
//1. 创建 socket;
CHECK_RET(listen_sock_.Socket());
// 2. 绑定端口号
CHECK_RET(listen_sock_.Bind(ip_, port_));
// 3. 进行监听
CHECK_RET(listen_sock_.Listen(5));
// 4. 进入事件循环
for (;;)
{
// 5. 进行 accept
TcpSocket new_sock;
std::string ip;
uint16_t port = 0;
if (!listen_sock_.Accept(&new_sock, &ip, &port))
{
continue;
}
printf("[client %s:%d] connect!\n", ip.c_str(), port);
ProcessConnect(new_sock, ip, port, handler);
}
return true;
}
private:
TcpSocket listen_sock_;
std::string ip_;
uint16_t port_;
};
- tcp_pthread_server.hpp
#pragma once
#include <functional>
#include <pthread.h>
#include "tcp_socket.hpp"
typedef std::function<void (const std::string& req, std::string* resp)> Handler;
struct ThreadArg
{
TcpSocket new_sock;
std::string ip;
uint16_t port;
Handler handler;
};
class TcpThreadServer
{
public:
TcpThreadServer(const std::string& ip, uint16_t port)
: ip_(ip), port_(port)
{
}
bool Start(Handler handler)
{
// 1. 创建 socket;
CHECK_RET(listen_sock_.Socket());
// // 2. 绑定端口号
CHECK_RET(listen_sock_.Bind(ip_, port_));
// // 3. 进行监听
CHECK_RET(listen_sock_.Listen(5));
// 4. 进入循环
for (;;)
{
// 5. 进行 accept
ThreadArg* arg = new ThreadArg();
arg->handler = handler;
bool ret = listen_sock_.Accept(&arg->new_sock, &arg->ip, &arg->port);
if (!ret)
{
continue;
}
printf("[client %s:%d] connect\n", arg->ip.c_str(), arg->port);
// 6. 创建新的线程完成具体操作
pthread_t tid;
pthread_create(&tid, NULL, ThreadEntry, arg);
pthread_detach(tid);
}
return true;
}
static void* ThreadEntry(void* arg)
{
ThreadArg* p = reinterpret_cast<ThreadArg*>(arg);
PthreadConnect(p);
// 一定要记得释放内存!!! 也要记得关闭文件描述符
p->new_sock.Close();
delete p;
return NULL;
}
static void PthreadConnect(ThreadArg* arg)
{
// 1. 循环进行读写
for (;;)
{
std::string req;
// 2. 读取请求
bool ret = arg->new_sock.Recv(&req);
if (!ret)
{
printf("[client %s:%d] disconnected!\n", arg->ip.c_str(), arg->port);
break;
}
std::string resp;
// 3. 根据请求计算响应
arg->handler(req, &resp);
// 4. 发送响应
arg->new_sock.Send(resp);
printf("[client %s:%d] req: %s, resp: %s\n", arg->ip.c_str(),
arg->port, req.c_str(), resp.c_str());
}
}
private:
TcpSocket listen_sock_;
std::string ip_;
uint16_t port_;
};
- tcp_threadpool_server.hpp
#pragma once
#include"tcp_socket.hpp"
#include<pthread.h>
#include"threadpool.hpp"
#include<sys/syscall.h>
#include<unistd.h>
#include<stdio.h>
#include <functional>
typedef std::function<void (const std::string& req,std::string* resp)> Handler;
struct Arg
{
TcpSocket sock_;
std::string _ip;
uint16_t _port;
Handler _handler;
};
class MyTask:public Task
{
public:
MyTask(void* arg)
:arg_(arg)
{}
virtual void Run()override
{
Arg *arg = (Arg*)arg_;
TcpSocket newsock_ = arg->sock_;
std::string ArgIp = arg->_ip;
int ArgPort = arg->_port;
Handler ArgHandler = arg->_handler;
while(1)
{
std::string msg;
int n = newsock_.Recv(&msg);
if(n < 0)
{
continue;
}
else if(n == 0)
{
printf("[%s:%d]客户端已经关闭!\n",ArgIp.c_str(),ArgPort);
break;
}
printf("[%s:%d]客户端输入:%s\n",ArgIp.c_str(),ArgPort,msg.c_str());
std::string resp;
ArgHandler(msg,&resp);
if(newsock_.Send(resp) == false)
continue;
}
newsock_.Close();
delete arg;
}
private:
void* arg_;
};
class TcpThreadPoolServer
{
public:
TcpThreadPoolServer(const std::string ip,uint16_t port)
:pool(10)
,ip_(ip)
,port_(port)
{}
~TcpThreadPoolServer()
{
sock_.Close();
}
bool Start(Handler handler)
{
if(sock_.Socket() == false)
return false;
cout<<__LINE__<<endl;
if(sock_.Bind(ip_,port_) == false)
return false;
cout<<__LINE__<<endl;
if(sock_.Listen() == false)
return false;
while(1)
{
Arg *NewArg = new Arg;
NewArg->_handler = handler;
if(sock_.Accept(&(NewArg->sock_),&NewArg->_ip,&NewArg->_port) == false)
continue;
printf("[%s:%d]客户端已连接!\n",NewArg->_ip.c_str(),NewArg->_port);
pool.AddTask(new MyTask((void*)NewArg));
}
}
private:
TcpSocket sock_;
ThreadPool pool;
std::string ip_;
uint16_t port_;
};