“Web 服务器” 笔记00 ------ 代码

这篇博客详细记录了构建Web服务器的过程,从locker.h和pthreadpool.h的使用开始,逐步深入到http_conn.h和http_conn.cpp的实现,包括笔记02至04中的main.cpp文件改动。博主通过一步步的代码讲解,展示了如何搭建和优化服务器,最后提到了包含1个image图片和1个index.html文件的Resource部分。
摘要由CSDN通过智能技术生成

目录

1、locker.h

2、pthreadpool.h

3、“Web 服务器” 笔记02

3.1  main.cpp

3.2  http_conn.h

3.3  http_conn.cpp

4、“Web 服务器” 笔记03

4.1 main.cpp 

4.2 http_conn.h

4.3 http_conn.cpp

5、“Web 服务器” 笔记04

5.1 main.cpp

5.2 http_conn.h

5.3 http_conn.cpp

5.4 Resource


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/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值