长沙哪里学网站建设/网站制作app免费软件
文章目录
- 前言
- 一、认识URL
- urlencode和urldecode
- 二、http协议格式
- 三、http的请求方法
- GET和POST的区别
- 三、HTTP的状态码
- 重定向
- 四、HTTP常见的Header
- cookie
- session
- Connection
- 总结
前言
虽然说应用层协议是我们程序猿自己来定的!!!
但实际上,已经有大佬们定义了一些现成的,又非常好用的应用层协议,可以供我们直接参考使用.HTTP(超文本传输协议)就是其中的一个.
正文开始!
一、认识URL
但是在我们现在使用中,端口号大多都被省略了!
如下:
这是因为在使用确定协议的时候,一般显示的时候,会缺省端口号(有端口号被省略了).
所以,浏览器访问指定的URL的时候,浏览器必须给我们自动添加port;
浏览器如何得知,URL匹配的port是谁呢? 特定的众所周知的服务,端口号必须是确定的!!!
http->80
https->443
sshd->22
注意:
我们自己写的网络服务bind端口的时候,只能绑定[1024,n]范围内的端口号.
http就是获取网页资源的,视频,音频等的也都是文件!!!也就是向特定的服务器申请特定的"资源",然后获取到本地,进行展示和某种使用的!
如果client没有获取的时候,资源在哪里呢?—>远端的服务器!!!—>服务器都是Linux系统!—>这些资源都是文件!—>通过路径来访问文件!
urlencode和urldecode
像 / ? : 等这样的字符,已经被url当做特殊意义理解了.因此这些字符不能随意出现.
比如,某个参数中需要带有这些字符,就必须先对特殊字符进行转义.
转义的规则如下:将需要转码的字符转为16进制,然后从左到右,取4位(不足4位直接处理),每2位做1位,前面加上%,编码成%XY的格式.
例如:
“+“就被转义为”%2B”.
urldecode就是urlencode的逆过程.
urlencode工具!!!
二、http协议格式
任何协议的request or response:
报头+有效载荷
那么http如何保证自己的报头和有效载荷被全部读取呢?----无论是请求还是响应.
- 读取完整报头:按行读取,直到读取到空行!
- 你又如何保证,你能读取到完整的正文呢?(报头能读取完毕,请求或者响应属性中,"一定"要包含正文的长度!)
server.hpp
#include<iostream>
#include<string>
#include<cassert>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<sys/socket.h>
#include<string.h>
#include<arpa/inet.h>
#include<fstream>
using namespace std;#define CRLF "\r\n"
#define SPACE " "
#define SPACE_LEN strlen(SPACE)
#define HOME_PAGE "index.html" //首页字段
#define ROOT_PATH "wwwroot"string GetPath(string httpRequest)
{size_t pos = httpRequest.find(CRLF);if(pos == string::npos) return "";string requestLine = httpRequest.substr(0,pos);//GET /a/b/index.html http/1.0size_t first = requestLine.find(SPACE);if(first == string::npos) return "";size_t second = requestLine.rfind(SPACE);if(second == string::npos) return "";string path = requestLine.substr(first+SPACE_LEN,second-(first+SPACE_LEN));if(path.size()==1 && path[0]=='/') path+=HOME_PAGE;return path;}
string readFile(const string& recource)
{// int fd = open(recource.c_str(),O_RDONLY|O_APPEND,0666);ifstream in(recource);if(!in.is_open()) return "404";string content;string line;while(getline(in,line)){content += line;}in.close();return content;}void handlerHttpRequest(int sock)
{char buffer[10240];ssize_t s = read(sock,buffer,sizeof buffer);if(s>0){cout<<buffer<<endl;}string path = GetPath(buffer);cout<<path<<endl;// path = "/a/b/index.html";// recource = "./wwroot"; //我们的web根目录// recource += path; // ./wwwroot/a/b/index.html// 1.文件在哪里? 在请求的请求行中,第二个字段// 2.如何读取?string recource = ROOT_PATH;recource += path;cout<<recource<<endl;string html = readFile(recource);size_t pos = recource.rfind('.');string suffix = recource.substr(pos);//开始响应string response;response = "HTTP/1.0 200 OK\r\n";if(suffix == ".jpg")response += "Content-Type: image/jpeg\r\n";elseresponse += "Content-Type: text/html\r\n";response += ("Content-Length: "+to_string(html.size()) + "\r\n");response += "\r\n";response += html;send(sock,response.c_str(),response.size(),0);}class ServerTcp
{
public:ServerTcp(uint16_t port, string ip = ""): _listenSock(-1), _port(port), _ip(ip){}~ServerTcp(){}public:void init(){// 1.创建socket_listenSock = socket(AF_INET, SOCK_STREAM, 0);if (_listenSock < 0){exit(1);}// 2.bind绑定// 2.1填充服务器struct sockaddr_in local; // 用户栈memset(&local, 0, sizeof local);local.sin_family = AF_INET;local.sin_port = htons(_port);_ip.empty() ? (local.sin_addr.s_addr = INADDR_ANY) : (inet_aton(_ip.c_str(), &local.sin_addr));// 2.2本地socket信息,写入_sock对应的内核区域if (bind(_listenSock, (const sockaddr *)&local, sizeof local) < 0){exit(2);}// 3.监听socket,为何要监听呢?tcp是面向连接的!if (listen(_listenSock, 5 /*后面再说*/) < 0){exit(3);}// 允许别人来连接你了}void loop(){while (true){struct sockaddr_in peer;socklen_t len = sizeof(peer);// 4.获取连接,accept的返回值是一个新的socket fd??// 4.1 _listenScok:监听&&获取新的连接--->sock// 4.2 serviceSock:给用户提供新的socket服务int serviceSock = accept(_listenSock, (struct sockaddr *)&peer, &len);if (serviceSock < 0){continue;}// //5.1 V1.1---多进程版本//爷爷进程pid_t id=fork();assert(id != -1);if(id==0){//爸爸进程close(_listenSock);//又进行了一次forkif(fork()>0) exit(0);//孙子进程--就没有爸爸进程了--孤儿进程--被系统领养了--回收问题就交给了系统来回收handlerHttpRequest(serviceSock);exit(0);}close(serviceSock);//爸爸进程直接终止,立马得到退出码,释放僵尸进程状态pid_t ret=waitpid(id,nullptr,0);//就用阻塞式等待(void)ret;}}private:int _listenSock;uint16_t _port;string _ip;
};
serverTcp.cc
#include"server.hpp"
static void Usage(string proc)
{cerr << "Usage\n\t" << proc << " port ip" << endl;cerr << "Example\n\t" << proc << " 8080 127.0.0.1\n"<< endl;
}// ./serverTcp local_port [local_ip]
int main(int argc, char *argv[])
{if (argc != 2 && argc != 3){Usage(argv[0]);exit(4);}uint16_t port = stoi(argv[1]);string ip;if (argc == 3){ip = argv[2];}ServerTcp svr(port, ip);svr.init();svr.loop();return 0;
}
备注:此处我使用了8080端口号启动了HTTP服务器,虽然HTTP服务器一般使用80端口!
但这只是一个通用的习惯.并不是说HTTP服务器就不能使用其他的端口号.
以上就是一个表单!
我们的网络行为无非有两种:
- 我想把远端的资源拿到你的本地:GET /index.html htpp/1.1
- 我们想把我们的属性字段提交给远端. GET or POST
用抓包软件进行抓包!
三、http的请求方法
方法 | 说明 | 支持的HTTP协议版本 |
---|---|---|
GET | 获取资源 | 1.0、1.1 |
POST | 传输实体主体 | 1.0、1.1 |
PUT | 传输文件 | 1.0、1.1 |
HEAD | 获得报文首部 | 1.0、1. |
DELETE | 删除文件 | 1.0、1.1 |
OPTIONS | 询问支持的方法 | 1.1 |
TRACE | 追踪路径 | 1.1 |
CONNECT | 要求用隧道协议连接代理 | 1.1 |
LINK | 建立和资源之间的联系 | 1.0、1.1 |
UNLINE | 断开连接关系 | 1.0、1.1 |
其中最常用的就是GET方法和POST方法!
GET和POST的区别
区别:
- GET通过url传参
- POST通过正文传参
- GET方法传参不私密
- POST方法通过正文传参,所以相对私密一些.
- GET通过url传参,POST通过正文传参,所以一般一些比较大的内容都是通过POST方式传参的!
三、HTTP的状态码
类别 | 原因短语 | |
---|---|---|
1XX | Informational(信息状态码) | 接受的请求正在处理 |
2XX | Success(成功状态码) | 请求正常处理完毕 |
3XX | Redirection(重定向状态码) | 需要进行附加操作以完成请求 |
4XX | Client Error(客服端错误状态码) | 服务器无法处理请求 |
5XX | Server Error(服务器错误状态码) | 服务器处理请求出错 |
最常见的状态码,比如200(OK),404(Not Found),403(Forbidden),302(Redirect,重定向),504(Bad Gateway).
重定向
301:永久重定向
302:临时重定向
四、HTTP常见的Header
- Content-Type:数据类型(text/html等)
- Content-Length:body的长度
- Host:客户端告知服务器,所请求的资源是在那个主机的那个端口上面;
- User-Agent:声明用户的操作系统和浏览器版本信息;
- referer:当前页面是从哪个页面跳转过来的;
- Location:搭配3xx状态码使用,告诉客户端接下来要去哪里访问;
- cookie:用于在客户端存储少量信息.通常用于实现会话(session)的功能;
cookie
http协议特点之一:无状态
举个栗子:比如你刚才访问一个网页之后,http是不会帮你做记录的,也就是说http不知道你之前访问的是那个网页.
那么用户需要一个功能:会话保持.
一旦登陆,会有各种会话保持的策略.
使用B站举个栗子:
删除这些字段以后
刷新一下页面
这个网页就不认识这个用户了!
cookie:存储用户名&&密码
以后发起http请求访问该网站就会自动携带cookie文件中的内容.
cookie:浏览器维护的文件.真正的存在磁盘&&内存级文件.
注意: cookie的安全问题,比如中间人可能通过某种手段(植入病毒等)获取到了我们的cookie文件中的用户名和密码,获取到我们的信息,那么就可以以我们的身份访问网站了???
接下来引入session字段,解决这个问题!
session
Connection
Connection:keep-alive 长连接
用户所看到的的完整的页面内容–背后可能是无数次http请求
http底层主流采用的就是tcp协议!
一个tcp链接会有多个http请求!(根据请求报头中的Content-Length字段读取多个请求!)
http协议是无连接的!
总结
(本章完!)