网络编程
上过了计算机网络的课程,总结一下学过的知识。
网络分层
计算机网络体系结构一般采用五层协议。
1.物理层 2.数据链路层 3.网络层 4.传输层 5.应用层
分层 | 说明 |
---|---|
应用层(HTTP、FTP、DNS、SMTP 等) | 定义了如何包装和解析数据,应用层是 http 协议的话,则会按照协议规定包装数据,如按照请求行、请求头、请求体包装,包装好数据后将数据传至运输层 |
运输层(TCP、UDP 等) | 运输层有 TCP 和 UDP 两种,分别对应可靠和不可靠的运输。在这一层,一般都是和 Socket 打交道,Socket 是一组封装的编程调用接口,通过它,我们就能操作 TCP、UDP 进行连接的建立等。这一层指定了把数据送到对应的端口号 |
网络层(IP 等) | 这一层IP协议,以及一些路由选择协议等等,所以这一层的指定了数据要传输到哪个IP地址。中间涉及到一些最优线路,路由选择算法等 |
数据链路层(ARP) | 负责把 IP 地址解析为 MAC 地址,即硬件地址,这样就找到了对应的唯一的机器 |
物理层 | 提供二进制流传输服务,也就是真正开始通过传输介质(有线、无线)开始进行数据的传输 |
TCP/IP
IP(Internet Protocol)协议提供了主机和主机间的通信,为了完成不同主机的通信,我们需要某种方式来唯一标识一台主机,这个标识,就是著名的 IP 地址。通过IP地址,IP 协议就能够帮我们把一个数据包发送给对方。
TCP 的全称是 Transmission Control Protocol,TCP 协议在 IP 协议提供的主机间通信功能的基础上,完成这两个主机上进程对进程的通信。
三次握手
所谓三次握手(Three-way Handshake),是指建立一个 TCP 连接时,需要客户端和服务器总共发送3个包。
三次握手的目的是连接服务器指定端口,建立 TCP 连接,并同步连接双方的序列号和确认号,交换 TCP 窗口大小信息。在 socket 编程中,客户端执行 connect() 时。将触发三次握手。
- 第一次握手(SYN=1, seq=x):
客户端发送一个 TCP 的 SYN 标志位置 1 的包,指明客户端打算连接的服务器的端口,以及初始序号 X,保存在包头的序列号 (Sequence Number) 字段里。发送完毕后,客户端进入 SYN_SEND
状态。
- 第二次握手(SYN=1, ACK=1, seq=y, ACKnum=x+1):
服务器发回确认包(ACK)应答。即 SYN 标志位和 ACK 标志位均为 1。服务器端选择自己 ISN 序列号,放到 Seq 域里,同时将确认序号(Acknowledgement Number)设置为客户的 ISN 加1,即 X+1。 发送完毕后,服务器端进入 SYN_RCVD
状态。
- 第三次握手(ACK=1,ACKnum=y+1)
客户端再次发送确认包(ACK),SYN 标志位为 0,ACK 标志位为 1,并且把服务器发来 ACK 的序号字段 +1,放在确定字段中发送给对方,并且在数据段放写 ISN 的 +1
发送完毕后,客户端进入 ESTABLISHED 状态,当服务器端接收到这个包时,也进入 ESTABLISHED 状态,TCP 握手结束。
四次挥手
TCP 的连接的拆除需要发送四个包,因此称为四次挥手(Four-way handshake),也叫做改进的三次握手。客户端或服务器均可主动发起挥手动作,在 socket 编程中,任何一方执行 close() 操作即可产生挥手操作。
- 第一次挥手(FIN=1,seq=x)
假设客户端想要关闭连接,客户端发送一个 FIN 标志位置为1的包,表示自己已经没有数据可以发送了,但是仍然可以接受数据。
发送完毕后,客户端进入 FIN_WAIT_1 状态。
- 第二次挥手(ACK=1,ACKnum=x+1)
服务器端确认客户端的 FIN 包,发送一个确认包,表明自己接受到了客户端关闭连接的请求,但还没有准备好关闭连接。
发送完毕后,服务器端进入 CLOSE_WAIT 状态,客户端接收到这个确认包之后,进入 FIN_WAIT_2 状态,等待服务器端关闭连接。
- 第三次挥手(FIN=1,seq=y)
服务器端准备好关闭连接时,向客户端发送结束连接请求,FIN 置为1。
发送完毕后,服务器端进入 LAST_ACK 状态,等待来自客户端的最后一个ACK。
- 第四次挥手(ACK=1,ACKnum=y+1)
客户端接收到来自服务器端的关闭请求,发送一个确认包,并进入 TIME_WAIT状态,等待可能出现的要求重传的 ACK 包。
服务器端接收到这个确认包之后,关闭连接,进入 CLOSED 状态。
客户端等待了某个固定时间(两个最大段生命周期,2MSL,2 Maximum Segment Lifetime)之后,没有收到服务器端的 ACK ,认为服务器端已经正常关闭连接,于是自己也关闭连接,进入 CLOSED 状态。
摘自《计算机网络》(第7版)。
TCP 与 UDP 的区别
| 区别点 | TCP | UDP | | ——– | ——– | —— | | 连接性 | 面向连接 | 无连接 | | 可靠性 | 可靠 | 不可靠| | 有序性 | 有序 | 无序 | | 面向 | 字节流 | 报文(保留报文的边界) | | 有界性 | 有界 | 无界 | | 流量控制 | 有(滑动窗口) | 无 | | 拥塞控制 | 有(慢开始、拥塞避免、快重传、快恢复) | 无 | | 传输速度 | 慢 | 快 | | 量级 | 重量级 | 轻量级 | | 双工性 | 全双工 | 一对一、一对多、多对一、多对多 | | 头部 | 大(20-60 字节) | 小(8 字节) | | 应用 | 文件传输、邮件传输、浏览器等 | 即时通讯、视频通话等 |
Socket
Socket 是一组操作 TCP/UDP 的 API,像 HttpURLConnection 和 Okhttp 这种涉及到比较底层的网络请求发送的,最终当然也都是通过 Socket 来进行网络请求连接发送,而像 Volley、Retrofit 则是更上层的封装。
使用示例
使用 socket 的步骤如下:
- 创建 ServerSocket 并监听客户连接;
- 使用 Socket 连接服务端;
- 通过 Socket.getInputStream()/getOutputStream() 获取输入输出流进行通信。
public class EchoClient {
private final Socket mSocket;
public EchoClient(String host, int port) throws IOException {
// 创建 socket 并连接服务器
mSocket = new Socket(host, port);
}
public void run() {
// 和服务端进行通信
Thread readerThread = new Thread(this::readResponse);
readerThread.start();
OutputStream out = mSocket.getOutputStream();
byte[] buffer = new byte[1024];
int n;
while ((n = System.in.read(buffer)) > 0) {
out.write(buffer, 0, n);
}
}
private void readResponse() {
try {
InputStream in = mSocket.getInputStream();
byte[] buffer = new byte[1024];
int n;
while ((n = in.read(buffer)) > 0) {
System.out.write(buffer, 0, n);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] argv) {
try {
// 由于服务端运行在同一主机,这里我们使用 localhost
EchoClient client = new EchoClient("localhost", 9877);
client.run();
} catch (IOException e) {
e.printStackTrace();
}
}
}
HTTP协议
HTTP简介
超文本传输协议已经演化出了很多版本,它们中的大部分都是向下兼容的。在 RFC 2145 中描述了HTTP版本号的用法。客户端在请求的开始告诉服务器它采用的协议版本号,而后者则在响应中采用相同或者更早的协议版本。
HTTP/0.9 已过时。只接受GET一种请求方法,没有在通讯中指定版本号,且不支持请求头。由于该版本不支持POST方法,因此客户端无法向服务器传递太多信息。
HTTP/1.0 这是第一个在通讯中指定版本号的HTTP协议版本,至今仍被采用,特别是在代理服务器中。
HTTP/1.1 持久连接被默认采用,并能很好地配合代理服务器工作。还支持以管道方式在同时发送多个请求,以便降低线路负载,提高传输速度。
HTTP/1.1相较于HTTP/1.0协议的区别主要体现在:
- 缓存处理
- 带宽优化及网络连接的使用
- 错误通知的管理
- 消息在网络中的发送
- 互联网地址的维护
- 安全性及完整性
HTTP/2 当前版本,于2015年5月作为互联网标准正式发布。Http2.0 相对于 Http1.x 来说提升是巨大的,主要有以下几点:
- 二进制格式:http1.x 是文本协议,而 http2.0 是二进制以帧为基本单位,是一个二进制协议,一帧中除了包含数据外同时还包含该帧的标识:Stream Identifier,即标识了该帧属于哪个 request,使得网络传输变得十分灵活。
- 多路复用:多个请求共用一个TCP连接,多个请求可以同时在这个 TCP 连接上并发,一个是解决了建立多个 TCP 连接的消耗问题,一个也解决了效率的问题。
- header 压缩:主要是通过压缩 header 来减少请求的大小,减少流量消耗,提高效率。
- 支持服务端推送
HTTP协议的主要特点
- 支持C/S(客户/服务器)模式。
- 简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST,每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
- 灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。
- 无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
- 无状态:HTTP协议是无状态协议,无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
HTTP URL的格式:
http://host[":"port][abs_path]
http表示要通过HTTP协议来定位网络资源;host表示合法的Internet主机域名或者IP地址;port指定一个端口号,为空则使用默认端口80;abs_path指定请求资源的URI(Web上任意的可用资源)。
请求报文
<method> <request-URL> <version>
<headers>
<entity-body>
Method表示请求方法;Request-URI是一个统一资源标识符;HTTP-Version表示请求的HTTP协议版本;HTTP请求方法有8种,分别是GET、POST、DELETE、PUT、HEAD、TRACE、CONNECT 、OPTIONS。其中PUT、DELETE、POST、GET分别对应着增删改查,最常用的就是POST和GET了。
- GET:请求获取Request-URI所标识的资源
- POST:在Request-URI所标识的资源后附加新的数据
- HEAD:请求获取由Request-URI所标识的资源的响应消息报头
- PUT: 请求服务器存储一个资源,并用Request-URI作为其标识
- DELETE :请求服务器删除Request-URI所标识的资源
- TRACE : 请求服务器回送收到的请求信息,主要用于测试或诊断
- CONNECT: HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。
- OPTIONS :请求查询服务器的性能,或者查询与资源相关的选项和需求
例:访问 GET https://wushiqian.github.io/ HTTP/1.1