深入理解计算机网络之应用层详解
首先我们先来回顾一下OSI七层模型:
这里我们只介绍网络通信中重要的四层:
分层名称 | 功能 | 常用协议 |
---|---|---|
应用层 | 针对特定应用的协议 | FTP(文件传输有协议),DNS(域名系统)以及常见的 HTTP协议 |
传输层 | 管理两个节点之间的数据传输。负责可靠传输(确保数据被可靠的传送到目标地址) | TCP(传输控制协议)和UDP(用户数据报协议) |
网络层 | 地址管理和路由选择(通过路由器寻址) | IP协议 |
数据链路层 | 互联设备之间的传送和识别数据帧 | 一般是一些硬件:网卡光纤等可见设备 |
应用层详解
HTTP协议即超文本传输协议。
说到HTTP协议,我们就离不开URL(统一资源定位符),没有它,HTTP是不完整的。
什么是URL?
https://www.google.com/search?q=URL&oq=URL&aqs=chrome..69i57j0l5.1240j0j8&sourceid=chrome&ie=UTF-8
这就是一个URL,其实通俗来讲,它就是我们所说的网址。
一个完整的URL包括:
目录 | 解释 |
---|---|
协议名 | http 或者 https |
登录认证信息 | 可选,一般不可见 |
服务器地址 | www.google.com |
服务器端口号 | http是80 https是 443(比http更加保密的协议) ssh是 22 |
带层次的文件路径 | 这里没显示出来,一般是一个相对路径比如:/dir/index.html |
查询字符串 | 这里显示的是search?q = URL(?之前是访问的资源,后面是传输的参数,=各个参数之间用 & 分隔) |
片段标识符 | … |
HTTP协议格式
画一张图以便理解和记忆:
HTTP请求格式
格式 | 内容 |
---|---|
首行 | 方法+URL+版本号(版本号现在浏览器一般都支持 HTTP/1.1) |
Header(头部) | 请求的属性,冒号分割的键值对,每组属性之间使用 \n 分隔,遇到空行表示Header部分结束 |
Body | 空行后面都是body。Body允许为空字符串。如果Body存在,则在Header中有一个Content-Length来标记Body长度 |
HTTP请求方法
方法 | 说明 | 支持的HTTP协议版本 |
---|---|---|
GET | 获取资源 (通过URL传参,一般数量是有限的) | 1.0、1.1 |
POST | 获取传输实体的主体(通过正文传参) | 1.0、1.1 |
PUT | 传输文件 | 1.0、1.1 |
HEAD | 回去报文首部 | 1.0、1.1 |
DELETE | 删除文件 | 1.0、1.1 |
OPTIONS | 询问支持的方法 | 1.1 |
TRACE | 追踪路径 | 1.1 |
CONNECT | 邀请用隧道协议来连接代理 | 1.1 |
LINK | 建立和资源之间的关系 | 1.0 |
UNLNE | 断开连接关系 | 1.0 |
其中我们最常用的就是 GET 和POST请求。
HTTP响应
看起来HTTP响应格式跟请求很相似:
格式 | 解释 |
---|---|
首行 | 版本号+状态码+状态码解释 |
Header | 请求的属性,冒号分割的键值对,每组属性之间使用 \n 分隔,遇到空行表示Header部分结束 |
Body | 空行后面都是Body。Body允许为空字符串。如果Body存在,则在Header中有一个Content-Length属性来标识Body长度;如果服务器返回了一个html页面,那么html页面内容就在Body中。 |
HTTP状态码及解释
类型 | 类别 | 解释 |
---|---|---|
1xx | Informational(信息状态码) | 接受的请求正在处理 |
2xx | Success(成功状态码) | 请求正常处理完毕 |
3xx | Redirection(重定向状态码) | 需要进行附加操作已完成请求 |
4xx | Client Error(客户端错误状态码) | 服务器无法处理请求 |
5xx | Server Error(服务器错误状态码) | 服务器处理请求错误 |
常见状态码:
200(OK)、404(Not Found)、403(Forbidden)、302(重定向)、504(Bad Gateway)
HTTP常见Header:
类型 | 解释 |
---|---|
Content-Type | 数据类型,如:txt、html |
Content-Length | Body长度 |
Host | 客户端告诉服务器,所请求的资源是在哪个主机的哪个端口 |
User-Agent | 声明用户的操作系统和浏览器版本信息 |
referer | 当前页面是从哪个页面跳转过来的 |
location | 搭配3xx状态码使用, 告诉客户端接下来要去哪里访问 |
Cookie | 用于在客户端存储少量信息. 通常用于实现会话(session)的功能; |
简易HTTP服务器
#include <iostream>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <cstdio>
using namespace std;
int main(int argc,char* argv[])
{
if(argc != 3)
{
cerr << argv[0] << " ip port"<< endl;
exit(0);
}
int fd = socket(AF_INET,SOCK_STREAM,0);
if(fd < 0)
{
cerr<< "socket error" << endl;
exit(1);
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(argv[2]));
addr.sin_addr.s_addr = inet_addr(argv[1]);
int ret = bind(fd,(struct sockaddr*)&addr,sizeof(addr));
if(ret < 0)
{
cerr<< "bind error"<< endl;
exit(2);
}
ret = listen(fd,5);
if(ret < 0)
{
cerr<< "listen error " << endl;
exit(3);
}
while(1)
{
struct sockaddr_in client_addr;
socklen_t len;
int client_fd = accept(fd, (struct sockaddr*)&client_addr, &len);
if (client_fd < 0) {
perror("accept");
continue;
}
char input_buf[1024 * 10] = {0};
ssize_t read_size = read(client_fd, input_buf, sizeof(input_buf) - 1);
if (read_size < 0) {
return 1;
}
cout<< "[Request] "<< input_buf << endl;
char buf[1024] = {0};
const char* hello = "<h1>hello world</h1>";
sprintf(buf, "HTTP/1.0 200 OK\nContent-Length:%lu\n\n%s", strlen(hello), hello);
write(client_fd, buf, strlen(buf));
}
return 0;
}
说完状态码,我们再来谈谈端口号:
端口号: 就是 用来标识一台主机上进行通信的不同的应用程序
在TCP/IP中,用一个五元组来标识一个通信:
五元组
五元组: 即源IP、源端口号、目的IP、目的端口号、协议号
查看指令:nenstat -n
端口号的划分范围:
- 0 - 1023:表示知名端口号,常见的HTTP、HTTPS、SSH、FTP都属于知名端口号,都是固定的。
- 1024 - 65535 :操作系统动态分配的端口号。客户端程序的端口号就是由操作系统动态分配出来的。
知名端口号: cat /etc/services 可以查看知名端口号
- ssh服务器, 使用22端口
- ftp服务器, 使用21端口
- telnet服务器, 使用23端口
- http服务器, 使用80端口
- https服务器, 使用443
那么问题来了(面试题):
- 一个进程是否可以bind多个端口号?
答:可以,一个进程可以有多个socket,每个socket可以bind一个端口号。 - 一个端口号是否可以被多个进程bind?
答:不可以。前面我们说过端口号是用来标识一台主机上不同的应用程序,一个应用程序就是一个进程,如果可以的话,那么这个端口号标识的是哪一个进程呢。
但是, 非要这样做也不是没有办法,可以 在一个进程bind一个端口号之后 fork出一个子进程,该子进程也可以拥有此 端口号。
最后介绍两个查看网络状态的指令:
-
netstat 用来产看网络状态的重要工具
语法: nestat 【选项】
功能: 产看网络状态
常用选项:- 拒绝显示别名,能显示数字的全部转化成数字
- l 仅列出有在 Listen (监听) 的服務状态
- p 显示建立相关链接的程序名
- t (tcp)仅显示tcp相关选项
- u (udp)仅显示udp相关选项
- a (all)显示所有选项,默认不显示LISTEN相关
查看TCP: netstat -alpt
查看UDP: netstat -alpu
-
pidof 查看服务器进程id
功能:通过进程名,查看进程id
了解传输层请移步:传输层详解(TCP和UDP)