报文头 和fprint的说明 day45
三:send_head
报文头
给浏览器一个校准的头,不依靠浏览器的自身纠错能力
头部信息基本都是固定的,关注报文的响应报文,而不是请求报文
#include <asm-generic/socket.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h> /* See NOTES */
#include <time.h>
#include <unistd.h>
typedef struct sockaddr*(SA);
typedef enum
{FILE_HTML,FILE_PNG,FILE_JPG
} FILE_TYPE;
int send_head(int conn, char* filename, FILE_TYPE type)
{struct stat st;//用于获取文件大小int ret = stat(filename, &st);if (-1 == ret){// perror("");fprintf(stderr, "stat error , %s ,filename:%s\n", strerror(errno),filename);return 1;}char* httm_cmd[6] = {NULL};char typebuf[100] = {0};char lengbuf[50] = {0};//响应行 表是可以正常响应httm_cmd[0] = "HTTP/1.1 200 OK\r\n"; //200正常响应, //状态码(Status-Code)都是三位数字的,分为5大类共33种。//例如,1xx表示通知信息的,如请求收到了或正在进行处理。//2xx表示成功,如接受或知道了。//3xx表示重定向,如要完成请求还必须采取进一步的行动。 如304 比如照片无法正常缓存//4xx表示客户的差错,如请求中有错误的语法或不能完成。 如404 :意思是找不到//5xx表示服务器的差错,如服务器失效无法完成请求。httm_cmd[1] = "Server: Tengine\r\n"; // web server名字httm_cmd[2] = typebuf; // 告知对方浏览器,发送的文件的类型switch (type){case FILE_HTML://对应是那种类型,是活的sprintf(typebuf, "Content-Type: text/html; charset=UTF-8\r\n");break;case FILE_JPG:sprintf(typebuf, "content-type: image/jpeg\r\n");break;case FILE_PNG:sprintf(typebuf, "Content-Type: image/png\r\n");break;}// 告知对方浏览器,资源的大小httm_cmd[3] = lengbuf;sprintf(lengbuf, "Content-Length: %lu\r\n", st.st_size);//连接方式 长连接 closed(短链接)httm_cmd[4] = "Connection: keep-alive\r\n";//资源的过期时间httm_cmd[5] = "Date: Wed, 30 Jul 2025 03:21:39 GMT\r\n\r\n";for (int i = 0; i < 6; ++i){send(conn, httm_cmd[i], strlen(httm_cmd[i]), 0);}return 0;
}int send_file(int conn, char* filename, FILE_TYPE type)
{send_head(conn, filename, type);int fd = open(filename, O_RDONLY);if (-1 == fd){perror("open");return 1;}while (1){char buf[1024] = {0};int ret = read(fd, buf, sizeof(buf));if (ret <= 0){break;}send(conn, buf, ret, 0);}close(fd);return 0;
}
int main(int argc, char** argv)
{//监听套接字 功能检测是否有客户端 连连接服务器int listfd = socket(AF_INET, SOCK_STREAM, 0);if (-1 == listfd){perror("socket");return 1;}int on = 1;// man 7 socket 地址和断开可以多次绑定,这两个选项仅在测试时开启。setsockopt(listfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));setsockopt(listfd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on));// man 7 ipstruct sockaddr_in ser, cli;bzero(&ser, sizeof(ser));bzero(&cli, sizeof(cli));ser.sin_family = AF_INET;// sudo ./sser.sin_port = htons(80);// 代表本机地址 外部客户端可以连接到服务器ser.sin_addr.s_addr = INADDR_ANY;int ret = bind(listfd, (SA)&ser, sizeof(ser));if (-1 == ret){perror("bind");return 1;}// 同一时刻可以服务器建立连接的排队数listen(listfd, 3);socklen_t len = sizeof(cli);while (1){// 1 建立连接int conn = accept(listfd, (SA)&cli, &len);if (-1 == conn){perror("accept");close(conn);continue;}char buf[1024] = {0};// 2接收客户端的请求int rec_ret = recv(conn, buf, sizeof(buf), 0);if (rec_ret <= 0){close(conn);continue;}printf("%s\n", buf);// 3 处理请求+回应// GET / HTTP/1.1\r\nchar* method = strtok(buf, " ");//统一资源定为符char* url = strtok(NULL, " ");char* ver = strtok(NULL, "\r");if (0 == strcmp(url, "/")){send_file(conn, "./02.html",FILE_HTML); // login}else if (0 == strcmp(url, "/favicon.ico")){send_file(conn, "./2.png",FILE_PNG); // login}// /login?username=zhangsan&userpw=123else if (0 == strncmp(url, "/login", strlen("/login"))){char* name = strchr(url, '=');name += 1;char* end = strchr(name, '&');*end = '\0';char* pw = strchr(end + 1, '=');pw += 1;if (0 == strcmp(name, "zhangsan") && 0 == strcmp(pw, "123")){//账号 ,密码正确 发送功能页面send_file(conn, "./01.html",FILE_HTML);}else{//账号密码错误, 发送密码错误页面send_file(conn, "./03.html",FILE_HTML);}}else if (strlen(url) > 4 &&0 == strcmp(&url[strlen(url) - 3], "png")) // /100.png{send_file(conn, url + 1,FILE_PNG);}else if (strlen(url) > 4 &&0 == strcmp(&url[strlen(url) - 3], "jpg")) // /100.png{send_file(conn, url + 1,FILE_JPG);}// 4 断开连接close(conn);}close(listfd);// system("pause");return 0;
}
五:关于项目中fprintf
的一些说明
明白了!你的问题核心是:
fprintf(fp, " <td>%s</td>\n", result[i]);
中,%s
是怎么和result[i]
对应起来的?
它为什么不需要你手动逐字符扫过去(像fgetc()
那样)?是否可以详细解释一下fprintf()
的工作原理?
一:fprintf()
函数详解
什么是 fprintf()
fprintf()
是 C 语言中的格式化输出函数,用于将格式化的数据写入指定文件。- 它的工作原理和
printf()
类似,但输出目标不同:printf()
输出到标准输出(屏幕);fprintf(fp, ...)
输出到文件指针fp
指定的文件中。
c复制代码
int fprintf(FILE *stream, const char *format, ...);
stream
:文件指针,如fp
;format
:格式字符串;...
:对应格式字符串中%
占位符的参数。
二:fprintf()
的工作原理
格式字符串解析
fprintf()
会逐字符解析格式字符串:
c复制代码
fprintf(fp, " <td>%s</td>\n", result[i]);
-
遇到普通字符,如空格、
<
,t
,d
等,会直接写入文件。 -
遇到
%s
,会自动用result[i]
的内容替换。 -
整行内容最终写入文件,如:
css复制代码 <td>hello</td>
自动处理字符串输出
%s
表示输出一个字符串,fprintf()
会从传入的char*
地址开始,直到遇到\0
结束。- 无需手动逐字符写入或格式拼接。
三:与其他写入函数的对比
函数 | 用途 | 优点 | 缺点 |
---|---|---|---|
fprintf() | 格式化写入文件 | 自动格式化、最灵活 | 相对底层控制力弱 |
fputs() | 写入字符串 | 直接写字符串 | 不支持格式替换 |
fputc() | 写入单字符 | 精确控制 | 需要逐字符拼接 |
fgetc() | 读取单字符 | 用于读取,不写入 | 与写操作无关 |
四:常用格式符号
格式符 | 含义 | 示例值 |
---|---|---|
%s | 字符串 | "hello" |
%d | 十进制整数 | 123 |
%f | 浮点数 | 3.14 |
%c | 单个字符 | 'A' |
%% | 百分号本身 | % |