TCP与UDP
TCP与UDP是基于IP协议的.
TCP与UDP为应用进程之间提供了逻辑通信. IP协议则为主机之间提供了逻辑通信.
IP协议的服务模型是尽力而为交付服务(best-effort delivery service).这就意味着IP尽力在通信的主机之间交付报文段,却不做任何担保. 它不保证报文段的交付,不保证报文段的按序交付,更不能保证报文段中数据的完整性.因此,IP协议被称为不可靠服务.
说到TCP与UDP,有几个特别重要的名词就不得不提了: 套接字(Socket), 运输层的多路复用(transport-layer multiplexing)与多路分解(demultiplexing).
套接字(Socket)
作为操作系统的一套应用程序编程接口.(即进程用来网络通信,由操作系统提供的编程接口).应用程序可以通过套接字, 进行消息发送与接收.
运输层的多路复用
从不同进程中收集数据块(进程通过Socket发送的数据),并为每个数据块封装上首部信息从而生成报文段,然后将报文段传递到网络层的工作称为多路复用.
多路分解
将运输层报文段中的数据交付到正确的套接字(通过唯一标识: IP+端口号识别)的工作称为多路分解.
UDP
轻量级传输协议. 只提供了基础的差错检测和多路复用/多路分解功能.实际上,如果应用程序开发人员选择UDP,则应用程序几乎就直接与IP在打交道.
UDP是无连接的.
UDP的优势:
应用层能更好的控制要发送的数据和发送时间.
无需连接建立.
无连接状态.
分组首部开销小.
UDP报文格式:
TCP
TCP是可靠的运输协议. 它能确保一个进程从其接收缓存中读出非损坏的,无间隔的,无冗余的和按序的数据流.
TCP是面向连接的. 两个应用进程在发送数据之前, 必须进行相互"握手"("三次握手", 即它们必须相互发送某些预备报文段, 以建立确保数据传输所需的参数.)
TCP是全双工服务(full-duplex service). 如果一台主机的进程A与另一台主机的进程B存在一条TCP连接, 那么应用层数据就能从进程B流向进程A的同时, 也从进程A流向进程B. TCP连接也总是点对点(point-to-point)的.
TCP报文结构.
- 源端口号与目标端口号就不用多说了, 和UDP一样, 用于多路复用/多路分解.
- 序号 SEQ标识所传字节的序号. 用于保证数据的有序性. (与确认号都是可靠传输的重要角色)
- 确认号 ACK, 用于告诉接收方已确认的数据序号.(讲白点,就是一句通知: "东西我已经收到了, 我希望你下次送我ACK序号的东西" )
- 首部长度 由于TCP选项的原因, TCP首部长度是可变的.(一般为空, 所以一般TCP首部为20字节)
- 6比特的标志字段
ACK用于指示确认字段中的值时有效的.
RST, SYN和FIN比特用于连接的建立和拆除.
PSH 表示接收放应立即将数据交给上层
URG 指存在紧急数据. 与后面的紧急数据指针字段
- 检验和 同UDP.
TCP连接的建立.
- 第一步: 客户机端TCP首先向服务器发送一个特殊的TCP报文段. 该报文SYN标识会被置为1. 且会生成一个起始序号client_isn, 将其放入报文的序号字段中. (client_isn的生成是有一定的学问的)
- 第二步: 当SYN报文到达服务端时, 服务器会从该数据报中提取出TCP SYN报文段, 为该TCP连接分配TCP缓存和变量. 并向客户机TCP发送允许连接的报文段.
- 这个允许连接的报文段不包含应用层数据. 但SYN比特会被置为1, 且确认号被置为client_isn + 1. 最后服务器会生成自己的初始序号(server_isn), 并将其放置到TCP报文段首部的序号字段中. 这个连接报文段被称为 SYNACK报文段
- 第三步: 在收到SYNACK报文段后, 客户机也要给该连接分配缓存和变量.客户端主机还会想服务器发送另外一个报文段, 这个报文段对服务器的允许连接的报文段进行了确认.
- (客户机将值server_isn+1放置到TCP报文首部的确认字段来完成此项工作). 因为连接已建立, 所以该SYN标志被置为0. (这个报文可以携带数据)
TCP可靠数据的传递机制.
TCP利用几个特别的技术实现了可靠传输. 检验和, 重传, 定时器, 滑动窗口, 确认序号.
/** 发送方 **/
sendBase = client_isn,
nextSeqNum = client_isn,
MSS = 1460;
events = {
"sendData": "应用程序发送数据",
"timeout": "定时器超时",
"acceptAck": "接收ack",
};
while (event.incoming()) {
if (event == events.sendData) {
// 不考虑流量控制的情况下.
if ()
}
if (event == events.timeout) {
}
if (event == events.acceptAck) {}
}