这里接着介绍上篇的内容:
本来构造响应报文应该在上一篇一起介绍的,东西比较多就留到这里说了。
这里我只弄了最基础简单的5行,还有非常多功能是没有的,有兴趣的可能去查看http/1.1的RFC文档,现在都有中文版pdf的了,或者也可以使用curl命令先看看apache服务器构造了哪些:
下面只说有实现的,其他如Cache缓存控制等可以去查上面说的RFC文档
第一行:Connection:keep-alive
持续连接,默认http/1.1就是持续的,可以不用发这个
第2行:Date:…
时间,也不是必须的,强调一下,这里的时间是你收到请求响应的服务器时间,并不是显示到你浏览器的时间,因为传输过程中还是会有一些时延的
第3行:Server:…
也不是必须的,比如apache的就会写成:Apache/2.4.10,可以改成自己喜欢的内容
第4行:Content-Length: 95
发送实体的长度,即这里文件1.html的长度
第5行:Content-Type:text/html
用于定义网络文件的类型和网页的编码,决定浏览器将以什么形式、什么编码读取这个文件;如果这里你没有指定类型,比如这时传输一个html文档,360浏览器可能就不会显示出来而是直接下载该文档了;
Content-Type有很多类型,详见http/1.1 RFC文档
下面是简单客户端的C代码:
#include <Winsock2.h> #include <stdio.h> #include<string.h> #pragma comment(lib,"Ws2_32.lib") #define BUF_LENGTH 1024 #define USER_ERROR -1 int main() { WSADATA wsData; SOCKET sClient; //客户端套接字 struct sockaddr_in ser; //服务器进程地址 char send_buf[BUF_LENGTH]; //发送缓存 char recv_buf[BUF_LENGTH]; //接收缓存 int iSend, iRecv; //接收数据和发送数据长度 //第一步:加载协议栈 if (WSAStartup(MAKEWORD(2,2),&wsData)!=0) { printf("Failed to load Winsock.\n"); return USER_ERROR; } //创建服务器地址 ser.sin_family=AF_INET; ser.sin_port=htons(80); //服务器端口号 ser.sin_addr.s_addr=inet_addr("127.0.0.1"); //服务器IP地址 //第二步:创建流套接字,运输层采用TCP协议 sClient=socket(AF_INET,SOCK_STREAM,0); if (sClient==INVALID_SOCKET) { printf("socket() Failed:%d\n",WSAGetLastError()); return USER_ERROR; } //第三步:通过创建的套接字向相应的服务器发起连接请求 if (connect(sClient,(struct sockaddr*)&ser,sizeof(ser))==INVALID_SOCKET) { printf("connet() Failed:%d\n",WSAGetLastError()); return USER_ERROR; } else //连接成功 { //第四步:通过套接字发数据 printf("input data to send:\n"); memset(send_buf,0,sizeof(send_buf)); //缓存清0 gets(send_buf); iSend=send(sClient,send_buf,sizeof(send_buf),0); //通过套接字发送数据给服务器 if (iSend==SOCKET_ERROR) //发送不成功 { printf("send() Failed:%d\n",WSAGetLastError()); return USER_ERROR; } else printf("send success\n"); //发送成功 printf("recv data from server: \n"); while(1) { memset(recv_buf,0,sizeof(recv_buf)); //缓存清0 iRecv=recv(sClient,recv_buf,sizeof(recv_buf),0); if(strcmp(recv_buf,"") == 0) // 接收缓存为空则表示接受完毕,退出 break; if (iRecv ==SOCKET_ERROR) //接收错误 { printf("recv() Failed:%d\n",WSAGetLastError()); return USER_ERROR; } else printf("%s\n",recv_buf); // 打印接收缓存 } } //第五步:释放该套接字,断开连接 closesocket(sClient); WSACleanup(); return 0; }
首先运行上次的web服务器,然后再运行客户端:
例1、输入:HEAD /1.html HTTP/1.1 表示使用HEAD方法获取首部
例2、输入:HEAD /1.htmlxxx HTTP/1.1
例3、输入:GET /1.html HTTP/1.1 返回首部和实体,即这里的文件内容
例4、输入:GET /1.htmlxxx HTTP/1.1