文章目录
httpserver
该示例可通过浏览器访问httpserver:192.168.1.100:2048(此地址为自己应用程序里绑定的地址端口),服务器返回一个静态页面的响应。实现简单的后端http服务器。
代码示例
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/poll.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
using namespace std;
#define BUFFER_LEN 1024
#define CLIENT_MAX_COUNT 2048
typedef int (*EPOLL_CALLBACK)(int fd);
struct CONNECT_ITEM {
char rBuffer[BUFFER_LEN] = {0};
int rLen = 0;
char wBuffer[BUFFER_LEN] = {0};
int wLen = 0;
union {
EPOLL_CALLBACK accept_callback = nullptr;
EPOLL_CALLBACK recv_callback;
}recv_t;
EPOLL_CALLBACK send_callback = nullptr;
}connect_item[CLIENT_MAX_COUNT];
typedef CONNECT_ITEM connection_t;
int epfd = 0;
enum _EPOLL_CTRL{
ADD,
MOD
};
int http_response(connection_t& conn) {
#if 1
conn.wLen = sprintf(conn.wBuffer,
"HTTP/1.1 200 OK\r\n"
"Accept-Ranges: bytes\r\n"
"Content-Length: 82\r\n"
"Content-Type: text/html\r\n"
"Date: Sat, 06 Aug 2023 13:16:46 GMT\r\n\r\n"
"<html><head><title>0voice.king</title></head><body><h1>King</h1></body></html>\r\n\r\n");
#else
//读取.html文件内容
int filefd = open("./test.html", O_RDONLY);
//获取文件内容的大小
struct stat statBuf;
fstat(filefd, &statBuf);
conn.wLen = sprintf(conn.wBuffer,
"HTTP/1.1 200 OK\r\n"
"Accept-Ranges: bytes\r\n"
"Content-Length: %ld\r\n"
"Content-Type: text/html\r\n"
"Date: Sat, 06 Aug 2023 13:16:46 GMT\r\n\r\n", statBuf.st_size);
int rSize = read(filefd, conn.wBuffer + conn.wLen, statBuf.st_size);
conn.wLen += rSize;
#endif
return conn.wLen;
}
void setEvent(int fd, EPOLL_EVENTS events, _EPOLL_CTRL ctrl) {
epoll_event ev;
ev.events = events; //默认水平触发(LT),有(数据)事件就会一直触发,知道全部处理完
/*
EPOLLET为边沿触发(ET),当有事件发生时只触发一次,
比如来数据了,如果一次没有读完,不会再触发了,所以必须全部读完,在进行下一次epoll_wait
*/
//ev.events = EPOLLIN | EPOLLET;
ev.data.fd = fd;
epoll_ctl(epfd, ctrl == ADD ? EPOLL_CTL_ADD : EPOLL_CTL_MOD, fd, &ev);
}
int recv_cb(int fd) {
char* buffer = connect_item[fd].rBuffer;
int index = connect_item[fd].rLen;
//int count = recv(fd, buffer + index, BUFFER_LEN - index, 0);
int count = recv(fd, buffer, BUFFER_LEN, 0);
if (count == 0) {
//printf("disconnect: %d %d \n", fd, index);
epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
close(fd);
return count;
}else if (count < 0) {
//printf("error\n");
return count;
}
//connect_item[fd].rLen += count;
connect_item[fd].rLen = count;
//printf("RECV===>>> clientfd: %d, count: %d, buffer: %s\n", fd, count, buffer);
//发送buffer赋值
#if 0
memcpy(connect_item[fd].wBuffer, connect_item[fd].rBuffer, connect_item[fd].rLen);
connect_item[fd].wLen = connect_item[fd].rLen;
#else
http_response(connect_item[fd]);
#endif
//改变该文件描述符的事件类型为EPOLLOUT
setEvent(fd, EPOLLOUT, MOD);
return count;
}
int send_cb(int fd) {
char* buffer = connect_item[fd].wBuffer;
int index = connect_item[fd].wLen;
int count = send(fd, buffer, connect_item[fd].wLen, 0);
//printf("SEND===>>> clientfd: %d, count: %d, buffer: %s\n", fd, count, buffer);
//改变该文件描述符的事件类型为EPOLLIN
setEvent(fd, EPOLLIN, MOD);
return count;
}
int accept_cb(int fd) {
struct sockaddr_in clientaddr;
socklen_t len = sizeof(clientaddr);
int clientfd = accept(fd, (struct sockaddr*)&clientaddr, &len);
setEvent(clientfd, EPOLLIN, ADD);
memset(connect_item[clientfd].rBuffer, 0, sizeof(connect_item[clientfd].rBuffer));
connect_item[clientfd].rLen = 0;
memset(connect_item[clientfd].wBuffer, 0, sizeof(connect_item[clientfd].wBuffer));
connect_item[clientfd].wLen = 0;
connect_item[clientfd].recv_t.recv_callback = recv_cb;
connect_item[clientfd].send_callback = send_cb;
//printf("ACCEPT===>>> clientfd:%d\n", clientfd);
return clientfd;
}
int main() {
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in serverAddr;
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddr.sin_port = htons(2048);
bind(listenfd, (sockaddr*)&serverAddr, sizeof(serverAddr));
listen(listenfd, 10);
epfd = epoll_create(1); // int size
connect_item[listenfd].recv_t.accept_callback = accept_cb;
setEvent(listenfd, EPOLLIN, ADD);
struct epoll_event events[1024] = {0};
while (1) {
int nready = epoll_wait(epfd, events, 1024, -1);
for (int i = 0; i < nready; i++) {
int connfd = events[i].data.fd;
if (events[i].events & EPOLLIN) {
int count = connect_item[connfd].recv_t.recv_callback(connfd);
}else if (events[i].events & EPOLLOUT) {
connect_item[connfd].send_callback(connfd);
}
}
}
close(listenfd);
return 0;
}
吞吐能力测试
通过wrk工具对该server进行吞吐能力(QPS)测试。
-
什么是QPS?
QPS即每秒查询率,是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准。 QPS = req/sec(请求数/秒),即每秒的响应请求数,也即是最大吞吐能力。
wrk工具是专门用来测试服务器的QPS。
wrk工具下载地址:git clone https://gitee.com/why168/wrk.git,下载下来后,直接在其目录下,执行make,编译完成后,执行./wrk 以及加上对应的参数就可以进行测试了。示例:
-
执行命令:
./wrk -c 100 -d10s -t 50 http://192.168.204.128:2048
-c 100:100个连接
-t 50:50个线程
-d10s:持续测量10s
http://192.168.204.128:2048:服务器地址 -
测试结果:
Running 10s test @ http://192.168.204.128:2048 50 threads and 100 connections Thread Stats Avg Stdev Max +/- Stdev Latency 4.77ms 30.11ms 838.79ms 99.09% Req/Sec 830.76 418.18 27.10k 95.60% 396632 requests in 10.10s, 77.54MB read Requests/sec: 39278.90 Transfer/sec: 7.68MB
Requests/sec: 39278.90:代表每秒可处理39278.90个http请求。