TCP/IP 协议族
TCP/IP 协议不是指 TCP 和 IP 两种协议,而是指利用 IP 进行通信时所需要用到的协议群的统称。
OSI中的层 | 功能 | TCP/IP协议族 |
应用层 | 文件传输,电子邮件,文件服务,虚拟终端 | TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet |
表示层 | 数据格式化,代码转换,数据加密 | 没有协议 |
会话层 | 解除或建立与别的接点的联系 | 没有协议 |
传输层 | 提供端对端的接口 | TCP,UDP |
网络层 | 为数据包选择路由 | IP,ICMP,RIP,OSPF,BGP,IGMP |
数据链路层 | 传输有地址的帧以及错误检测功能 | SLIP,CSLIP,PPP,ARP,RARP,MTU |
物理层 | 以二进制数据形式在物理媒体上传输数据 | ISO2110,IEEE802。IEEE802.2 |
下面的协议都属于TCP/IP协议族:
- TCP(Transport Control Protocol)传输控制协议
- IP(Internetworking Protocol)网间网协议
- UDP(User Datagram Protocol)用户数据报协议
- ICMP(Internet Control Message Protocol)互联网控制信息协议
- SMTP(Simple Mail Transfer Protocol)简单邮件传输协议
- SNMP(Simple Network manage Protocol)简单网络管理协议
- FTP(File Transfer Protocol)文件传输协议
- ARP(Address Resolation Protocol)地址解析协议
其中物理层、数据链路层、网络层、传输层都是由系统提供的能力,我们无法进行处理,也不用太去关心,绝大部分情况下我们都是在应用层对TCP或者UDP进行编程处理。
TCP 编程
对TCP编程我们一般使用Socket,Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。Socket 包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。
应用层通过传输层进行数据通信时,TCP会遇到同时为多个应用程序进程提供并发服务的问题。多个TCP连接或多个应用程序进程可能需要通过同一个TCP协议端口传输数据。为了区别不同的应用程序进程和连接,通过Socket可以区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务。
Socket 提供以下接口:
socket
: 创建socket。bind
: 绑定socket到本地地址和端口,通常由服务端调用。listen
: 开启监听模式监听端口。accept
: 服务器等待客户端连接,一般是阻塞态。connect
: 客户端主动连接服务器。send
: 发送数据。recv
: 接受数据。closesocket
: 关闭socket。
可以简单理解为通过Socket编程通过TCP协议和另一个IP主机进行数据的交互,在TCP上传输的数据都是明文的。
三次握手和四次挥手在 Socket 之下,不属于 Socket 的内容。
HTTP 协议
HTTP 协议是在TCP 协议之上建立的,客户端和服务端在通过TCP协议建立连接后,通过明文传输HTTP报文,传输结束后断开TCP 。
HTTP 报文在TCP上是明文传输的,在TCP层可以拦截HTTP请求的报文,任何一个路由器节点可以查看、修改、拦截HTTP请求。
HTTP 协议的请求报文和响应报文的结构基本相同,由四部分组成:
- 请求行(request line):由请求方法、URL(包含Query参数)和HTTP协议版本组成。
- 头部(header):由多个key-value值组成。
- 空行:请求报文使用空行将请求头部和请求数据分隔。
- 实体:POST、PUT 等方法可以携带 Body。
请求行
请求报文里的起始行也就是请求行(request line),它简要地描述了客户端想要如何操作服务器端的资源。
请求行由三部分构成:
- 请求方法:是一个动词,如 GET/POST,表示对资源的操作;
- 请求目标:通常是一个 URI,标记了请求方法要操作的资源;
- 版本号:表示报文使用的 HTTP 协议版本。
这三个部分通常使用空格(space)来分隔,最后要用 CRLF 换行表示结束。
POST /search?q=http HTTP/1.1
状态行
响应报文里的起始行,在这里它不叫“响应行”,而是叫“状态行”(status line),意思是服务器响应的状态。
比起请求行来说,状态行要简单一些,同样也是由三部分构成:
- 版本号:表示报文使用的 HTTP 协议版本;
- 状态码:一个三位数,用代码的形式表示处理的结果,比如 200 是成功,500 是服务器错误;
- 原因:作为数字状态码补充,是更详细的解释文字,帮助人理解原因。
HTTP/1.1 200 OK
头部字段
请求行或状态行再加上头部字段集合就构成了 HTTP 报文里完整的请求头或响应头:
请求头和响应头的结构是基本一样的,唯一的区别是起始行。
头部字段是 key-value 的形式,key 和 value 之间用“:”分隔,最后用 CRLF 换行表示字段结束。比如在“Host: 127.0.0.1”这一行里 key 就是“Host”,value 就是“127.0.0.1”。
HTTP 头字段非常灵活,不仅可以使用标准里的 Host、Connection 等已有头,也可以任意添加自定义头,这就给 HTTP 协议带来了无限的扩展可能。
不过使用头字段需要注意下面几点:
- 字段名不区分大小写,例如“Host”也可以写成“host”,但首字母大写的可读性更好;
- 字段名里不允许出现空格,可以使用连字符“-”,但不能使用下划线“_”。例如,“test-name”是合法的字段名,而“test name”“test_name”是不正确的字段名;
- 字段名后面必须紧接着“:”,不能有空格,而“:”后的字段值前可以有多个空格;
- 字段的顺序是没有意义的,可以任意排列不影响语义;
- 字段原则上不能重复,除非这个字段本身的语义允许,例如 Set-Cookie。
实体
实体在请求里是POST、PUT等请求body里携带的内容,大部分情况下是 JSON,也可以是 Protobuf 等进行编码过的内容。
在响应里就是响应的实际内容,例如GET请求返回的HTML页面,或者JSON数据等等。
HTTPS
上面可以看到 HTTP 报文就是一个文本协议,可以看成一个字符串,这个字符串在 TCP 上是进行明文传输的,非常的不安全,所以为了数据安全传输,需要对这个字符串进行加密传输,这个加密的方法就是 HTTPS。
在请求发送时,还是正常的HTTP报文,然后通过 TLS协商密钥进行加密,传输到服务器上时再通过 协商密钥 进行解密。响应数据一样的加密解密。
具体HTTPS的过程可以看我的博客:HTTPS小故事
完整过程大致如下
- 客户端和服务器通过TCP三次握手建立连接。
- 客户端和服务器通过Socket发送和接收数据进行TLS握手生成协商密钥。
- 客户端使用协商密钥对HTTP请求报文进行加密。
- Socket通过TCP把加密后的HTTP请求报文发送到服务器。
- 服务器通过Socket获取加密后的HTTP请求报文。
- 服务器解密数据获取HTTP请求报文,进行解析。
- 服务器处理HTTP请求报文,生成一个HTTP响应报文。
- 服务器使用协商密钥对HTTP响应报文进行加密。
- 服务器使用Socket通过协商密钥把加密后的HTTP响应报文发送到客户端。
- 客户端收到加密数据进行解密,获取HTTP响应报文。
- 客户端和服务器通过TCP四次挥手取消连接。