目录
1、locker.h
#ifndef LOCKER_H
#define LOCKER_H
/********************* 封装线程同步的类和头文件 ********************/
#include <pthread.h>
#include <exception>
#include <semaphore.h>
using namespace std;
// 互斥锁类
class locker{
public:
locker(){
if( pthread_mutex_init(&m_mutex, NULL) != 0 )
throw exception(); // 抛出异常
}
~locker(){
pthread_mutex_destroy(&m_mutex);
}
bool lock(){
return pthread_mutex_lock(&m_mutex) == 0;
}
bool trylock(){
return pthread_mutex_trylock(&m_mutex) == 0;
}
bool unlock(){
return pthread_mutex_unlock(&m_mutex) == 0;
}
private:
pthread_mutex_t m_mutex;
};
// 条件变量类
class cond{
public:
cond(){
if( pthread_cond_init(&m_cond, NULL) != 0 )
throw exception();
}
~cond(){
pthread_cond_destroy(&m_cond);
}
bool wait(pthread_mutex_t *m_mutex){
return pthread_cond_wait(&m_cond, m_mutex) == 0;
}
bool timedwait(pthread_mutex_t* m_mutex, struct timespec t){
return pthread_cond_timedwait(&m_cond, m_mutex, &t) == 0;
}
bool signal(){
return pthread_cond_signal(&m_cond) == 0;
}
bool broadcast(){
return pthread_cond_broadcast(&m_cond) == 0;
}
private:
pthread_cond_t m_cond;
};
// 信号量类
class sem{
public:
sem(){
if( sem_init(&m_sem, 0, 0) != 0)
throw exception();
}
~sem(){
sem_destroy(&m_sem);
}
bool wait(){
return sem_wait(&m_sem) == 0;
}
bool trywait(){
return sem_trywait(&m_sem) == 0;
}
bool post(){
return sem_post(&m_sem) == 0;
}
private:
sem_t m_sem;
};
#endif
2、pthreadpool.h
#ifndef PTHREADPOOL_H
#define PTHREADPOOL_H
/******************** 封装线程池类头文件 *******************/
#include "locker.h"
#include <list>
#include <iostream>
using namespace std;
template <typename T>
class pthreadpool{
public:
pthreadpool(int workqueue_number = 10000, int pthread_number = 8);
~pthreadpool();
// 向工作队列中添加任务
bool append(T* request);
static void* worker(void* arg);
private:
// 线程池运行函数
void run();
private:
// 工作队列最大容量
int m_workqueue_number;
// 工作队列
list<T*> m_workqueue;
// 工作线程最大数量
int m_pthread_number;
// 线程池数组
pthread_t * m_pthreads;
// 信号量用来判断是否有任务需要处理
sem workqueuetat;
// 工作队列互斥锁
locker workqueuelocker;
// 是否停止线程
bool m_stop;
};
template <typename T>
pthreadpool<T>::pthreadpool(int workqueue_number, int pthread_number) : // 初始化
m_workqueue_number(workqueue_number), m_pthread_number(pthread_number),
m_stop(false), m_pthreads(NULL){
// 创建线程池数组
m_pthreads = new pthread_t[m_pthread_number];
// 创建工作线程,并设置线程分离
for(int i = 0; i < m_pthread_number; i++){
if( pthread_create(m_pthreads + i, NULL, worker, this) == 0 ){
cout << "creat " << i <<"th pthread" << endl;
// printf("创建第%d个线程\n",i);
pthread_detach(m_pthreads[i]);
}
}
}
template <typename T>
pthreadpool<T>::~pthreadpool(){
delete [] m_pthreads;
m_stop = true;
}
template <typename T>
void* pthreadpool<T>::worker(void* arg){
pthreadpool* poll = (pthreadpool*) arg;
poll->run();
return poll;
}
template <typename T>
void pthreadpool<T>::run(){
while( !m_stop ){
workqueuetat.wait();
workqueuelocker.lock();
if( !m_workqueue.empty() ){
// 工作队列中有任务
T* request = m_workqueue.front();
m_workqueue.pop_front();
workqueuelocker.unlock();
// 处理任务函数
request->process();
}
}
}
template <typename T>
bool pthreadpool<T>::append(T* request){
// 工作队列会被所有线程共享,所以要加锁
workqueuelocker.lock();
if( m_workqueue.size() >= m_workqueue_number ){
// 工作队列已满
workqueuelocker.unlock();
printf("工作队列已满!");
return false;
}
// 将任务插入到工作队列中,并且信号量+1
m_workqueue.push_back(request);
workqueuelocker.unlock();
workqueuetat.post();
return true;
}
#endif
3、“Web 服务器” 笔记02
3.1 main.cpp
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <string.h>
#include "pthreadpool.h"
#include "http_conn.h"
#include <signal.h>
#define MAX_FD 65535 //最大的文件描述符个数
#define MAX_EVENT_NUMBER 10000 // 监听的最大的事件数量
int main( int argc, char* argv[] ){
// 获取端口号
int port = atoi(argv[1]);
// 对SIGPIPE信号进行处理
struct sigaction m_sa;
m_sa.sa_flags = 0;
m_sa.sa_handler = SIG_IGN;
sigfillset( &m_sa.sa_mask ); // 将临时阻塞信号集中的所有的标志位置为1
sigaction(SIGPIPE, &m_sa, NULL);
// 创建线程池
pthreadpool<http_conn>* pool = new pthreadpool<http_conn>;
// pthreadpool<http_conn>* pool = NULL;
// 创建一个数组,用于保存所有客户端信息
http_conn * users = new http_conn[MAX_FD];
// 创建监听的套接字
//int listenfd = socket(PF_INET, SOCK_STREAM, 0);
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
// 端口复用
int optval = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
// 绑定
struct sockaddr_in sa;
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
sa.sin_addr.s_addr = INADDR_ANY;
bind(listenfd, (struct sockaddr *)&sa, sizeof(sa));
// 监听
listen(listenfd, 5);
// 创建epoll实例,并将监听的文件描述符放入epoll实例中
int epollfd = epoll_create(5); // 所有的连接文件描述符共享一个 epollfd
http_conn::m_epollfd = epollfd;
struct epoll_event epev;
epev.events = EPOLLIN | EPOLLRDHUP;
epev.data.fd = listenfd;
epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &epev);
// 创建epoll_event数组,将发生了变化的文件描述符放入其中
struct epoll_event epevs[MAX_EVENT_NUMBER];
while(1){
int number = epoll_wait(epollfd, (epoll_event *)&epevs, MAX_FD, -1);
// 循环遍历epoll_event数组
for(int i=0; i<number; i++){
int sockfd = epevs[i].data.fd;
if(sockfd == listenfd){
// 有客户连接进来
struct sockaddr_in client_address;
int client_address_len = sizeof(client_address);
int connfd = accept(listenfd, (struct sockaddr*)&client_address, (socklen_t *)&client_address_len);
if(http_conn::m_user_count > MAX_FD){
// 用户连接已满
close(connfd);
continue;
}
// 将新客户的信息初始化,放到users数组中
users[connfd].init(connfd);
}else if(epevs[i].events & (EPOLLERR | EPOLLHUP | EPOLLRDHUP)){
// 客户端异常断开或者错误等事件发生,关闭连接
users[sockfd].close_conn();
}else if(epevs[i].events & EPOLLIN){
// 读数据
printf("读取客户端%d\n",sockfd);
if(users[sockfd].read()){
// 一次性把所有数据都读完,将任务添加到工作队列中
pool->append(users + sockfd);
}else{
users[sockfd].close_conn();
}
}else if(epevs[i].events & EPOLLOUT){
// 写数据
// 一次性把数据都写完,关闭文件描述符
if(users[sockfd].write()){
users[sockfd].close_conn();
}
}
}
}
close(epollfd);
close(listenfd);
delete [] users;
delete pool;
return 0;
}
3.2 http_conn.h
#ifndef HTTP_CONN_H
#define HTTP_CONN_H
#include <stdio.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
class http_conn{
public:
static int m_user_count; // 用户数量
static int m_epollfd; // epoll实例的文件描述符
http_conn(){}
~http_conn(){}
void process(); // 处理客户端请求
void init(int sockfd); // 初始化新接收的客户端
bool read(); // 一次性把数据都读完
bool write(); // 一次性把数据都写完
int m_sockfd; // 连接的文件描述符
void close_conn(); // 关闭连接
private:
};
#endif
3.3 http_conn.cpp
#include "http_conn.h"
// 初始化
int http_conn::m_user_count = 0;
int http_conn::m_epollfd = -1;
// 关闭连接
void http_conn::close_conn(){
// 即从epoll实例中删除该连接的文件描述符
epoll_ctl(m_epollfd, EPOLL_CTL_DEL, m_sockfd, 0);
close(m_sockfd);
m_sockfd = -1;
http_conn::m_user_count -=1;
}
// 初始化新接收的客户端
void http_conn::init(int sockfd){
printf("有新的客户端%d连接进来了\n",sockfd);
http_conn::m_sockfd = sockfd;
// 端口复用
int optval = 1;
setsockopt(m_sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
// 将连接的客户端的文件描述符放到epoll实例中
struct epoll_event epev;
epev.events = EPOLLIN | EPOLLRDHUP | EPOLLONESHOT;
// epev.events = EPOLLIN | EPOLLRDHUP;
epev.data.fd = m_sockfd;
epoll_ctl(m_epollfd, EPOLL_CTL_ADD, m_sockfd, &epev);
m_user_count += 1; // 总用户数+1
}
// 一次性把数据都读完
bool http_conn::read(){
printf("一次性读完数据\n");
return true;
}
// 一次性把数据都写完
bool http_conn::write(){
printf("一次性写完数据\n");
return true;
}
// 工作线程去处理HTTP请求的的入口代码
void http_conn::process(){
// 解析HTTP请求
printf("解析HTTP请求,并生成响应\n");
// 响应HTTP请求
//printf("响应HTTP请求");
}
4、“Web 服务器” 笔记03
4.1 main.cpp
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/