/**
httpserver
HTTPServer.cpp
Copyright 2011-2014 Ramsey Kant
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "HTTPServer.h"
/**
* 构造函数
* 初始化数据变量,ResourceHost和虚拟主机
*/
HTTPServer::HTTPServer() {
canRun = false;
listenSocket = INVALID_SOCKET;
// Create a resource host serving the base path ./htdocs on disk
ResourceHost* resHost = new ResourceHost("./htdocs");
hostList.push_back(resHost);
// Setup the resource host serving htdocs to provide for the following vhosts:
vhosts.insert(std::pair<std::string, ResourceHost*>("localhost:8080", resHost));
vhosts.insert(std::pair<std::string, ResourceHost*>("127.0.0.1:8080", resHost));
}
/**
* 析构函数
* 释放所有ResourceHost和虚拟主机
*/
HTTPServer::~HTTPServer() {
// Loop through hostList and delete all ResourceHosts
while(!hostList.empty()) {
ResourceHost* resHost = hostList.back();
delete resHost;
hostList.pop_back();
}
vhosts.clear();
}
/**
* 启动服务器
* 创建socket,绑定并监听
*
* @param port 监听的端口号
* @return 启动成功返回True
*/
bool HTTPServer::start(int port) {
canRun = false;
listenPort = port;
// 创建监听的socket
listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(listenSocket == INVALID_SOCKET) {
std::cout << "Could not create socket!" << std::endl;
return false;
}
// 设置socket为非阻塞
fcntl(listenSocket, F_SETFL, O_NONBLOCK);
// 设置服务器地址信息
// modify to support multiple address families (bottom): http://eradman.com/posts/kqueue-tcp.html
memset(&serverAddr, 0, sizeof(struct sockaddr_in)); // clear the struct
serverAddr.sin_family = AF_INET; // Family: IP protocol
serverAddr.sin_port = htons(listenPort); // Set the port (convert from host to netbyte order)
serverAddr.sin_addr.s_addr = INADDR_ANY; // Let OS intelligently select the server's host address
// 绑定到服务器地址及端口
if(bind(listenSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) != 0) {
std::cout << "Failed to bind to the address!" << std::endl;
return false;
}
// 监听状态等待客户端连接
if(listen(listenSocket, SOMAXCONN) != 0) {
std::cout << "Failed to put the socket in a listening state" << std::endl;
return false;
}
// 创建 kqueue 描述符
kqfd = kqueue();
if(kqfd == -1) {
std::cout << "Could not create the kernel event queue!" << std::endl;
return false;
}
// 将监听的socket添加到kqueue中
updateEvent(listenSocket, EVFILT_READ, EV_ADD, 0, 0, NULL);
canRun = true;
std::cout << "Server ready. Listening on port " << listenPort << "..." << std::endl;
return true;
}
/**
* 停止服务
* 断开所有客户端连接并清理所有启动阶段分配的资源
*/
void HTTPServer::stop() {
canRun = false;
if(listenSocket != INVALID_SOCKET) {
// 断开所有客户端连接
for(auto& x : clientMap)
disconnectClient(x.second, false);
// 清理客户端列表
clientMap.clear();
// 从kqueue中删除监听的socket
updateEvent(listenSocket, EVFILT_READ, EV_DELETE, 0, 0, NULL);
// 关闭监听的socket
shutdown(listenSocket, SHUT_RDWR);
close(listenSocket);
listenSocket = INVALID_SOCKET;
}
if(kqfd != -1) {
close(kqfd);
kqfd = -1;
}
std::cout << "Server shutdown!" << std::endl;
}
/**
* 更新 kqueue
* 创建合适的kevent对象来更新kqueue
* See kqueue documentation for parameter descriptions
*/
void HTTPServer::updateEvent(int ident, short filter, u_short flags, u_int fflags, int data, void *udata) {
struct kevent kev;
EV_SET(&kev, ident, filter, flags, fflags, data, udata);
kevent(kqfd, &kev, 1, NULL, 0, NULL);
}
/**
* 服务器主循环
* 主循环中检查是否有新的客户端连接及新的客户端数据需要进行读取和写入
*/
void HTTPServer::process() {
// kevent 返回的事件数量
int nev = 0;
Client* cl = NULL;
// 处理读和写的kevent对象
struct kevent read_kev, write_kev;
while(canRun) {
// 获取触发的事件列表
nev = kevent(kqfd, NULL, 0, evList, QUEUE_SIZE, &kqTimeout);
if(nev <= 0)
continue;
// 循环处理所有事件
for(int i = 0; i < nev; i++) {
// 新的客户端连接
if(evList[i].ident == (unsigned int)listenSocket) {
acceptConnection();
} else {
// 客户端连接中触发的读写事件
cl = getClient(evList[i].ident);
if(cl == NULL) {
std::cout << "Could not find client" << std::endl;
continue;
}
// 客户端要断开连接
if(evList[i].flags & EV_EOF) {
disconnectClient(cl);
continue;
}
// 初始化kevent对象
memset(&read_kev, 0, sizeof(struct kevent));
memset(&write_kev, 0, sizeof(struct kevent));
if(evList[i].filter == EVFILT_READ) {
// 读取并处理数据
readClient(cl, evList[i].data);
// 设置读取事件处理完成,触发写入事件
updateEvent(evList[i].ident, EVFILT_READ, EV_DISABLE, 0, 0, NULL);
updateEvent(evList[i].ident, EVFILT_WRITE, EV_ENABLE, 0, 0, NULL);
} else if(evList[i].filter == EVFILT_WRITE) {
// 写入数据到客户端
if(!writeClient(cl, evList[i].data)) {
// 如果队列中仍然有数据则需要继续出发读事件而关闭写事件
updateEvent(evList[i].ident, EVFILT_READ, EV_ENABLE, 0, 0, NULL);
updateEvent(evList[i].ident, EVFILT_WRITE, EV_DISABLE, 0, 0, NULL);
}
}
}
} // 事件处理循环
} // 服务器主循环
}
/**
* 接受客户端连接
* 有客户端连接时该函数被调用,创建新的客户端对象并添加到字典中
*/
void HTTPServer::acceptConnection() {
// 设置客户端地址信息和socket值
sockaddr_in clientAddr;
int clientAddrLen = sizeof(clientAddr);
int clfd = INVALID_SOCKET;
clfd = accept(listenSocket, (sockaddr*)&clientAddr, (socklen_t*)&clientAddrLen);
if(clfd == INVALID_SOCKET)
return;
fcntl(clfd, F_SETFL, O_NONBLOCK);
// 创建客户端对象
Client *cl = new Client(clfd, clientAddr);
// 添加客户端socket到kqueue中,读取和写入,注意写入初始状态是关闭
updateEvent(clfd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, NULL);
updateEvent(clfd, EVFILT_WRITE, EV_ADD | EV_DISABLE, 0, 0, NULL);
// 添加客户端对象到字典
clientMap.insert(std::pair<int, Client*>(clfd, cl));
std::cout << "[" << cl->getClientIP() << "] connected" << std::endl;
}
/**
* 返回客户端对象
* 根据socket查找并返回客户端对象
*
* @param clfd 客户端socket
* @return 如果找到则返回客户端对象指针否则返回NULL
*/
Client* HTTPServer::getClient(int clfd) {
std::unordered_map<int, Client*>::const_iterator it;
it = clientMap.find(clfd);
if(it == clientMap.end())
return NULL;
return it->second;
}
/**
* 断开连接
* 释放客户端socket及Client对象
*
* @param cl 客户端对象指针
* @param mapErase 为True时从map中删除客户端对象
*/
void HTTPServer::disconnectClient(Client *cl, bool mapErase) {
if(cl == NULL)
return;
std::cout << "[" << cl->getClientIP() << "] disconnected" << std::endl;
// 清理kqueue中的数据
updateEvent(cl->getSocket(), EVFILT_READ, EV_DELETE, 0, 0, NULL);
updateEvent(cl->getSocket(), EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
// 关闭socket
Linux环境下,c++开发web服务器
3星 · 超过75%的资源 需积分: 50 31 浏览量
2017-10-10
15:34:08
上传
评论 3
收藏 37KB ZIP 举报
yrc_Note
- 粉丝: 22
- 资源: 7
最新资源
- python-leetcode面试题解之第157题用Read4读取N个字符-题解.zip
- python-leetcode面试题解之第156题上下翻转二叉树-题解.zip
- python-leetcode面试题解之第155题最小栈-题解.zip
- python-leetcode面试题解之第153题寻找旋转排序数组中的最小值-题解.zip
- python-leetcode面试题解之第152题乘积最大子数组-题解.zip
- python-leetcode面试题解之第151题反转字符串中的单词-题解.zip
- python-leetcode面试题解之第150题逆波兰表达式求值-题解.zip
- python-leetcode面试题解之第149题直线上最多的点数-题解.zip
- python-leetcode面试题解之第148题排序链表-题解.zip
- python-leetcode面试题解之第147题对链表进行插入排序-题解.zip
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈