《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 服务器可以正常的显示出页面。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/75998.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CVE-2023-21292 AMS框架层高危漏洞分析

文章目录 前言漏洞细节故事起源漏洞利用漏洞修复 总结 前言 本周在分析 Google 官方发布的 Android 2023 年8 月安全公告 涉及的漏洞补丁的时候&#xff0c;遇到一个有意思的漏洞&#xff1a;CVE-2023-21292。 之所以说它有意思是因为这个漏洞早在去年年底就在某平台上被国外…

Git分布式版本控制系统

目录 2、安装git 2.1 初始环境 2.2 Yum安装Git 2.3 编译安装 2.4 初次运行 Git 前的配置 2.5 初始化及获取 Git 仓库 2.6 Git命令常规操作 2.6.2 添加新文件 2.6.3 删除git内的文件 2.6.4 重命名暂存区数据 2.6.5 查看历史记录 2.6.6 还原历史数据 2.6.7 还原未来…

泰迪大数据挖掘建模平台功能特色介绍

大数据挖掘建模平台面相高校、企业级别用户快速进行数据处理的建模工具。 大数据挖掘建模平台介绍 平台底层算法基于R语言、Python、Spark等引擎&#xff0c;使用JAVA语言开发&#xff0c;采用 B/S 结构&#xff0c;用户无需下载客户端&#xff0c;可直接通过浏览器进行…

【JavaSE】面向对象之多态

文章目录 多态的概念多态实现条件重写向上转型和向下转型向上转型向下转型 避免在构造方法中调用重写的方法 多态的概念 通俗来说&#xff0c;就是多种形态&#xff0c;具体点就是去完成某个行为&#xff0c;当不同的对象去完成时会产生出不同的状态。 多态实现条件 必须要…

Android JNI系列详解之JNI、NDK环境搭建和编译工具安装

本文主要介绍JNI、NDK环境变量的搭建&#xff0c;以及CMake工具的安装和ndk-build工具的安装。 一、JNI环境 JNI属于Java中的一部分&#xff0c;所以只需要搭建Java环境就有了JNI的环境&#xff0c;安装Java的环境可以网上查找教程&#xff0c;很多的安装JDK的博客。我电脑是安…

16.5.4 【Linux】SELinux 政策内的规则管理

SELinux 各个规则的布林值查询 getsebool 如果想要查询系统上面全部规则的启动与否 &#xff08;on/off&#xff0c;亦即布林值&#xff09;&#xff0c;很简单的通过 sestatus-b 或 getsebool -a 均可&#xff01; SELinux 各个规则规范的主体程序能够读取的文件 SELinux typ…

点亮一颗LED灯

TOC LED0 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//使能APB2的外设时钟GPIO_InitTypeDef GPIO_Initstructure;GPIO_Initstructure.GPIO_Mode GPIO_Mode_Out_PP;//通用推挽输出GPIO_Initstructure.GPIO_Pin GPIO_Pin_5;GPIO_Initstructure.GPIO_Speed GPIO_S…

mac上如何压缩视频大小?

mac上如何压缩视频大小&#xff1f;由于视频文件体积庞大&#xff0c;常常会占据我们设备的大量存储空间。通常情况下&#xff0c;我们选择删除视频以释放内存&#xff0c;但这将永久丢失它们。然而&#xff0c;有一种更好的方法可以在不删除视频的情况下减小内存占用&#xff…

漏洞指北-VulFocus靶场专栏-中级01

漏洞指北-VulFocus靶场专栏-中级01 中级001 &#x1f338;dcrcms 文件上传 &#xff08;CNVD-2020-27175)&#x1f338;step1&#xff1a;输入账号 密码burp suite 拦截 修改类型为 jpeg 中级002 &#x1f338;thinkphp3.2.x 代码执行&#x1f338;step1&#xff1a;burpsuite …

Python3 从简介及环境搭建到第一个入门程序

文章目录 前言一、Python3 简介1. 什么是python2. python的发展史3. python的特点 二、Python环境搭建1. Python3 下载2. Python安装3. 第一个Python3.x程序 总结 前言 为了巩固所学的知识&#xff0c;作者尝试着开始发布一些学习笔记类的博客&#xff0c;方便日后回顾。当然&…

基于Java+SpringBoot+Vue的学校田径运动会管理系统【源码+论文+演示视频+包运行成功】

博主介绍&#xff1a;✌擅长Java、微信小程序、Python、Android等&#xff0c;专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3fb; 不然下次找不到哟 Java项目精品实战案…

在项目中如何解除idea和Git的绑定

在项目中如何解除idea和Git的绑定 1、点击File--->Settings...(CtrlAltS)--->Version Control--->Directory Mappings--->点击取消Git的注册根路径&#xff1a; 2、回到idea界面就没有Git了&#xff1a; 3、给这个项目初始化 这样就可以重新绑定远程仓库了&#x…