目录
[1、HTML 基础概念](#1、HTML 基础概念)
一、引言------回顾上文

- HTTP请求方法:GET,POST
- HTTP应答状态:100~500
二、解析报文
1、创建套接字,指定端口创建监听队列
2、接受连接,此时需要浏览器发起请求,客户端才会返回。
3、recv接收整个报文,获取整个资源的名称(strtok分割函数)
4、打开文件,选择存放的路径,
5、开始解析,获取文件大小
注:这里没有采用多线程,只采用短连接。多线程可以实现多个浏览器同时连接。
cpp
#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,"");//用空格分割,第一次分割为get
while (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){//如果打开失败回复404
send(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文档包含以下核心部分:
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>(单元格)使用。
表单标签
html
<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、示例代码
html
<!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、函数原型
c
char *strtok(char *str, const char *delimiters);
- str : 待分割的字符串(首次调用时传入,后续调用需传入
NULL)。 - delimiters: 分隔符集合(每个字符均视为独立分隔符)。
- 返回值 : 返回下一个子串的指针;若无更多子串则返回
NUL
2、核心特性
-
修改原字符串
strtok会在分隔符位置插入'\0',直接修改原字符串。若需保留原字符串,应先拷贝再分割。 -
状态依赖
首次调用需传入目标字符串,后续调用需传入
NULL(函数内部静态指针记录剩余部分)。
3、使用示例
c
#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;
}
输出:
cpp
Token: apple
Token: banana
Token: cherry
4、注意事项
-
线程不安全
静态指针导致
strtok不可重入。多线程环境下应使用strtok_r(POSIX 标准)或strtok_s(C11 标准)。 -
连续分隔符处理
连续的分隔符会被视为单个分隔符。例如
"a,,b"按","分割得到"a"和"b"。 -
空字符串
若字符串仅含分隔符(如
","),首次调用返回NULL。
5、替代方案
-
strtok_r(可重入版本)cchar *strtok_r(char *str, const char *delim, char **saveptr);通过
saveptr参数保存状态,避免静态变量。 -
strsep(BSD 扩展)cchar *strsep(char **stringp, const char *delim);更高效但会修改输入指针,适合高频分割场景。
6、常见问题
-
分割后原字符串丢失
cchar *str = strdup("a,b,c"); // 必须拷贝原字符串 char *token = strtok(str, ","); // 使用后释放拷贝的字符串 free(str); -
混合分隔符
cchar str[] = "apple.banana,cherry"; char *token = strtok(str, ".,"); // 同时使用 '.' 和 ','