什么是http协议:基于tcp的超文本传输协议
http编程模型(B/S):
1.建立tcp连接
1.1 创建socket
1.2 设置socket
1.3 绑定
1.4 监听
2.等待客户端连接服务器 accept
3.接收客户端发来的请求并解析
4.制作响应
5.响应发送客户端
http特性:
1.支持C/S、B/S模式
2.简单且快速
3.灵活:可以传输任意类型数据
4.可以无连接:每次tcp处理一个(1.1版本)或多个(2.0版本以上)请求
5.无状态:协议对业务的处理没有记忆能力
优点:快速、简单、方便、效率高
缺点:明文传输数据,不是很可靠、不是很稳定
写一个通过http路径访问网页:
文件准备:
随便一张图片用于在网页中展示
网页html文件
方便编译运行的makefile文件(可以没有)
代码文件
index.html
<!DOCTYPE html>
<html lang="zh">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>httpServer</title>
</head>
<body><h1>day14_httpServer</h1><img src="./1.jpg" alt="">
</body>
</html>
server.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
// 创建监听套接字
int create_listenfd();
// 处理请求
void handle_request(int clientfd);
int main()
{// 1.建立tcp连接int serverfd = create_listenfd();while (1){// 2.接收客户端连接int clientfd = accept(serverfd, NULL, NULL);if (-1 == clientfd){printf("accept失败%m\n");continue;}// 3.处理客户端发来的请求handle_request(clientfd);}return 0;
}
int create_listenfd()
{printf("------start of socket------\n");// 1.1 创建socketint sfd = socket(AF_INET, SOCK_STREAM, 0);if (-1 == sfd){printf("create_listenfd中创建socket失败%m\n");return -1;}printf("创建socket成功\n");// 1.2 设置socketint n = 1;int r = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));if (-1 == r){printf("create_listenfd中setsockopt失败%m\n");return -1;}printf("设置socket成功\n");// 1.3 绑定struct sockaddr_in addr = {0};addr.sin_family = AF_INET;addr.sin_port = htons(80);addr.sin_addr.s_addr = INADDR_ANY; // 监听任意地址r = bind(sfd, (struct sockaddr *)&addr, sizeof(addr));if (-1 == r){printf("create_listenfd中bind失败%m\n");return -1;}printf("绑定成功\n");// 1.4 监听r = listen(sfd, 999999);if (-1 == r){printf("create_listenfd中listen失败%m\n");return -1;}printf("监听成功\n");printf("------end of socket------\n");return sfd;
}
void handle_request(int clientfd)
{// 接收请求char buff[1024 * 1024] = {0};int nread = read(clientfd, buff, sizeof(buff)); // 也可以用recvif (-1 == nread){printf("read失败%m\n");return;}// printf("%d: %s\n",nread,buff);// 解析请求// 获取文件名char filename[20] = {0};sscanf(buff, "GET /%s", filename);printf("文件名:%s\n", filename);// 根据文件名获取mime文件类// 用来放到响应头中,告诉浏览器返回的是什么类型的文件char *mime = NULL;if (strstr(filename, ".html")){mime = "text/html"; // 文本类型}else if (strstr(filename, ".jpg")){mime = "image/jpg"; // jpeg图片类型}// 制作响应char response[1024 * 1024] = {0};// 响应头//这里不直接用sprintf是因为sprintf会报warningconst char* header = "HTTP/1.1 200 OK\r\nContent-Type:%s\r\n\r\n";snprintf(response, sizeof(response), header, mime);int header_len = strlen(response);// 响应体int fd = open(filename, O_RDONLY);if(-1 == fd){printf("open %s 失败%m\n",filename);return;}int file_len = read(fd, response + header_len, sizeof(response) - header_len);// 发送响应write(clientfd, response, header_len + file_len);close(fd);
}