【计算机网络】HTTP协议(二)——超文本传输协议
目录
一、引言——回顾上文
二、解析报文
三、HTML超文本标记语言
1、HTML 基础概念
2、基本结构
3、常用标签
4、、语义化标签(HTML5新增)
5、属性与注释
6、示例代码
四、strtok分割函数
1、函数原型
2、核心特性
3、使用示例
4、注意事项
5、替代方案
6、常见问题
一、引言——回顾上文

- HTTP请求方法:GET,POST
- HTTP应答状态:100~500
二、解析报文
1、创建套接字,指定端口创建监听队列
2、接受连接,此时需要浏览器发起请求,客户端才会返回。
3、recv接收整个报文,获取整个资源的名称(strtok分割函数)
4、打开文件,选择存放的路径,
5、开始解析,获取文件大小
注:这里没有采用多线程,只采用短连接。多线程可以实现多个浏览器同时连接。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>//打开方式
char *get_filename(char buff[]){char *s=strtok(buff,"");//用空格分割,第一次分割为getwhile (s==NULL){return NULL;}s=strtok(NULL,"");//第二次为要解析的数据return s;
}
int main(){//创建套接字,返回值:文件描述符,参数:收到,流式服务int sockfd = socket(AF_INET,SOCK_STREAM,0);if(sockfd==-1){exit(1);//退出}struct sockaddr_in saddr,caddr;//saddr代表服务器的,caddr代表客户端即浏览器memset(&saddr,0,sizeof(saddr));//先清空套接字地址,在进行填充saddr.sin_family =AF_INET;saddr.sin_port =htons(80);saddr.sin_addr.s_addr =inet_addr("127.0.0.1");//绑定ip和端口int res =bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));if(res==-1){printf("bind err\n");//绑定失败打印错误信息exit(1);}//创建监听队列res =listen(sockfd,5);if(res==-1){exit(1);}while(1){//短链接//接收客户端连接int len=sizeof(caddr);int c=accept(sockfd,(struct sockaddr*)&caddr,&len);//如果客户端不连接则会阻塞if(c<0){continue;}printf("accept c=%d\n",c);//c就是描述符,通过描述符和客户端进行沟通char buff[1024]={0};int n=recv(c,buff,1023,0);//这时从c上接收数据,把数据存入buff中printf("buff=%s\n",buff);//给客户端回复数据char *filename=get_filename(buff);//解析数据if(filename==NULL){close(c);continue;}//设置存放位置char path[256]={"/home/stu/mycode/c2408/day19"};//存放位置if(strcmp(filename,"/")==0){strcat(path,"/index.html");//如果filename只有一个/,自定义设置名字}else{strcat(path,filename);//若有多个,则拼接}int fd=open(path,O_RDONLY);//打开方式,只读if(fd==-1){//如果打开失败回复404send(c,"404",3,0);close(c);continue;}//发送头部//获得文件大小:lseek int size=lseek(fd,0,SEEK_END);//0=距离末尾多远,即0个字节。获取文件大小,文件偏移量移动到文件末尾lseek(fd,0,SEEK_SET);//复原文件偏移量到起始位置,否则下次读不到char head[256]="HTTP/1.1 200 ok\r\n";strcat(head,"Server:myhttp\r\n");sprintf(head+strlen(head),"Content=Length:%d\r\n",size);//数据部分和头部之间有一个空格请求,标识头部字段结束strcat(head,"\r\n");//空行//数据部分// strcat(head,"hello");send(c,head,strlen(head),0);printf("head:\n%s\n",head);//发送数据部分char data[1024]={0};//文件数据int num=0;//每次读取多少while(num=read(fd,data,1024)>0){//从fd读取,存储到data中,希望读1024//如个num>0,则读到数据send(c,data,num,0);//把data中的数据发送给浏览器,发送num个}close(fd);close(c);}
}
三、HTML超文本标记语言
1、HTML 基础概念
HTML(HyperText Markup Language)是用于创建网页的标准标记语言。它通过标签(Tags)定义网页的结构和内容,例如文本、图像、链接等。浏览器解析HTML代码后渲染出可视化页面。
2、基本结构
一个标准的HTML文档包含以下核心部分:
<!DOCTYPE html>
<html>//开始标签
<head>//头部标签<title>页面标题</title><meta charset="UTF-8">
</head>
<body>//文件主体<!-- 页面内容 -->
</body>
</html>//末尾标签
<!DOCTYPE html>声明文档类型为HTML5。<html>是根元素,包含整个页面内容。<head>存放元数据(如标题、字符集、CSS/JS链接)。<body>包含用户可见的内容。
3、常用标签
文本标签
<h1>到<h6>:标题(h1最大,h6最小)。<p>:段落。<a href="url">:超链接。<strong>或<b>:加粗文本。<em>或<i>:斜体文本。
多媒体标签
<img src="image.jpg" alt="描述">:插入图片。<audio>和<video>:嵌入音视频。<iframe src="url">:嵌入其他网页。
列表与表格
<ul>(无序列表)和<ol>(有序列表)配合<li>使用。<table>定义表格,配合<tr>(行)、<td>(单元格)使用。
表单标签
<form action="/submit" method="post"><input type="text" name="username" placeholder="输入用户名"><input type="password" name="pwd"><button type="submit">提交</button>
</form>
<input>支持多种类型(text、password、checkbox等)。<textarea>多行文本输入。<select>下拉选择框。
4、、语义化标签(HTML5新增)
<header>:页眉或内容头部。<nav>:导航栏。<section>:文档中的独立区块。<article>:独立内容(如博客文章)。<footer>:页脚。
5、属性与注释
- 属性:为标签提供额外信息,如
<a href="https://example.com" target="_blank">中的target="_blank"表示在新窗口打开链接。 - 注释:
<!-- 注释内容 -->,不会在页面显示。
6、示例代码
<!DOCTYPE html>
<html>
<head><title>示例页面</title>
</head>
<body><h1>欢迎</h1><p>这是一个段落。<a href="https://example.com">示例链接</a></p><img src="example.jpg" width="200" alt="示例图片">
</body>
</html>
四、strtok分割函数
strtok 是 C 标准库(<string.h>)提供的字符串分割函数,用于按指定分隔符将字符串拆分为多个子串(token)。
1、函数原型
char *strtok(char *str, const char *delimiters);
- str: 待分割的字符串(首次调用时传入,后续调用需传入
NULL)。 - delimiters: 分隔符集合(每个字符均视为独立分隔符)。
- 返回值: 返回下一个子串的指针;若无更多子串则返回
NUL
2、核心特性
-
修改原字符串
strtok会在分隔符位置插入'\0',直接修改原字符串。若需保留原字符串,应先拷贝再分割。 -
状态依赖
首次调用需传入目标字符串,后续调用需传入NULL(函数内部静态指针记录剩余部分)。
3、使用示例
#include <stdio.h>
#include <string.h>int main() {char str[] = "apple,banana,cherry";const char *delim = ",";char *token = strtok(str, delim);while (token != NULL) {printf("Token: %s\n", token);token = strtok(NULL, delim);}return 0;
}
输出:
Token: apple
Token: banana
Token: cherry
4、注意事项
-
线程不安全
静态指针导致strtok不可重入。多线程环境下应使用strtok_r(POSIX 标准)或strtok_s(C11 标准)。 -
连续分隔符处理
连续的分隔符会被视为单个分隔符。例如"a,,b"按","分割得到"a"和"b"。 -
空字符串
若字符串仅含分隔符(如","),首次调用返回NULL。
5、替代方案
-
strtok_r(可重入版本)char *strtok_r(char *str, const char *delim, char **saveptr);通过
saveptr参数保存状态,避免静态变量。 -
strsep(BSD 扩展)char *strsep(char **stringp, const char *delim);更高效但会修改输入指针,适合高频分割场景。
6、常见问题
-
分割后原字符串丢失
char *str = strdup("a,b,c"); // 必须拷贝原字符串 char *token = strtok(str, ","); // 使用后释放拷贝的字符串 free(str); -
混合分隔符
char str[] = "apple.banana,cherry"; char *token = strtok(str, ".,"); // 同时使用 '.' 和 ','
