一个HTTP连接的功能就是:读取请求+解析请求+生成响应+回送响应
解析HTTP请求报文(GET、POST)完成了读取请求+解析请求的两步;
生成HTTP响应报文 完成了生成响应的一步;
所以本文首先要实现回送响应的部分,然后将这四步有序组织起来。
首先是回送响应:
因为生成请求的部分将响应头置于缓冲区buffer内,将响应体即要回送的文件,通过mmap方式建立了内存映射,获得一个指向被映射区的指针char* mmFile。
通过writev方法,将两块内存中的数据集中写至socket中,发往客户端
给struct iovec vec[2] 赋值,vec[0]保存响应头的首地址和长度,vec[1]保存响应体的首地址和长度
response.makeResponse(writeBuff);//响应头vec[0].iov_base=const_cast(writeBuff.peek());vec[0].iov_len=writeBuff.readableBytes();iovCnt=1;//响应体if(response.getFileLen()>0&&response.getfile()){vec[1].iov_base=response.getfile();vec[1].iov_len=response.getFileLen();iovCnt=2;}
使用writev函数向socket内写入响应报文
ssize_t HttpConn::write(int* errno_)
{ssize_t len=-1;while (true){len=writev(fd,vec,iovCnt);if(len<0)//出错{*errno_=errno;break;}if(vec[0].iov_len+vec[1].iov_len==0)//全部写完 { break; }if(static_cast(len)>vec[0].iov_len){vec[1].iov_base=(uint8_t*)vec[1].iov_base+(len-vec[0].iov_len);vec[1].iov_len-=(len-vec[0].iov_len);if(vec[0].iov_len){writeBuff.retrieveAll();vec[0].iov_len=0;}}else{vec[0].iov_base=(uint8_t*)vec[0].iov_base+len;vec[0].iov_len-=len;writeBuff.retrieve(len);}}return len;
}
其次是组织读取请求+解析请求+生成响应+回送响应这四步:
通过ssize_t HttpConn::read(int* errno_) +bool HttpConn::process()函数+上面的ssize_t HttpConn::write(int* errno_)函数完成。
/*从客户端读取请求至readBuff内*/
ssize_t HttpConn::read(int* errno_)
{ssize_t len=-1;while(true){len=readBuff.readFd(fd,errno_);if(len<=0)break;}return len;
}
/*解析客户端请求,生成Http响应报文*/
bool HttpConn::process()
{request.init();//初始化Http请求if(readBuff.readableBytes()<=0){return false;}if(request.parse(readBuff))//解析Http请求成功{LOG_DEBUG("%s", request.getPath().c_str());//初始化Http响应,状态码为200response.init(srcDir,request.getPath(),request.isKeepAlive(),200);}else//解析Http请求失败{//初始化Http响应,状态码为400response.init(srcDir,request.getPath(),false,400);}response.makeResponse(writeBuff);//生成Http响应报文/*给struct iovec vec[2]赋值,vec[0]保存响应头的首地址和长度,vec[1]保存响应体的首地址和长度*///响应头vec[0].iov_base=const_cast(writeBuff.peek());vec[0].iov_len=writeBuff.readableBytes();iovCnt=1;//响应体if(response.getFileLen()>0&&response.getfile()){vec[1].iov_base=response.getfile();vec[1].iov_len=response.getFileLen();iovCnt=2;}return true;
}
HttpConn类结构 httpconn.h
#ifndef HTTPCONN_H
#define HTTPCONN_H#include
#include
#include #include "../log/log.h"
#include "../pool/sqlconnpool.h"
#include "../buffer/buffer.h"
#include "httprequest.h"
#include "httpresponse.h"class HttpConn
{
public:HttpConn();~HttpConn();void init(int connfd,const sockaddr_in& sockAddr);ssize_t read(int* errno_);//从客户端读取请求bool process();//解析请求+生成响应ssize_t write(int* errno_);//向客户端写响应void closeConn();//关闭Http连接const sockaddr_in& getSockAddr() const;int getFd() const;int getPort() const;const char* getIP() const;bool isKeepAlive() const;bool isWriteOver() const;static std::atomic userCount;//原子类型,保证多线程修改userCount时的正确性static const char* srcDir;
private:int fd;struct sockaddr_in addr;bool isClose;int iovCnt;struct iovec vec[2];Buffer readBuff;Buffer writeBuff;HttpRequest request;HttpResponse response;
};#endif //! HTTPCONN__H
HttpConn类实现 httpconn.cpp
#include "httpconn.h"std::atomic HttpConn::userCount;
const char* HttpConn::srcDir=nullptr;HttpConn::HttpConn()
{fd=-1;addr={0};isClose=true;Log::getInstance()->init();
}HttpConn::~HttpConn()
{closeConn();
}void HttpConn::closeConn()
{response.unmapFile();if(isClose==false){isClose=true;userCount--;close(fd);LOG_INFO("Client (%s:%d) quit, userCount:%d",getIP(),getPort(),static_cast(userCount));}
}void HttpConn::init(int connfd,const sockaddr_in& sockAddr)
{assert(connfd>0);userCount++;fd=connfd;addr=sockAddr;readBuff.retrieveAll();writeBuff.retrieveAll();isClose=false;LOG_INFO("Client (%s:%d) in, userCount:%d",getIP(),getPort(),static_cast(userCount));
}const sockaddr_in& HttpConn::getSockAddr() const
{return addr;
}
int HttpConn::getFd() const
{return fd;
}
int HttpConn::getPort() const
{return addr.sin_port;
}
const char* HttpConn::getIP() const
{return inet_ntoa(addr.sin_addr);
}
bool HttpConn::isKeepAlive() const
{return request.isKeepAlive();
}
bool HttpConn::isWriteOver() const
{return vec[0].iov_len+vec[1].iov_len==0;
}ssize_t HttpConn::read(int* errno_)
{ssize_t len=-1;while(true){len=readBuff.readFd(fd,errno_);if(len<=0)break;}return len;
}ssize_t HttpConn::write(int* errno_)
{ssize_t len=-1;while (true){len=writev(fd,vec,iovCnt);if(len<0){*errno_=errno;break;}if(vec[0].iov_len+vec[1].iov_len==0) { break; }if(static_cast(len)>vec[0].iov_len){vec[1].iov_base=(uint8_t*)vec[1].iov_base+(len-vec[0].iov_len);vec[1].iov_len-=(len-vec[0].iov_len);if(vec[0].iov_len){writeBuff.retrieveAll();vec[0].iov_len=0;}}else{vec[0].iov_base=(uint8_t*)vec[0].iov_base+len;vec[0].iov_len-=len;writeBuff.retrieve(len);}}return len;
}bool HttpConn::process()
{request.init();if(readBuff.readableBytes()<=0){return false;}if(request.parse(readBuff)){LOG_DEBUG("%s", request.getPath().c_str());response.init(srcDir,request.getPath(),request.isKeepAlive(),200);}else{response.init(srcDir,request.getPath(),false,400);}response.makeResponse(writeBuff);//响应头vec[0].iov_base=const_cast(writeBuff.peek());vec[0].iov_len=writeBuff.readableBytes();iovCnt=1;//响应体if(response.getFileLen()>0&&response.getfile()){vec[1].iov_base=response.getfile();vec[1].iov_len=response.getFileLen();iovCnt=2;}return true;
}