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

《TCP IP网络编程》第二十四章

第 24 章 制作 HTTP 服务器端

24.1 HTTP 概要

        本章将编写 HTTP(HyperText Transfer Protocol,超文本传输协议)服务器端,即 Web 服务器端

理解 Web 服务器端:

        web服务器端就是要基于 HTTP 协议,将网页对应文件传输给客户端的服务器端。Hypertext(超文本)是可以根据客户端请求而跳转的结构化信息。

HTTP:

        HTTP协议是以超文本传输为目的而设计的应用层协议,这种协议同样属于基于TCP/IP实现的协议,从结果上看,实现该协议相当于实现web服务器端。

        此外,浏览器也属于基于套接字的客户端,因为连接到任意Web服务器端时,浏览器内部也会创建套接字,只不过浏览器多了一项功能:将服务器端传输的HTML格式的超文本解析为可读性较强的视图。 总之,Web服务器端是以HTTP协议为基础传输超文本的服务器端

        HTTP是无状态的 Stateless 协议。如图: 

        从上图可以看出,服务器端相应客户端请求后立即断开连接。换言之,服务器端不会维持客户端状态。即使同一客户端再次发送请求,服务器端也无法辨认出是原先那个,而会以相同方式处理新请求。因此,HTTP 又称「无状态的 Stateless 协议」。

请求消息(Request Message)的结构:

        下面是客户端向服务端发起请求消息的结构:

        从图中可以看出,请求消息可以分为请求头、消息头、消息体 3 个部分。其中,请求行含有请求方式(请求目的)信息。典型的请求方式有 GET 和 POST ,GET 主要用于请求数据,POST 主要用于传输数据。为了降低复杂度,我们实现只能响应 GET 请求的 Web 服务器端,下面解释图中的请求行信息。其中「GET/index.html HTTP/1.1」 具有如下含义: 

        请求(GET)index.html 文件,通常以 1.1 版本的 HTTP 协议进行通信。

        请求行只能通过 1 行(line)发送,因此,服务器端很容易从 HTTP 请求中提取第一行,并分别分析请求行中的信息。

        请求行下面的消息头中包含发送请求的浏览器信息、用户认证信息等关于 HTTP 消息的附加信息。最后的消息体中装有客户端向服务端传输的数据,为了装入数据,需要以 POST 方式发送请求。但是我们的目标是实现 GET 方式的服务器端,所以可以忽略这部分内容。另外,消息体和消息头与之间以空行隔开,因此不会发生边界问题

响应消息(Response Message)的结构:

        下面是 Web 服务器端向客户端传递的响应信息的结构。从图中可以看出,该响应消息由状态行、头信息、消息体等 3 个部分组成。状态行中有关于请求的状态信息,这是与请求消息相比最为显著地区别。

        第一个字符串状态行中含有关于客户端请求的处理结果。例如,客户端请求 index.html 文件时,表示 index.html 文件是否存在、服务端是否发生问题而无法响应等不同情况的信息写入状态行。图中的「HTTP/1.1 200 OK」具有如下含义:

        我想用HTTP1.1版本进行响应,你的请求已正确处理(200,OK)。

        表示“客户端请求的执行结果”的数字成为状态码,典型的有以下几种:

  • 200 OK : 成功处理了请求!
  • 404 Not Found : 请求的文件不存在!
  • 400 Bad Request : 请求方式错误,请检查!

        消息头中含有传输的数据类型和长度等信息。图中的消息头含有如下信息:

   服务端名为 SimpleWebServer ,传输的数据类型为 text/html。数据长度不超过 2048 个字节。

        最后插入一个空行后,通过消息体发送客户端请求的文件数据。以上就是实现 Web 服务端过程中必要的 HTTP 协议。

24.2 实现简单的 Web 服务器端

实现基于 Linux 的多线程 Web 服务器端:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <pthread.h>#define BUF_SIZE 1024
#define SMALL_BUF 100void *request_handler(void *arg);
void send_data(FILE *fp, char *ct, char *file_name);
char *content_type(char *file);
void send_error(FILE *fp);
void error_handling(char *message);int main(int argc, char *argv[])
{int serv_sock, clnt_sock;struct sockaddr_in serv_adr, clnt_adr;int clnt_adr_size;char buf[BUF_SIZE];pthread_t t_id;if (argc != 2){printf("Usage : %s <port>\n", argv[0]);exit(1);}serv_sock = socket(PF_INET, SOCK_STREAM, 0);memset(&serv_adr, 0, sizeof(serv_adr));serv_adr.sin_family = AF_INET;serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);serv_adr.sin_port = htons(atoi(argv[1]));if (bind(serv_sock, (struct sockaddr *)&serv_adr, sizeof(serv_adr)) == -1)error_handling("bind() error");if (listen(serv_sock, 20) == -1)error_handling("listen() error");while (1){clnt_adr_size = sizeof(clnt_adr);clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_adr, &clnt_adr_size);printf("Connection Request : %s:%d\n",inet_ntoa(clnt_adr.sin_addr), ntohs(clnt_adr.sin_port));// 创建线程来处理客户端请求pthread_create(&t_id, NULL, request_handler, &clnt_sock);pthread_detach(t_id);}close(serv_sock);return 0;
}
// 处理客户端请求的线程函数
void *request_handler(void *arg)
{int clnt_sock = *((int *)arg);char req_line[SMALL_BUF];FILE *clnt_read;FILE *clnt_write;char method[10];char ct[15];char file_name[30];clnt_read = fdopen(clnt_sock, "r");clnt_write = fdopen(dup(clnt_sock), "w");fgets(req_line, SMALL_BUF, clnt_read);// 检查请求行中是否包含 "HTTP/"if (strstr(req_line, "HTTP/") == NULL){send_error(clnt_write);fclose(clnt_read);fclose(clnt_write);return;}// 解析请求行,提取请求方法和文件名strcpy(method, strtok(req_line, " /"));strcpy(file_name, strtok(NULL, " /"));strcpy(ct, content_type(file_name));// 检查请求方法是否为 "GET"if (strcmp(method, "GET") != 0){send_error(clnt_write);fclose(clnt_read);fclose(clnt_write);return;}// 关闭读取文件流,然后发送数据给客户端fclose(clnt_read);send_data(clnt_write, ct, file_name);
}
// 发送数据给客户端
void send_data(FILE *fp, char *ct, char *file_name)
{char protocol[] = "HTTP/1.0 200 OK\r\n";char server[] = "Server:Linux Web Server \r\n";char cnt_len[] = "Content-length:2048\r\n";char cnt_type[SMALL_BUF];char buf[BUF_SIZE];FILE *send_file;sprintf(cnt_type, "Content-type:%s\r\n\r\n", ct);send_file = fopen(file_name, "r");// 检查文件是否存在if (send_file == NULL){send_error(fp);return;}//传输头信息fputs(protocol, fp);fputs(server, fp);fputs(cnt_len, fp);fputs(cnt_type, fp);//传输请求数据while (fgets(buf, BUF_SIZE, send_file) != NULL){fputs(buf, fp);fflush(fp);}fflush(fp);fclose(fp);
}
// 获取文件的内容类型
char *content_type(char *file)
{char extension[SMALL_BUF];char file_name[SMALL_BUF];strcpy(file_name, file);strtok(file_name, ".");strcpy(extension, strtok(NULL, "."));if (!strcmp(extension, "html") || !strcmp(extension, "htm"))return "text/html";elsereturn "text/plain";
}
void send_error(FILE *fp)
{char protocol[] = "HTTP/1.0 400 Bad Request\r\n";char server[] = "Server:Linux Web Server \r\n";char cnt_len[] = "Content-length:2048\r\n";char cnt_type[] = "Content-type:text/html\r\n\r\n";char content[] = "<html><head><title>NETWORK</title></head>""<body><font size=+5><br>发生错误! 查看请求文件名和请求方式!""</font></body></html>";fputs(protocol, fp);fputs(server, fp);fputs(cnt_len, fp);fputs(cnt_type, fp);fflush(fp);
}
void error_handling(char *message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}

运行结果:

        经过测试,这个简单的 HTTP 服务器可以正常的显示出页面。 

相关文章:

《TCP IP网络编程》第二十四章

第 24 章 制作 HTTP 服务器端 24.1 HTTP 概要 本章将编写 HTTP&#xff08;HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff09;服务器端&#xff0c;即 Web 服务器端。 理解 Web 服务器端&#xff1a; web服务器端就是要基于 HTTP 协议&#xff0c;将网页对…...

【AI】文心一言的使用

一、获得内测资格&#xff1a; 1、点击网页链接申请&#xff1a;https://yiyan.baidu.com/ 2、点击加入体验&#xff0c;等待通过 二、获得AI伙伴内测名额 1、收到短信通知&#xff0c;点击链接 网页Link&#xff1a;https://chat.baidu.com/page/launch.html?fa&sourc…...

CSAPP Lab2:Bomb Lab

说明 6关卡&#xff0c;每个关卡需要输入相应的内容&#xff0c;通过逆向工程来获取对应关卡的通过条件 准备工作 环境 需要用到gdb调试器 apt-get install gdb系统: Ubuntu 22.04 本实验会用到的gdb调试器的指令如下 r或者 run或者run filename 运行程序,run filename就…...

Java中使用流将两个集合根据某个字段进行过滤去重?

Java中使用流将两个集合根据某个字段进行过滤去重? 在Java中&#xff0c;您可以使用流(Stream)来过滤和去重两个集合。下面是一个示例代码&#xff0c;展示如何根据对象的某个字段进行过滤和去重操作&#xff1a; import java.util.ArrayList; import java.util.List; impor…...

自动驾驶HMI产品技术方案

版本变更 序号 日期 变更内容 编制人 审核人 文档版本 1 2 1....

Git判断本地是否最新

场景需求 需要判断是否有新内容更新,确定有更新之后执行pull操作&#xff0c;然后pull成功之后再将新内容进行复制到其他地方 pgit log -1 --prettyformat:"%H" HEAD -- . "origin/HEAD" rgit rev-parse origin/HEAD if [[ $p $r ]];thenecho "Is La…...

Spring 整合RabbitMQ,笔记整理

1.创建生产者工程 spring-rabbitmq-producer 2.pom.xml添加依赖 <dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.1.7.RELEASE</version></dep…...

Lua 语言笔记(一)

1. 变量命名规范 弱类型语言(动态类型语言)&#xff0c;定义变量的时候&#xff0c;不需要类型修饰 而且&#xff0c;变量类型可以随时改变每行代码结束的时候&#xff0c;要不要分号都可以变量名 由数字&#xff0c;字母下划线组成&#xff0c;不能以数字开头&#xff0c;也不…...

【Redis】什么是缓存穿透,如何预防缓存穿透?

【Redis】什么是缓存穿透&#xff0c;如何预防缓存穿透&#xff1f; 缓存穿透是指查询一个一定不存在的数据&#xff0c;由于缓存中不存在&#xff0c;这时会去数据库查询查不到数据则不写入缓存&#xff0c;这将导致这个不存在的数据每次请求都要到数据库去查询&#xff0c;这…...

LeetCode128.最长连续序列

我这个方法有点投机取巧了&#xff0c;题目说时间复杂度最多O(n),而我调用了Arrays.sort(&#xff09;方法&#xff0c;他的时间复杂度是n*log(n)&#xff0c;但是AC了&#xff0c;这样的话这道题还是非常简单的&#xff0c;创建一个Hashmap&#xff0c;以nums数组的元素作为ke…...

Datawhale Django入门组队学习Task02

Task02 首先启动虚拟环境&#xff08;复习一下之前的&#xff09; 先退出conda的&#xff0c; conda deactivate然后cd到我的venv下面 &#xff0c;然后cd 到 scripts&#xff0c;再 activate &#xff08;powershell里面&#xff09; 创建admin管理员 首先cd到项目路径下&a…...

PCTA 认证考试高分通过经验分享

作者&#xff1a; msx-yzu 原文来源&#xff1a; https://tidb.net/blog/0b343c9f 序言 我在2023年8月10日&#xff0c;参加了 PingCAP 认证 TiDB 数据库专员 V6 考试 &#xff0c;并以 90分 的成绩通过考试。 考试总分是100分&#xff0c;超过60分就算通过考试。试卷…...

[Python]pytorch与C交互

文章目录 C库ctypes基础数据类型参数与返回值类型数组指针结构体类型回调函数工具函数 示例 ctypes是Python的外部函数&#xff0c;提供了与C兼容的类型&#xff0c;并允许调用DLL库中的函数。 C库 要使函数能被Python调用&#xff0c;需要编译为动态库&#xff1a; # -fPIC…...

C语言,静态变量static基础及使用实列

static关键字有多种用途。以下是关于静态变量 (static) 的简要概述&#xff1a; 1.静态局部变量&#xff1a; - 在函数内部定义的静态变量。 - 生命周期&#xff1a;从程序开始执行到程序结束。 - 作用域&#xff1a;仅限于在其被定义的函数中。 - 每次调用该函数…...

2023.8.19-2023.8.XX 周报【人脸3D+虚拟服装方向基础调研-Cycle Diffusion\Diffusion-GAN\】更新中

学习目标 1. 这篇是做diffusion和gan结合的&#xff0c;可以参照一下看看能不能做cyclegan的形式&#xff0c;同时也可以调研一下有没有人follow这篇论文做了类似cyclegan的事情 Diffusion-GAN论文精读https://arxiv.org/abs/2206.02262 2. https://arxiv.org/abs/2212.06…...

微表情识别(Python编程,cnn模型)

1.数据集包括7种类别微表情 anger文件夹&#xff0c;3995张 disgust文件夹&#xff0c; 436张照片 fear文件夹&#xff0c;4097张照片 happy文件夹&#xff0c;7215张照片 neutral文件夹&#xff0c;4965张照片 sad文件夹&#xff0c;4830张照片 surprised文件夹&#xff0c; 3…...

More Effective C++学习笔记(2)

目录 条款5&#xff1a;对定制的"类型转换函数"保持警觉条款6&#xff1a;自增(increment)、自减(decrement)操作符前缀形式与后缀形式的区别条款7&#xff1a;千万不要重载&&&#xff0c;||和&#xff0c;操作符条款8&#xff1a;了解各种不同意义的new和de…...

零售行业供应链管理核心KPI指标(三)

完美订单满足率和退货率 完美订单满足率有三个方面的因素影响&#xff1a;订单按时、足量、无损交货。通常情况下零售企业追求线上订单履行周期慢慢达到行业平均水平&#xff0c;就是交付的速度变快了&#xff0c;这个肯定是一件好事情&#xff0c;趋势越来越好。 同时&#…...

广州华锐互动:奶牛难产原因及救治VR仿真实训系统

奶牛难产是一种常见的疾病&#xff0c;对奶牛的健康和生产造成很大的影响。为了解决这一问题&#xff0c;许多奶牛养殖场开始采用VR仿真技术来培训奶牛兽医&#xff0c;帮助学生更好地理解奶牛养殖的实际过程&#xff0c;提高他们的实践能力的教学方式。 VR技术开发公司广州华锐…...

神经网络基础-神经网络补充概念-62-池化层

概念 池化层&#xff08;Pooling Layer&#xff09;是深度学习神经网络中常用的一种层级结构&#xff0c;用于减小输入数据的空间尺寸&#xff0c;从而降低模型的计算复杂度&#xff0c;减少过拟合&#xff0c;并且在一定程度上提取输入数据的重要特征。池化层通常紧跟在卷积层…...

day52 ResNet18 CBAM

在深度学习的旅程中&#xff0c;我们不断探索如何提升模型的性能。今天&#xff0c;我将分享我在 ResNet18 模型中插入 CBAM&#xff08;Convolutional Block Attention Module&#xff09;模块&#xff0c;并采用分阶段微调策略的实践过程。通过这个过程&#xff0c;我不仅提升…...

ardupilot 开发环境eclipse 中import 缺少C++

目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

12.找到字符串中所有字母异位词

&#x1f9e0; 题目解析 题目描述&#xff1a; 给定两个字符串 s 和 p&#xff0c;找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义&#xff1a; 若两个字符串包含的字符种类和出现次数完全相同&#xff0c;顺序无所谓&#xff0c;则互为…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

人机融合智能 | “人智交互”跨学科新领域

本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...

华为OD机考-机房布局

import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...

C#学习第29天:表达式树(Expression Trees)

目录 什么是表达式树&#xff1f; 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持&#xff1a; 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...

代码规范和架构【立芯理论一】(2025.06.08)

1、代码规范的目标 代码简洁精炼、美观&#xff0c;可持续性好高效率高复用&#xff0c;可移植性好高内聚&#xff0c;低耦合没有冗余规范性&#xff0c;代码有规可循&#xff0c;可以看出自己当时的思考过程特殊排版&#xff0c;特殊语法&#xff0c;特殊指令&#xff0c;必须…...

R 语言科研绘图第 55 期 --- 网络图-聚类

在发表科研论文的过程中&#xff0c;科研绘图是必不可少的&#xff0c;一张好看的图形会是文章很大的加分项。 为了便于使用&#xff0c;本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中&#xff0c;获取方式&#xff1a; R 语言科研绘图模板 --- sciRplothttps://mp.…...

【LeetCode】算法详解#6 ---除自身以外数组的乘积

1.题目介绍 给定一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&#xff0c;且在 O…...