究竟什么是协议?
在socket api接口中,读写数据都是按“字符串”的方式发送和接收的,有时候不利于我们的处理和结构化数据,应用层协议因此而来,协议保证一端发送构造的数据,在另一端能够正确的进行解析。
HTTP协议–应用最广泛的一种协议
HTTP请求
首行:[⽅法] + [url] + [版本]
Header: 请求的属性, 冒号分割的键值对;每组属性之间使⽤\n分隔;遇到空⾏表⽰Header部分结束
Http常见Header:
User-Agent:使得服务器能够识别客户使用操作系统及版本、CPU 类型、浏览器及版本、浏览器渲染引擎、浏览器语言、浏览器插件等,(通过UA历史,了解了好软件都是要向前兼容的)
Cookie:辨别用户身份而储存在用户本地终端上的数据(通常经过加密)
Referer:从那个页面跳转过来的
location:搭配3xx状态码使⽤, 告诉客户端接下来要去哪⾥访问
Host: 客户端告知服务器, 所请求的资源是在哪个主机的哪个端⼝上;
我们顺便来对比一下 GET和POST方法的区别
1.官方答案
GET获取资源,POST向指定的资源提交要被处理的数据
GET在浏览器回退时是无害的,而POST会再次提交请求。
GET产生的URL地址可以被Bookmark,而POST不可以。
GET请求会被浏览器主动cache,而POST不会,除非手动设置。
GET请求只能进行url编码,而POST支持多种编码方式。
GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
GET请求在URL中传送的参数是有长度限制的,而POST没有。
对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
GET参数通过URL传递,POST放在Request body中。
2.深入答案
GET和POST是HTTP协议中的两种发送请求的方法。
1.HTTP是基于TCP/IP关于数据如何在万维网中如何通信的协议。(HTTP的底层是TCP/IP。所以GET和POST的底层也是TCP/IP,也就是说,GET/POST都是TCP链接。GET和POST能做的事情是一样一样的。你要给GET加上request body,给POST带上url参数,技术上是完全行的通的。)2.GET产生一个TCP数据包;POST产生两个TCP数据包。(对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据))
HTTP响应
首行: [版本号] + [状态码] + [状态码解释]
Header: 请求的属性, 冒号分割的键值对;每组属性之间使⽤\n分隔;遇到空⾏表⽰Header部分结束
Body: 空⾏后⾯的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Header中会有⼀个
Content-Length属性来标识Body的⻓度; 如果服务器返回了⼀个html⻚⾯, 那么html⻚⾯内容就是在
body中
应用层熟知端口列表
应用程序 | FTP | SSH | TELNET | SMTP | DNS | TFTP | HTTP | HTTPS |
---|---|---|---|---|---|---|---|---|
熟知端口号 | 21 | 22 | 23 | 25 | 53 | 69 | 80 | 443 |
HTTP状态码
1xx 信息状态码 接收的请求正在处理
2xx 成功状态吗 请求正常处理完毕
3xx 重定向状态码 需要进行附加操作
4xx 客户端错误状态吗 服务器无法处理请求
5xx 服务器错误状态码 服务器处理请求出错
常见状态码:200(OK),404(Not Found), 403(Forbidden), 302(Redirect, 重定向), 504(Bad Gateway)
超简易版的http服务器
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
typedef struct sockaddr sockaddr;
typedef struct sockaddr_in sockaddr_in;
void PackageResponse(char http_resp[])
{
const char* hello ="<h1>hello world</h1>";
int cur_len = 0;
//cur_len += sprintf(http_resp,"HTTP/1.1 504 OK\n");
cur_len += sprintf(http_resp,"HTTP/1.1 200 OK\n");
// cur_len += sprintf(http_resp,"HTTP/1.1 302 OK\n");
//cur_len += sprintf(http_resp+cur_len,"Location:https://www.baidu.com\n");
cur_len += sprintf(http_resp+cur_len,"Content-Length:%d\n",strlen(hello));
cur_len += sprintf(http_resp+cur_len,"\n");
cur_len += sprintf(http_resp+cur_len,"%s",hello);
}
int main(int argc,char* argv[]){
if(argc!=3){
perror("Error Usage");
return 1;
}
int fd = socket(AF_INET,SOCK_STREAM,0);
if(fd<0){
perror("socket");
return 1;
}
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(argv[1]);
addr.sin_port = htons(atoi(argv[2]));
int ret = bind(fd,(sockaddr*)&addr,sizeof(addr));
if(ret<0){
perror("bind");
return 1;
}
ret = listen(fd,5);
if(ret<0){
perror("listen");
return 1;
}
while(1){
sockaddr_in peer_addr;
socklen_t len = sizeof(peer_addr);
int new_fd = accept(fd,(sockaddr*)&peer_addr,&len);
if(new_fd<0){
perror("accept");
return 1;
}
char buf[100*1024] = {0};
ssize_t read_size = read(new_fd,buf,sizeof(buf)-1);
if(read_size<0){
perror("read");
close(new_fd);;
return 1;
}
if(read_size==0){
close(new_fd);
continue;
}
printf("\n%s",buf);
char http_resp[100*1024]={0};
PackageResponse(http_resp);
write(new_fd,http_resp,strlen(http_resp));
close(new_fd);
}
return 0;
}
运行实例: