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

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洪流攻击的&#xff1f; 方式有很多种&#xff0c;我仅举例部分&#xff1a; 1、调整内核参数 我们知道SYN洪流攻击的原理就是发送一系列无法完成三次握手的特殊信号&#xff0c;导致正常的能够完成三次握手的信号因为 连接队列空间不足&#xff…...

【Vulkan 入门系列】创建帧缓冲、命令池、命令缓存,和获取图片(六)

这一节主要介绍创建帧缓冲&#xff08;Framebuffer&#xff09;&#xff0c;创建命令池&#xff0c;创建命令缓存&#xff0c;和从文件加载 PNG 图像数据&#xff0c;解码为 RGBA 格式&#xff0c;并将像素数据暂存到 Vulkan 的 暂存缓冲区中。 一、创建帧缓冲 createFramebu…...

【Git】fork 和 branch 的区别

在 Git 中&#xff0c;“fork” 和 “branch” 是两个不同的概念&#xff0c;它们用于不同的场景并且服务于不同的目的。理解这两者的区别对于有效地使用 Git 进行版本控制非常重要。 1. Fork&#xff08;分叉&#xff09; 定义 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目录&#xff0c;并将如下两个文件放入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为依据&#xff08;也可以通过源码方式安装&#xff0c;请自行Google&#xff01;&#xff09; $ sudo apt-get install -y gcc-12 g…...

缓存与内存;缺页中断;缓存映射:组相联

文章目录 内存&#xff08;RAM&#xff09;与缓存&#xff08;Cache&#xff09;Memory Management Unit缺页中断 多级缓存缓存替换策略缓存的映射方式 内存&#xff08;RAM&#xff09;与缓存&#xff08;Cache&#xff09; 缓存&#xff1a; CPU 内部或非常靠近的高速存储&a…...

FPGA入门学习Day1——设计一个DDS信号发生器

目录 一、DDS简介 &#xff08;一&#xff09;基本原理 &#xff08;二&#xff09;主要优势 &#xff08;三&#xff09;与传统技术的对比 二、FPGA存储器 &#xff08;一&#xff09;ROM波形存储器 &#xff08;二&#xff09;RAM随机存取存储器 &#xff08;三&…...

微信小程序拖拽排序有效果图

效果图 .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、多维度查询 关键词&#xff1a;bool must match {"query": {"bool": {"must": [{"match": {"server_name": "www.test.com"}},{"range": { //时间查询"createTime": …...

WT2000T专业录音芯片:破解普通录音设备信息留存、合规安全与远程协作三大难题

在快节奏的现代商业环境中&#xff0c;会议是企业决策、创意碰撞和战略部署的核心场景。然而&#xff0c;传统会议记录方式常面临效率低、信息遗漏、回溯困难等痛点。如何确保会议内容被精准记录并高效利用&#xff1f;会议室专用录音芯片应运而生&#xff0c;以智能化、高保真…...

【Python 学习笔记】 pip指令使用

系列文章目录 pip指令使用 文章目录 系列文章目录前言安装配置使用pip 管理Python包修改pip下载源 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 当前文章记录的是我在学习过程的一些笔记和思考&#xff0c;可能存在有误解的地方&#xff0c;仅供大家…...

与Ubuntu相关命令

windows将文件传输到Ubuntu 传输文件夹或文件 scp -r 本地文件夹或文件 ubuntu用户名IP地址:要传输到的文件夹路径 例如&#xff1a; scp -r .\04.py gao192.168.248.129:/home/gao 如果传输文件也可以去掉-r 安装软件 sudo apt-get update 更新软件包列表 sudo apt insta…...

C# 文件读取

文件读取是指使用 C# 程序从计算机文件系统中获取文件内容的过程。将存储在磁盘上的文件内容加载到内存中&#xff0c;供程序处理。主要类型有&#xff1a;文本文件读取&#xff08;如 .txt, .csv, .json, .xml&#xff09;&#xff1b;二进制文件读取&#xff08;如 .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.设备加电后执行第一段代码&#xff1a;Bootloader 系统引导分三种模式&#xff1a;fastboot&#xff0c;recovery&#xff0c;normal&#xff1a; fastboot模式&#xff1a;用于工厂模式的刷机。在关机状态下&#xff0c;按返回开机 键进…...

NLP高频面试题(五十二)——BERT 变体详解

在现代自然语言处理领域,BERT 系列模型不断演进,衍生出多种变体,它们通过改进预训练任务、模型结构和训练策略,在不同应用场景下取得了更优表现。本文首先概览主要 BERT 变体(如 ALBERT、RoBERTa、ELECTRA、SpanBERT、Transformer-XL 等),随后针对以下几个关键问题逐一展…...

【数据可视化-21】水质安全数据可视化:探索化学物质与水质安全的关联

&#x1f9d1; 博主简介&#xff1a;曾任某智慧城市类企业算法总监&#xff0c;目前在美国市场的物流公司从事高级算法工程师一职&#xff0c;深耕人工智能领域&#xff0c;精通python数据挖掘、可视化、机器学习等&#xff0c;发表过AI相关的专利并多次在AI类比赛中获奖。CSDN…...

CSS 选择器介绍

CSS 选择器介绍 1. 基本概念 CSS&#xff08;层叠样式表&#xff09;是一种用于描述 HTML 或 XML 文档外观的语言。通过 CSS&#xff0c;可以控制网页中元素的布局、颜色、字体等视觉效果。而 CSS 选择器则是用来指定哪些 HTML 元素应该应用这些样式的工具。 2. 基本选择器 …...

【prometheus+Grafana篇】从零开始:Linux 7.6 上二进制安装 Prometheus、Grafana 和 Node Exporter

&#x1f4ab;《博主主页》&#xff1a;奈斯DB-CSDN博客 &#x1f525;《擅长领域》&#xff1a;擅长阿里云AnalyticDB for MySQL(分布式数据仓库)、Oracle、MySQL、Linux、prometheus监控&#xff1b;并对SQLserver、NoSQL(MongoDB)有了解 &#x1f496;如果觉得文章对你有所帮…...

STM32(M4)入门:GPIO与位带操作(价值 3w + 的嵌入式开发指南)

一&#xff1a;GPIO 1.1 了解时钟树&#xff08;必懂的硬件基础&#xff09; 在 STM32 开发中&#xff0c;时钟系统是一切外设工作的 “心脏”。理解时钟树的工作原理&#xff0c;是正确配置 GPIO、UART 等外设的核心前提。 1.1.1 为什么必须开启外设时钟&#xff1f; 1. 计…...

树莓派超全系列教程文档--(42)树莓派config.txt旧版配置HDMI和杂项选项

树莓派config.txt旧版配置HDMI和杂项选项 Raspberry Pi 4 HDMI遗留的杂项选项avoid_warningslogging_level 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 Raspberry Pi 4 HDMI IMPORTANT: 使用VC4 KMS图形驱动程序时&#xff0c;完整的显示管道…...

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碰撞

文章目录 碰撞&#xff1a;碰撞体 运动后 产生碰撞的行为——碰撞响应由引擎负责&#xff0c;并向各自发送事件忽略重叠阻挡 碰撞响应关系有忽略必是忽略有重叠必是重叠有阻挡不一定阻挡&#xff08;双方都为阻挡&#xff09; 碰撞启用&#xff1a;纯查询&#xff1a;开启移动检…...

2025.04.23【探索工具】| STEMNET:高效数据排序与可视化的新利器

文章目录 1. STEMNET工具简介2. STEMNET的安装方法3. STEMNET常用命令 1. STEMNET工具简介 在生物信息学领域&#xff0c;分析和处理大规模数据集是研究者们面临的日常挑战。STEMNET工具应运而生&#xff0c;旨在提供一个强大的平台&#xff0c;用于探索和分析单细胞RNA测序&a…...

GitLab Runner配置并行执行多个任务

检查并修改方法&#xff1a; 打开 Runner 的配置文件&#xff08;通常位于 /etc/gitlab-runner/config.toml 或 ~/.gitlab-runner/config.toml&#xff09;。 确保 concurrent 值大于 1&#xff0c;例如&#xff1a; concurrent 4 # 允许最多 4 个任务同时运行重启 Runner…...

深入理解前端安全:CSRF与XSS攻击详解

引言 在Web开发的世界里&#xff0c;安全性就像是房子的门锁。你可能觉得它不显眼&#xff0c;但一旦没了它&#xff0c;麻烦可就大了&#xff01;本文将深入探讨两大前端安全威胁&#xff1a;CSRF&#xff08;跨站请求伪造&#xff09;和XSS&#xff08;跨站脚本攻击&#xf…...

Docker 中运行 JAR 文件

文章目录 步骤 1&#xff1a;准备文件结构步骤 2&#xff1a;编写 Dockerfile步骤 3&#xff1a;构建 Docker 镜像步骤 4&#xff1a;运行容器常见问题解决Q1&#xff1a;容器启动后立即退出Q2&#xff1a;时区不一致Q3&#xff1a;依赖外部服务&#xff08;如MySQL&#xff09…...