1.网络编程之 TCP 协议
ServerSocket socket 给服务器去使用
Socket 通信线路的 socket
InputStream is = getInputStream() OutputStream os = getOutputStream() 获取输入输出
TCP:面向字节流
不做[abc][defg][hij] 的保证,数据可能被拼接和拆分的
可能[a][bcdef][ghij]
可能[abcdefghij]
可能[abc][defg][hij] 缓缓再发送
传输层TCP 传输控制协议
职责:进程 to 进程 保证可靠性
可靠性:
- TCP 尽它最大可能,将数据发送给对方
- 即使数据没有发送给对方,TCP 至少给上层(进程)一个交代
- 保证接收方接收到的数据是有序的
- TCP可以保证不会收到错误的数据
- 根据接受能力 + 网络承载情况做了发送量控制了
2.TCP如何实现它的两个职责:
2.1 TCP实现进程到进程 解包、分用
2.2TCP是如何实现可靠性的
方法:确认(acknowledge)应答
2.2.1TCP的编号 发送端
TCP给所有的数据编号,这样就知道哪些数据收到了,哪些没收到
1)给应用层做编号 2)每个字节占用一个序号
【a b c d】
1 2 3 4
TCP 需要在发送的段(segment)携带编号信息
填的是本次发送的数据中,第一个字节的编号。
a b c d e f g h i j k... 假设 a 从编号 1 开始
[ a b c ] 序号 = 1;[ d e f g ] 序号 = 4;[ h i j k ] 序号 = 8
序号(Sequence Number) SN
SN + len 所有数据的编号
2.2.2 确认应答
通过序号表示哪些收到了 确认序号 ASN
ASN 填充的是:下一次期望收到的第一个数据的编号,换言之,ASN 之前的所有数据已经收到
A -> B: [ a b c d ] SN = 8 len = 4 B -> A: ASN = 12 代表 8-11 收到
A -> B:[e f g] SN = 12 len = 3 B -> A: ASN = 15 代表8 - 14 收到
我们不能有上帝视角
发送端(TCP):发送了数据之后,过了很久,没有收到应答,发生了什么?
1.对方完全没有收到数据:
①数据丢了②只是因为时间的原因,数据还没有到达对方
2.对方收到过数据,只是应答我们还没有收到:
①应答丢了 ②应答因为时间原因,暂时没有到达
这时我们采用一种方法:超时(timeout)重发
2.2.3 超时重发:
主机B第一次收到这个数据,进行应答。
主机B会收到重复的数据。主机 B 可以成功识别重复的数据,这些数据都有编号,所以可以识别。识别出来直接丢弃重复数据。再次应答即可
确认应答机制(数据编号机制 + 超时重传机制)
如果多次重发,仍然没有收到应答,在应该怎么办?
认为线路出现重大问题
1.关闭连接
2.通知进程,发送失败。在java中,使用异常通知
3.发送一个 reset
2.2.4 超时时间的选定
作为发送方,进程从开始发送数据,到发现线路出现重大故障,大概要多久:1+2+4+8 s才知道网络出问题了
作为接收方(进程),能否知道线程故障了? 不能。
2.2.5 确认应答机制(数据编号机制 + 超时重传机制)
保证了可靠性:
- TCP 尽它最大可能,将数据发送给对方
- 即使数据没有发送给对方,TCP 至少给上层(进程)一个交代
- 保证接收方接收到的数据是有序的
- TCP可以保证不会收到错误的数据
3.TCP中的链接(Connection)管理(Managment)
1.TCP 中有接收缓冲区
2.TCP中有发送缓冲区
TCP协议栈维护的数据:
针对每条线路(链接):①发送缓冲区②接收缓冲区③SN④ASN...
针对全局:Map<port,pid>
TCP 在正式发送数据之前,需要确认对方存在
TCP 一开始有一些数据需要同步:序列号 各自随机生成序号
4.TCP的三次握手(建立连接)
合并后
3.1 同步 segment
SYN = 1 代表同步
主 -> 被:SYN 不携带数据
被 -> 主:SYN + ACK 不携带数据
主 -> 被:ACK 允许携带
3.2 从序列号的角度
一开始随机生成的序列号——初始序列号ISN
5.TCP 的状态
ESTABLISHED:已建立
CLOSED:已关闭(虚拟状态)
LISTEN:被动连接方(正在听)等待连接
SYN_RCVD:收到了syn
SYN_SENT:syn已发送
6.四次挥手(断开链接)
标志位是FIN的,进行断开操作
握手称为三次握手,挥手为什么称为四次挥手?
CLOSE_WAIT:挥手阶段 / 被动 / 我收到了对方要求关闭连接的请求了,但我还不准备关闭
一般而言,对于服务器上出现大量的 CLOSE_WAIT 状态,该现象是否合理? ——不合理,可能是忘记写socket.close();
挥手是正常关闭,TCP 还支持一种异常关闭
收到一个rest segment
收到 reset segment后,TCP 直接关闭链接,不用再向对方发送任何数据。通知进程(进程收到异常)
7.TCP 异常情况
- 直接使用任务管理器关闭一个进程,请问被这个进程持有的连接会怎么办?
虽然进程不会执行关闭操作,但 OS 会执行 四次挥手的正常关闭
- 点击重启,运行着的的进程管理的连接会怎么办?
OS 会执行四次挥手的正常关闭。(点击关机,同样道理)
- 断电关闭电脑,进程持有的连接会怎么办? 什么都不会发生
我们这一侧的内存中数据没有了,所以连接从我们这一侧,认为已经不存在了(没有走任何关闭流程),连接的另一侧,不知道发生了什么。
对方如何知道连接出问题?—— 当对方有写操作时,由于多次重试失败,就会发现问题(但区分不了是对端的问题还是线路的问题)。当对方进行读操作时,永远发现不了问题。
因此需要应用层参与配合:1.应用层不要做无限制的读操作。增加读超时 2.定期给对方发消息,报平安
8.流量控制
发送方 利用 滑动窗口的机制,进行发送量的控制
1.TCP 有发送缓冲区:应用层写入,将要发送,但还未发送的数据
另外还可以通过网络承载能力来进行控制:拥塞控制
1.得知道当前网络的承载能力——拥塞窗口
2.发送量大小(发送窗口) = min(拥塞窗口,接收窗口)
3.按照滑动窗口的机制进行发送量控制
无法得到精确的拥塞窗口:只能采用一定的算法推算出当前的网络拥塞能力——网络拥塞算法