当前位置: 首页 > news >正文

linux网络编程3——http服务器的实现和性能测试

http服务器的实现

本文使用上一篇博文实现的epoll+reactor百万并发的服务器实现了一个使用http协议和WebSocket协议的WebServer。

完整代码请看我的github项目

1. 水平触发(Level Trigger)与边沿触发(Edge Trigger)

1.1 水平触发

水平触发是一种状态驱动机制。当文件描述符(如套接字)处于可读或可写状态时,内核会持续通知应用程序,直到应用程序处理完所有数据或资源。

优点

  • 容易编写,通常可以简单处理,因为内核会持续通知应用程序事件。

  • 不容易丢失事件通知。

缺点

  • 对于高并发场景,水平触发可能会造成不必要的系统调用。因为即使数据或资源已经读取过,内核还是会通知文件描述符仍然处于可读/可写状态。

使用场景

  • 典型的阻塞式 I/O 使用水平触发较为合适。
  • 适用于那些可以容忍一定的事件重复通知的应用程序。
1.2 边沿触发

边沿触发是一种状态变化驱动机制。只有当文件描述符的状态从不可读/不可写到可读/可写时,内核才会通知应用程序。ET 只在状态变化的那一刻通知,不会持续通知。

优点

  • 触发次数更少,减少了系统调用开销,适合高性能、高并发场景。

缺点

  • 容易出现遗漏事件的情况。应用程序需要一次性读取或写入尽可能多的数据,以确保没有遗漏。
  • 实现更为复杂。

使用场景

  • 非阻塞IO通常配合边沿触发使用,以避免阻塞和提高性能。
  • 边沿触发适用于高并发、追求性能的场景。
  • 如果数据包大小变化较大,适合使用边沿触发。

2. httpserver

2.1 调整内核tcp缓冲区大小

在这里插入图片描述

如果文件块太大,而用户层buffer太小或者内核tcp缓冲区太小,会导致需要多次发送,从而导致发送速度变慢。

可以尝试扩大TCP缓冲区,在/etc/sysctl.conf中设置

net.ipv4.tcp_wmem = 8192 8192 16384
net.ipv4.tcp_rmem = 8192 8192 16384
2.2 IO层和协议层

IO层包含负责管理IO事件的epoll和进行事件处理的reactor。

协议层就是实现http请求处理和发送http响应的函数。

2.3 使用状态机保存连接状态信息

可以在连接中保存一个status字段,表示当前连接的状态,当status为0,表示还没有发送任何信息,为1表示已经发送了头部,正在发送文件块,为2表示已经全部发送完毕。

显然我们需要在status为1时,将整个文件分块发送,因此就需要保存该文件描述符的上下文信息。

2.4 分块发送大文件,保存被发送文件的上下文信息

大文件传输中显然不能一次性把整个文件读出,然后写入用户缓冲区,再写入内核缓冲区。我们需要把文件分块,利用水平触发分多次写入,这样就绪要在connection中保存当前文件描述符,在status为0时打开文件,在status为2时关闭文件。

2.5 可选择使用sendfile函数减少内存复制

senfile函数可以在两个文件描述符之间直接传输数据,数据流不需要经过用户空间。它利用mmap指令直接将文件内容读取到系统缓冲区,因此性能更好。

缺点是,由于不经过用户空间,无法对文件分块发送,在阻塞IO模式下发送大文件可能长时间陷入阻塞。在非阻塞IO模式下,尽管不会陷入阻塞,但会可能导致其他连接饥饿。

2.6 性能测试qps

wrk是一款针对 Http 协议的基准测试工具,它能够在单机多核 CPU 的条件下,使用系统自带的高性能 I/O 机制,如 epoll,kqueue 等,通过多线程和事件模式,对目标机器产生大量的负载。

下载wrk。

这篇文章详细介绍了如何安装和使用wrk进行性能测试。

特点

  • 轻量级,简单易用
  • 只用于单机压测

测试结果:

  1. 对于每个http请求都返回一个738KB大小的图片,测试结果如下:
(base) fyli@a431:~/programs/sockets/course1 network_programs$ wrk -t12 -c400 -d30s http://localhost:2000
Running 30s test @ http://localhost:200012 threads and 400 connectionsThread Stats   Avg      Stdev     Max   +/- StdevLatency    17.69ms   14.40ms   1.68s    99.92%Req/Sec   143.45    153.46   600.00     83.31%25494 requests in 30.10s, 17.80GB readSocket errors: connect 0, read 25499, write 0, timeout 1
Requests/sec:    847.08
Transfer/sec:    605.61MB

可以看到qps是847.08

  1. 对于每个http请求都返回一个600+字节的html文件,测试结果如下:
(base) fyli@a431:~/programs/sockets/course1 network_programs$ wrk -c400 -t12 -d30 http://localhost:2000
Running 30s test @ http://localhost:200012 threads and 400 connectionsThread Stats   Avg      Stdev     Max   +/- StdevLatency     2.72ms   33.82ms   1.79s    99.38%Req/Sec     1.62k     1.02k    6.07k    72.66%461290 requests in 30.09s, 318.94MB readSocket errors: connect 0, read 461294, write 0, timeout 21
Requests/sec:  15327.85
Transfer/sec:     10.60MB

可以看到因为数据传输量变少,qps上升到了15327

2.7 代码实现

这里只展现了协议层和业务层的代码,IO层和事件回调的底层代码请看完整项目reactor.c。

webserver.h

#pragma once#include <stdio.h>#define BUFFER_LENGTH 819200
#define CONNECTION_LENGTH 256
#define READY_LENFTH 1024
#define PORT_NUM 2typedef int (*RCallBack)(int fd);struct Conn
{int fd;char rbuffer[BUFFER_LENGTH];char wbuffer[BUFFER_LENGTH];int rlength;int wlength;RCallBack send_callback;RCallBack recv_callback;int status;int file_fd;
};int http_request(struct Conn *);
int http_response(struct Conn *);int set_event(int fd, int event, int flag);void error_handling(const char *message);void log_error(const char *message);

webserver.c

#include <fcntl.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <sys/stat.h>
#include <sys/types.h>#include "webserver.h"int http_request(struct Conn *conn)
{set_event(conn->fd, EPOLLOUT, EPOLL_CTL_MOD);conn->status = 0;conn->wlength = 0;return 0;
}int http_response(struct Conn *conn)
{const char *file = "pic.png";int file_fd;if (conn->status == 0){file_fd = open(file, O_RDONLY);if (file_fd == -1){log_error("open() fails");return 1;}conn->file_fd = file_fd;}else{file_fd = conn->file_fd;}if (conn->status == 0){struct stat filestat = {0};fstat(file_fd, &filestat);int sended = snprintf(conn->wbuffer, BUFFER_LENGTH,"HTTP/1.1 200 OK\r\n""Content-Type: image/png\r\n""Accept-Ranges: bytes\r\n""Content-Length: %ld\r\n\r\n",filestat.st_size);conn->wlength = sended;conn->status = 1;}else if (conn->status == 1){ssize_t recved = read(file_fd, conn->wbuffer, BUFFER_LENGTH);if (recved == 0){close(file_fd);conn->status = 2;}if (recved < 0){close(file_fd);log_error("read() fails");conn->status = 2;}conn->wlength = recved;}return 0;
}

3. 可能出现的问题及解决

  1. connection reset

    recv()函数可能由于对端reset连接而返回-1,这是正常现象,关闭对应的fd即可。

  2. 服务器程序在客户端关闭后直接退出

    可能是由于服务器程序向已经被关闭的socket写数据时会接收到一个SIGPIPE,默认情况下没有设置该信号的处理函数的话,就会导致该进程直接被kill。

    • 可以设置忽略该信号。
    signal(SIGPIPE, SIG_IGN);
    
    • 也可以自定义信号处理函数
    struct sigaction sa;
    sa.sa_handler = handle_sigpipe;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;sigaction(SIGPIPE, &sa, NULL);  // 设置信号处理程序
    
    • 也可以在send函数参数中设置不发出信号
    send(fd, buffer, length, MSG_NOSIGNAL);
    

学习参考

学习更多请前往零声github。

相关文章:

linux网络编程3——http服务器的实现和性能测试

http服务器的实现 本文使用上一篇博文实现的epollreactor百万并发的服务器实现了一个使用http协议和WebSocket协议的WebServer。 完整代码请看我的github项目 1. 水平触发(Level Trigger)与边沿触发(Edge Trigger) 1.1 水平触发 水平触发是一种状态驱动机制。当文件描述符&a…...

Docker部署Kamailio,并使用LinPhone实现网络通话

前提条件 准备一个路由器&#xff0c;一个服务器&#xff0c;两个终端设备&#xff08;手机或电脑&#xff09; docker部署安装 我使用的是windows系统&#xff0c;docker desktop 先启动Docker desktop打开cmd&#xff0c;输入docker命令docker run --name kamailio --rm…...

JAVA-石头迷阵小游戏

采用企业式项目结构,接下来我将分享全部代码和结构,希望大家点点关注! 这是我的结构。首先使用IDE创建一个Module,命名stone-maze,接着把自带src下的main方法删除,接着在src下创建包,包名为com.wmuj,接着创建APP类代码如下: package com.wmuj;public class App {publ…...

鸿蒙--进度条通知

主要介绍如何使用通知能力和基础组件,实现模拟下载文件,发送通知的案例。 效果 代码结构 ├──entry/src/main/ets // 代码区 │ ├──common │ │ ├──constants │ │ │ └──CommonConstants.ets // 公共常量类 │ │ └──utils │ │ ├──Logger.ets //…...

搜维尔科技:varjo xr-4开箱测评,工业用途头显,一流视觉保真度

varjo xr-4开箱测评&#xff0c;工业用途头显&#xff0c;一流视觉保真度 搜维尔科技&#xff1a;varjo xr-4开箱测评&#xff0c;工业用途头显&#xff0c;一流视觉保真度...

mysql数据量分库分表

一、分库分表参考阈值 分库分表是解决大规模数据和高并发访问问题的常用策略。虽然没有绝对的阈值来决定何时进行分库分表&#xff0c;但以下是一些参考阈值和考虑因素&#xff0c;可以帮助你做出决策&#xff1a; 1.1 数据量阈值 单表数据行数&#xff1a;当单表的数据行数…...

Vite创建Vue3项目以及Vue3相关基础知识

1.创建Vue3项目 1.运行创建项目命令 # 使用 npm npm create vitelatest2、填写项目名称 3、选择前端框架 4、选择语法类型 5、按提示运行代码 不出意外的话&#xff0c;运行之后应该会出现 下边这个页面 6.延伸学习&#xff1a;对比webpack和vite&#xff08;这个是面试必考…...

Elasticsearch封装公共索引增删改查

什么是索引&#xff1f; 定义&#xff1a;索引是 Elasticsearch 中用于存储数据的逻辑命名空间。它由多个文档组成&#xff0c;每个文档是一个 JSON 格式的结构化数据对应关系&#xff1a;在关系数据库中&#xff0c;索引类似于表&#xff1b;而在 Elasticsearch 中&#xff0…...

Python异常检测:Isolation Forest与局部异常因子(LOF)详解

这里写目录标题 Python异常检测&#xff1a;Isolation Forest与局部异常因子&#xff08;LOF&#xff09;详解引言一、异常检测的基本原理1.1 什么是异常检测&#xff1f;1.2 异常检测的应用场景 二、Isolation Forest2.1 Isolation Forest的原理2.1.1 算法步骤 2.2 Python实现…...

Git的原理和使用(二)

1. git的版本回退 之前我们也提到过&#xff0c;Git 能够管理⽂件的历史版本&#xff0c;这也是版本控制器重要的能⼒。如果有⼀天你发现 之前前的⼯作做的出现了很⼤的问题&#xff0c;需要在某个特定的历史版本重新开始&#xff0c;这个时候&#xff0c;就需要版本 回退的功能…...

docker 发布镜像

如果要推广自己的软件&#xff0c;势必要自己制作 image 文件。 1 制作自己的 Docker 容器 基于 centos 镜像构建自己的 centos 镜像&#xff0c;可以在 centos 镜像基础上&#xff0c;安装相关的软件&#xff0c;之后进行构建新的镜像。 1.1 dockerfile 文件编写 首先&…...

投了15亿美元,芯片创新公司Ampere为何成了Oracle真爱?

【科技明说 &#xff5c; 科技热点关注】 一个数据库软件公司却想要操控一家芯片厂商&#xff0c;这样的想法不错。也真大胆。 目前&#xff0c;全球数据库巨头甲骨文Oracle已经持有Ampere Computing LLC 29%的股份&#xff0c;并有可能通过未来的投资选择权获得对这家芯片制造…...

vue 报告标题时间来自 elementUI的 el-date-picker 有开始时间和结束时间

要在Vue中使用 Element UI 的 el-date-picker 来选择开始时间和结束时间&#xff0c;并将其展示在报告中&#xff0c;以下是详细的实现步骤。 实现思路&#xff1a; 使用 Element UI 的 el-date-picker 组件&#xff0c;让用户选择时间范围&#xff08;开始时间和结束时间&am…...

简单几何问题的通解

来&#xff0c;这道题怎么做&#xff1f;边长为2的正方形内&#xff0c;2个扇形的交集面积是多少&#xff1f;这道题一定要画辅助线&#xff0c;因为要用到两个扇形的交点&#xff0c;如果不画辅助线&#xff0c;这个交点相关的4个子图一个都无法求出面积&#xff0c;只能求出子…...

DBeaver导出数据表结构和数据,导入到另一个环境数据库进行数据更新

在工作中&#xff0c;我们会进行不同环境之间数据库的数据更新&#xff0c;这里使用DBeaver导出新的数据表结构和数据&#xff0c;并执行脚本&#xff0c;覆盖另一个环境的数据库中对应数据表&#xff0c;完成数据表的更新。 一、导出 右键点击选中想要导出的数据表&#xff0…...

【Golang】合理运用泛型,简化开发流程

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…...

OpenCV单目相机内参标定C++

基于OpenCV 实现单目相机内参标定&#xff1a; a.使用OpenCV库实现内参标定过程。通过角点检测、亚像素角点定位、角点存储与三维坐标生成和摄像机标定分别获取左右相机的内参。 b.具体地&#xff0c;使用库函数检测两组图像&#xff08;左右相机拍摄图像&#xff09;中棋盘格…...

基于MATLAB(DCT DWT)

第三章 图像数字水印的方案 3.1 图像数字水印的技术方案 在数据库中存储在国际互联网上传输的水印图像一般会被压缩&#xff0c;有时达到很高的压缩比。因此&#xff0c;数字水印算法所面临的第一个考验就是压缩。JPEG和EZW&#xff08;Embedded Zero-Tree Wavelet&#xff0…...

渗透基础-rcube_webmail版本探测

简介 本文介绍了开源产品RoundCube webmail邮件系统的版本探测思路&#xff0c;并用go语言实现工具化、自动化探测。 正文 0x01 探测思路研究 探测系统版本&#xff0c;最理想的方法就是系统主页html代码中有特定的字符串&#xff0c;比如特定版本对应的hash在主页的html代…...

linux下编译鸿蒙版boost库

我在上一篇文章中介绍了curl和openssl的编译方式&#xff08;linux下编译鸿蒙版curl、openssl-CSDN博客&#xff09;&#xff0c;这篇再介绍一下boost库的编译。 未经许可&#xff0c;请勿转载&#xff01; 一.环境准备 1.鸿蒙NDK 下载安装方式可以参考上篇文章&#xff0c…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

五年级数学知识边界总结思考-下册

目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解&#xff1a;由来、作用与意义**一、知识点核心内容****二、知识点的由来&#xff1a;从生活实践到数学抽象****三、知识的作用&#xff1a;解决实际问题的工具****四、学习的意义&#xff1a;培养核心素养…...

反射获取方法和属性

Java反射获取方法 在Java中&#xff0c;反射&#xff08;Reflection&#xff09;是一种强大的机制&#xff0c;允许程序在运行时访问和操作类的内部属性和方法。通过反射&#xff0c;可以动态地创建对象、调用方法、改变属性值&#xff0c;这在很多Java框架中如Spring和Hiberna…...

大学生职业发展与就业创业指导教学评价

这里是引用 作为软工2203/2204班的学生&#xff0c;我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要&#xff0c;而您认真负责的教学态度&#xff0c;让课程的每一部分都充满了实用价值。 尤其让我…...

稳定币的深度剖析与展望

一、引言 在当今数字化浪潮席卷全球的时代&#xff0c;加密货币作为一种新兴的金融现象&#xff0c;正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而&#xff0c;加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下&#xff0c;稳定…...

【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error

在前端开发中&#xff0c;JavaScript 异常是不可避免的。随着现代前端应用越来越多地使用异步操作&#xff08;如 Promise、async/await 等&#xff09;&#xff0c;开发者常常会遇到 Uncaught (in promise) error 错误。这个错误是由于未正确处理 Promise 的拒绝&#xff08;r…...

在 Spring Boot 中使用 JSP

jsp&#xff1f; 好多年没用了。重新整一下 还费了点时间&#xff0c;记录一下。 项目结构&#xff1a; pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...

HybridVLA——让单一LLM同时具备扩散和自回归动作预测能力:训练时既扩散也回归,但推理时则扩散

前言 如上一篇文章《dexcap升级版之DexWild》中的前言部分所说&#xff0c;在叠衣服的过程中&#xff0c;我会带着团队对比各种模型、方法、策略&#xff0c;毕竟针对各个场景始终寻找更优的解决方案&#xff0c;是我个人和我司「七月在线」的职责之一 且个人认为&#xff0c…...

智能职业发展系统:AI驱动的职业规划平台技术解析

智能职业发展系统&#xff1a;AI驱动的职业规划平台技术解析 引言&#xff1a;数字时代的职业革命 在当今瞬息万变的就业市场中&#xff0c;传统的职业规划方法已无法满足个人和企业的需求。据统计&#xff0c;全球每年有超过2亿人面临职业转型困境&#xff0c;而企业也因此遭…...

CTF show 数学不及格

拿到题目先查一下壳&#xff0c;看一下信息 发现是一个ELF文件&#xff0c;64位的 ​ 用IDA Pro 64 打开这个文件 ​ 然后点击F5进行伪代码转换 可以看到有五个if判断&#xff0c;第一个argc ! 5这个判断并没有起太大作用&#xff0c;主要是下面四个if判断 ​ 根据题目…...