Linux网络编程 多线程Web服务器:HTTP协议与TCP并发实战
问题解答
TCP是如何防止SYN洪流攻击的?
方式有很多种,我仅举例部分:
1、调整内核参数
我们知道SYN洪流攻击的原理就是发送一系列无法完成三次握手的特殊信号,导致正常的能够完成三次握手的信号因为 连接队列空间不足,无法正常传输。
(1)增加 未连接队列 的容量
(2)缩短 未连接信号 的超时时间
(3)减少SYN-ACK重传次数
这里主要介绍一下(3):在三次握手过程中,当服务器收到客户端的SYN请求时,会回应SYN-ACK请求。当客户端不回应,则会重传。因此我们这里可以减少SYN-ACK重传次数,剪短一个异常信号占用内存空间的时间,SYN-ACK重传次数不易太少,太少会降低TCP传输数据的可靠度。((2)(3)原理都是差不多的,目的都是减少异常信号占用内存资源的时间)
具体操作需要用到bash操作,大家自行AI查询即可。
2、防火墙
防火墙会限制单个IP的并发SYN请求速率。
知识点1【Web服务器的概述】
web服务器使用的传送协议是:HTTP(超文本传送协议),基于TCP
HTML语言,超文本编辑语言,用于显示,可以脱离系统
HTTP协议 超文本传送协议 用于数据的传输
URL同一地址定位符 网址
访问的时候不能阻塞别人访问这个服务器,因此需要 实现的是并发服务器
TCP并发服务器又分为 线程版和进程版,我们这里选择线程版。
客户端是浏览器 我们需要实现的是服务器
因为TCP是传输层协议,HTTP是应用层,我们类似于在UDP中,TFTP协议理解,我们在TCP并发服务器的基础上,在核心代码(子进程的任务体代码(对应TCP并发服务器文章))中,以HTTP的专属报文格式进行解包组包即可。
web服务器的好处,无论是安卓,还是windows,Linux,都可以访问
特点
1、支持C/S架构
2、简单快速,客户端服务器申请服务时,只需要传送方法和路径,方法有GET和POST
3、无连接:限制每次连接只处理一个请求,且每一个请求是独立的 浏览器连接服务器只会有一个请求,即每次请求只要一个文件,不会要多个
文本算一个客户端,图片算一个客户端,图片文本分开存储,浏览器想要图片,需要向服务器发出请求,申请图片,即打开一个网页时,可能有几十个,甚至几百个客户端连接服务器。每个客户端一个请求
浏览器只需要传输请求的方式(GET/POST),GET是明文,POST是密文。
举例说明,当我们使用GET的方式,会把输入的内容放在红框部分,做成URL,发给服务器,如果是密码,那只要别人一抓包,密码就泄露了。
POST是密文传输,不过多介绍了。

4、无状态:如果后续处理需要前面的信息,必须重传,这样
![]()
HTTP传送文件没有固定大小限制
固定大小和大小限制的区别:
固定大小限制:不会限制为固定的字节大小发送
固定大小限制:最大发送多少字节
补充
HTML 超文本标记语言:显示文本
HTTP 超文本传输协议:传输协议
URL 同一地址定位符
浏览器的请求方式:

IP:端口号/请求的文件
浏览器会发出,向IP的端口,申请文件的请求(由浏览器完成)
解析文件名,打开本地文件(成功/失败)
以下都是报文格式,请仔细查看

注意
着重关注文件名,GET后有一个空格,文件名后有一个空格
服务器应答的格式:请求失败

服务器应答的格式:请求成功

现在我们通过代码实现一下
知识点2【多进程并发服务器代码复习】
这里我们先复习一下 多线程并发服务器的创建
1、创建套接字
2、绑定端口
3、监听(将套接字改为监听套接字,产生连接列表)
4、循环
提取已连接套接字
创建子进程
(1)子进程中
关闭监听套接字
任务体
关闭已连接套接字
关闭子进程
(2)父进程
关闭已连接套接字
回收子进程空间
5、关闭监听套接字
下面先实现多线程并发服务器
代码演示
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <strings.h> //bzero
#include <unistd.h>
#include <signal.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <stdlib.h> //atoi//释放子进程空间函数
void release_space(int signal)
{while(1){int ret_wait = waitpid(-1,NULL,WNOHANG);if(ret_wait == 0 || ret_wait < 0){break;}}
}
//子进程任务体(核心函数)
void fun_subprocess(int fd)
{while(1){char buf[256] = "";int len = recv(fd,buf,sizeof(buf),0);if(len < 0){perror("recv");_exit(-1);}send(fd,buf,sizeof(buf),0);if(len == 0){break;}}
}int main(int argc, char const *argv[])
{//参数个数判断if(argc != 2){printf("demo:./a.out 8000\\n");_exit(-1);}//创建套接字int fd_sock = socket(AF_INET,SOCK_STREAM,0);if(fd_sock < 0){perror("socket");_exit(-1);}//实现端口复用int opt;//这个opt,作用是二次确认setsockopt(fd_sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));//绑定套接字struct sockaddr_in addr_bind;bzero(&addr_bind,sizeof(addr_bind));addr_bind.sin_family = AF_INET;addr_bind.sin_port = htons(atoi(argv[1]));addr_bind.sin_addr.s_addr = htonl(INADDR_ANY);int ret_bind = bind(fd_sock,(struct sockaddr *)&addr_bind,sizeof(addr_bind));if(ret_bind < 0){perror("bind");_exit(-1);}//监听套接字int ret_listen = listen(fd_sock,10);if(ret_listen < 0){perror("listen");_exit(-1);}//循环while (1){//提取已连接套接字struct sockaddr_in addr_accept;bzero(&addr_accept,sizeof(addr_accept));int len_accept = sizeof(addr_accept);int fd_accept = accept(fd_sock,(struct sockaddr *)&addr_accept,&len_accept);if(fd_accept < 0){perror("accept");_exit(-1);}//创建子进程int pid = fork();if(pid < 0){perror("fork");_exit(-1);}//子进程if(pid == 0){//遍历一下 已连接客户端的IP,端口号,以及为其分配的进程号char buf_IP[16] = "";inet_ntop(AF_INET,&addr_accept.sin_port,buf_IP,sizeof(buf_IP));int int_port = ntohs(addr_accept.sin_port);printf("[%s:%d] pid:%d\\n",buf_IP,int_port,getpid());//关闭监听套接字close(fd_sock);//任务体fun_subprocess(fd_accept);//关闭接受套接字close(fd_accept);//关闭子进程_exit(0);}//父进程else{//关闭接受套接字close(fd_accept);//释放子进程signal(SIGCHLD,release_space);}}//关闭监听套接字close(fd_sock);return 0;
}
代码运行结果

非常流畅,大家可以自己动手写一下
知识点3【代码演示】
知识点补充
下面我们来写一下Web服务器的代码
首先我们先了解一下web服务器的收到的数据格式,这里我们只介绍明文方式
GET /文件名
前面我们着重标记了空格的位置,希望大家在这里能够理解,这是我们下面解包的重要方式
在打开文件时,我们需要对打开文件(.html)是否成功进行判断,下面分别是 HTTP协议,服务器应答成功与失败的报文格式
//打开失败,服务器应答
char err[] = "HTTP/1.1 404 Not Found\\r\\n"
"Content‐Type: text/html\\r\\n"
"\\r\\n"
"<HTML><BODY>File not found</BODY></HTML>";
//打开成功,服务器应答
char head[] = "HTTP/1.1 200 OK\\r\\n"
"Content‐Type: text/html\\r\\n"
"\\r\\n";
一定要严格遵守这个报文格式,哪怕是多一个 \0都不可以,因此发送长度的时候,要用strlen,这里大家用的时候直接复制粘贴即可,手敲反而容易出错。
在读取内容的时候,大家用循环读取,按照recv()函数的返回值作为判断条件即可,我在这里建议大家使用do{}while()类型,这样代码会更加简洁。
最后,请大家不要忘记相关文件描述符的关闭问题。
下面我们 来实践一下
代码演示
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <strings.h> //bzero
#include <unistd.h>
#include <signal.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <stdlib.h> //atoi
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>char err[] = "HTTP/1.1 404 Not Found\\r\\n""Content‐Type: text/html\\r\\n""\\r\\n""<HTML><BODY>File not found</BODY></HTML>";
char head[] = "HTTP/1.1 200 OK\\r\\n""Content‐Type: text/html\\r\\n""\\r\\n";
// 函数声明
// 释放子进程空间函数
void release_space(int signal);// 子进程任务体(核心函数)
void fun_subprocess(int arg);int main(int argc, char const *argv[])
{// 参数个数判断if (argc != 2){printf("demo:./a.out 8000\\n");_exit(-1);}// 创建套接字int fd_sock = socket(AF_INET, SOCK_STREAM, 0);if (fd_sock < 0){perror("socket");_exit(-1);}// 实现端口复用int opt; // 这个opt,作用是二次确认setsockopt(fd_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));// 绑定套接字struct sockaddr_in addr_bind;bzero(&addr_bind, sizeof(addr_bind));addr_bind.sin_family = AF_INET;addr_bind.sin_port = htons(atoi(argv[1]));addr_bind.sin_addr.s_addr = htonl(INADDR_ANY);int ret_bind = bind(fd_sock, (struct sockaddr *)&addr_bind, sizeof(addr_bind));if (ret_bind < 0){perror("bind");_exit(-1);}// 监听套接字int ret_listen = listen(fd_sock, 10);if (ret_listen < 0){perror("listen");_exit(-1);}// 循环while (1){// 提取已连接套接字struct sockaddr_in addr_accept;bzero(&addr_accept, sizeof(addr_accept));int len_accept = sizeof(addr_accept);int fd_accept = accept(fd_sock, (struct sockaddr *)&addr_accept, &len_accept);if (fd_accept < 0){perror("accept");_exit(-1);}// 创建子进程int pid = fork();if (pid < 0){perror("fork");_exit(-1);}// 子进程if (pid == 0){// 遍历一下 已连接客户端的IP,端口号,以及为其分配的进程号char buf_IP[16] = "";inet_ntop(AF_INET, &addr_accept.sin_port, buf_IP, sizeof(buf_IP));int int_port = ntohs(addr_accept.sin_port);printf("[%s:%d] pid:%d\\n", buf_IP, int_port, getpid());// 关闭监听套接字close(fd_sock);// 任务体fun_subprocess(fd_accept);// 关闭接受套接字close(fd_accept);// 关闭子进程_exit(0);}// 父进程else{// 关闭接受套接字close(fd_accept);// 释放子进程signal(SIGCHLD, release_space);}}// 关闭监听套接字close(fd_sock);return 0;
}// 释放子进程空间函数
void release_space(int signal)
{while (1){int ret_wait = waitpid(-1, NULL, WNOHANG);if (ret_wait == 0 || ret_wait < 0){break;}else{printf("进程%d已退出\\n", ret_wait);}}
}// 客户端核心任务线程函数
void fun_subprocess(int arg)
{unsigned char cmd_buf[1024] = "";int len = recv(arg, cmd_buf, sizeof(cmd_buf), 0);if (len <= 0){close(arg);_exit(-1);}char file_name[128] = "./html/";sscanf(cmd_buf, "GET /%[^ ]", file_name + 7);if (file_name[7] == '\\0'){strcat(file_name, "index.html");}printf("file_name=##%s##\\n", file_name);// open打开本地文件int fd = open(file_name, O_RDONLY);if (fd < 0){// 告诉浏览器404send(arg, err, strlen(err), 0);close(arg);perror("open");_exit(-1);}// 告诉浏览器 200 打开成功准备接受send(arg, head, strlen(head), 0);//循环读取本地文件数据发送给浏览器while (1){unsigned char buf[512] = "";// 读取本地文件数据int len = read(fd, buf, sizeof(buf));send(arg, buf, len, 0);if (len < 512)break;}close(fd);_exit(-1);
}
代码运行结果

结束
代码重在练习!
代码重在练习!
代码重在练习!
今天的分享就到此结束了,希望对你有所帮助,如果你喜欢我的分享,请点赞收藏夹关注,谢谢大家!!!
相关文章:
Linux网络编程 多线程Web服务器:HTTP协议与TCP并发实战
问题解答 TCP是如何防止SYN洪流攻击的? 方式有很多种,我仅举例部分: 1、调整内核参数 我们知道SYN洪流攻击的原理就是发送一系列无法完成三次握手的特殊信号,导致正常的能够完成三次握手的信号因为 连接队列空间不足ÿ…...
【Vulkan 入门系列】创建帧缓冲、命令池、命令缓存,和获取图片(六)
这一节主要介绍创建帧缓冲(Framebuffer),创建命令池,创建命令缓存,和从文件加载 PNG 图像数据,解码为 RGBA 格式,并将像素数据暂存到 Vulkan 的 暂存缓冲区中。 一、创建帧缓冲 createFramebu…...
【Git】fork 和 branch 的区别
在 Git 中,“fork” 和 “branch” 是两个不同的概念,它们用于不同的场景并且服务于不同的目的。理解这两者的区别对于有效地使用 Git 进行版本控制非常重要。 1. Fork(分叉) 定义 Fork 是指在 GitHub、GitLab 等代码托管平台上…...
Qt 下载的地址集合
Qt 下载离线安装包 download.qt.io/archive/qt/5.14/5.14.2/ Qt 6 安装下载在线安装包 Index of /qt/official_releases/online_installers/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror...
java将pdf转换成word
1、jar包准备 在项目中新增lib目录,并将如下两个文件放入lib目录下 aspose-words-15.8.0-jdk16.jar aspose-pdf-22.9.jar 2、pom.xml配置 <dependency><groupId>com.aspose</groupId><artifactId>aspose-pdf</artifactId><versi…...
ubuntu下gcc/g++安装及不同版本切换
1. 查看当前gcc版本 $ gcc --version# 查看当前系统中已安装版本 $ ls /usr/bin/gcc*2. 安装新版本gcc $ sudo apt-get update# 这里以版本12为依据(也可以通过源码方式安装,请自行Google!) $ sudo apt-get install -y gcc-12 g…...
缓存与内存;缺页中断;缓存映射:组相联
文章目录 内存(RAM)与缓存(Cache)Memory Management Unit缺页中断 多级缓存缓存替换策略缓存的映射方式 内存(RAM)与缓存(Cache) 缓存: CPU 内部或非常靠近的高速存储&a…...
FPGA入门学习Day1——设计一个DDS信号发生器
目录 一、DDS简介 (一)基本原理 (二)主要优势 (三)与传统技术的对比 二、FPGA存储器 (一)ROM波形存储器 (二)RAM随机存取存储器 (三&…...
微信小程序拖拽排序有效果图
效果图 .wxml <view class"container" style"--w:{{w}}px;" wx:if"{{location.length}}"><view class"container-item" wx:for"{{list}}" wx:key"index" data-index"{{index}}"style"--…...
elasticsearch 查询检索
一、查询方式列举 1、多维度查询 关键词:bool must match {"query": {"bool": {"must": [{"match": {"server_name": "www.test.com"}},{"range": { //时间查询"createTime": …...
WT2000T专业录音芯片:破解普通录音设备信息留存、合规安全与远程协作三大难题
在快节奏的现代商业环境中,会议是企业决策、创意碰撞和战略部署的核心场景。然而,传统会议记录方式常面临效率低、信息遗漏、回溯困难等痛点。如何确保会议内容被精准记录并高效利用?会议室专用录音芯片应运而生,以智能化、高保真…...
【Python 学习笔记】 pip指令使用
系列文章目录 pip指令使用 文章目录 系列文章目录前言安装配置使用pip 管理Python包修改pip下载源 前言 提示:这里可以添加本文要记录的大概内容: 当前文章记录的是我在学习过程的一些笔记和思考,可能存在有误解的地方,仅供大家…...
与Ubuntu相关命令
windows将文件传输到Ubuntu 传输文件夹或文件 scp -r 本地文件夹或文件 ubuntu用户名IP地址:要传输到的文件夹路径 例如: scp -r .\04.py gao192.168.248.129:/home/gao 如果传输文件也可以去掉-r 安装软件 sudo apt-get update 更新软件包列表 sudo apt insta…...
C# 文件读取
文件读取是指使用 C# 程序从计算机文件系统中获取文件内容的过程。将存储在磁盘上的文件内容加载到内存中,供程序处理。主要类型有:文本文件读取(如 .txt, .csv, .json, .xml);二进制文件读取(如 .jpg, .pn…...
leetcode125.验证回文串
class Solution {public boolean isPalindrome(String s) {s s.replaceAll("[^a-zA-Z0-9]", "").toLowerCase();for(int i0,js.length()-1;i<j;i,j--){if(s.charAt(i)!s.charAt(j))return false;}return true;} }...
【Android面试八股文】Android系统架构【一】
Android系统架构图 1.1 安卓系统启动 1.设备加电后执行第一段代码:Bootloader 系统引导分三种模式:fastboot,recovery,normal: fastboot模式:用于工厂模式的刷机。在关机状态下,按返回开机 键进…...
NLP高频面试题(五十二)——BERT 变体详解
在现代自然语言处理领域,BERT 系列模型不断演进,衍生出多种变体,它们通过改进预训练任务、模型结构和训练策略,在不同应用场景下取得了更优表现。本文首先概览主要 BERT 变体(如 ALBERT、RoBERTa、ELECTRA、SpanBERT、Transformer-XL 等),随后针对以下几个关键问题逐一展…...
【数据可视化-21】水质安全数据可视化:探索化学物质与水质安全的关联
🧑 博主简介:曾任某智慧城市类企业算法总监,目前在美国市场的物流公司从事高级算法工程师一职,深耕人工智能领域,精通python数据挖掘、可视化、机器学习等,发表过AI相关的专利并多次在AI类比赛中获奖。CSDN…...
CSS 选择器介绍
CSS 选择器介绍 1. 基本概念 CSS(层叠样式表)是一种用于描述 HTML 或 XML 文档外观的语言。通过 CSS,可以控制网页中元素的布局、颜色、字体等视觉效果。而 CSS 选择器则是用来指定哪些 HTML 元素应该应用这些样式的工具。 2. 基本选择器 …...
【prometheus+Grafana篇】从零开始:Linux 7.6 上二进制安装 Prometheus、Grafana 和 Node Exporter
💫《博主主页》:奈斯DB-CSDN博客 🔥《擅长领域》:擅长阿里云AnalyticDB for MySQL(分布式数据仓库)、Oracle、MySQL、Linux、prometheus监控;并对SQLserver、NoSQL(MongoDB)有了解 💖如果觉得文章对你有所帮…...
STM32(M4)入门:GPIO与位带操作(价值 3w + 的嵌入式开发指南)
一:GPIO 1.1 了解时钟树(必懂的硬件基础) 在 STM32 开发中,时钟系统是一切外设工作的 “心脏”。理解时钟树的工作原理,是正确配置 GPIO、UART 等外设的核心前提。 1.1.1 为什么必须开启外设时钟? 1. 计…...
树莓派超全系列教程文档--(42)树莓派config.txt旧版配置HDMI和杂项选项
树莓派config.txt旧版配置HDMI和杂项选项 Raspberry Pi 4 HDMI遗留的杂项选项avoid_warningslogging_level 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 Raspberry Pi 4 HDMI IMPORTANT: 使用VC4 KMS图形驱动程序时,完整的显示管道…...
Linux419 三次握手四次挥手抓包 wireshark
还是Notfound 没连接 可能我在/home 准备配置静态IP vim ctrlr 撤销 u撤销 配置成功 准备关闭防火墙 准备配置 YUM源 df -h 未看到sr0文件 准备排查 准备挂载 还是没连接 计划重启 有了 不重启了 挂载准备 修改配置文件准备 准备清理缓存 ok 重新修改配…...
CSS-跟随图片变化的背景色
CSS-跟随图片变化的背景色 获取图片的主要颜色并用于背景渐变需要安装依赖 colorthief获取图片的主要颜色. 并丢给背景注意 getPalette并不是个异步方法 import styles from ./styles.less; import React, { useState } from react; import Colortheif from colorthief;cons…...
解决Docker 配置 daemon.json文件后无法生效
vim /etc/docker/daemon.json 在daemon中配置一下dns {"registry-mirrors": ["https://docker.m.daocloud.io","https://hub-mirror.c.163.com","https://dockerproxy.com","https://docker.mirrors.ustc.edu.cn","ht…...
虚幻基础:ue碰撞
文章目录 碰撞:碰撞体 运动后 产生碰撞的行为——碰撞响应由引擎负责,并向各自发送事件忽略重叠阻挡 碰撞响应关系有忽略必是忽略有重叠必是重叠有阻挡不一定阻挡(双方都为阻挡) 碰撞启用:纯查询:开启移动检…...
2025.04.23【探索工具】| STEMNET:高效数据排序与可视化的新利器
文章目录 1. STEMNET工具简介2. STEMNET的安装方法3. STEMNET常用命令 1. STEMNET工具简介 在生物信息学领域,分析和处理大规模数据集是研究者们面临的日常挑战。STEMNET工具应运而生,旨在提供一个强大的平台,用于探索和分析单细胞RNA测序&a…...
GitLab Runner配置并行执行多个任务
检查并修改方法: 打开 Runner 的配置文件(通常位于 /etc/gitlab-runner/config.toml 或 ~/.gitlab-runner/config.toml)。 确保 concurrent 值大于 1,例如: concurrent 4 # 允许最多 4 个任务同时运行重启 Runner…...
深入理解前端安全:CSRF与XSS攻击详解
引言 在Web开发的世界里,安全性就像是房子的门锁。你可能觉得它不显眼,但一旦没了它,麻烦可就大了!本文将深入探讨两大前端安全威胁:CSRF(跨站请求伪造)和XSS(跨站脚本攻击…...
Docker 中运行 JAR 文件
文章目录 步骤 1:准备文件结构步骤 2:编写 Dockerfile步骤 3:构建 Docker 镜像步骤 4:运行容器常见问题解决Q1:容器启动后立即退出Q2:时区不一致Q3:依赖外部服务(如MySQL)…...
