目录
应用层
HTTP协议
URL
urlencode和urldecode
HTTP协议格式
传输层
端口号
端口号划分
认识知名端口号
两个问题
UDP协议
UDP特点
UDP缓冲区
TCP协议
理解TIME_WAIT状态
解决TIME_WAIT状态引起的bind失败方法
确认应答ACK机制
超时重传机制
滑动窗口机制
流量控制
拥塞窗口(拥塞控制)
延时应答
捎带应答机制
面向字节流
TCP异常情况
TCP小结
可靠性:
提高性能:
其他
基于TCP的应用层协议
应用层协议:保证一端发送时构造的数据,在另一端能够正确的进行解析,这种约定就是应用层协议
有很多现成的,非常好用的协议,比如HTTP(超文本传输协议)
平时俗称的网址就是说的URL
像/?: 等字符,以及被当做特殊意义理解了,因此不能随意出现,因此参数中的特殊字符必须要转译
转译规则:
将需要的字符转换为16进制,从右到左,取四位,每两位做一位,前面加上%,编码成为:%XY的格式
eg:+ > %2B
http是一个明文字符串传输,分了四个部分:
首行\r\n协议头1\r\n协议头2\r\n\r\n正文
1:首行:请求方法/协议版本 + url/状态码 + 协议版本/状态码描述\r\n
2:协议头:请求的属性,都是一个一个键值对,(冒号空格)间隔的键值对,这些键值对
之间以\r\n来进行间隔
3:空行:\r\n\r\n
4:正文
请求方法 URL(请求资源调用符) 协议版本
请求方法:GET (获取资源为主,也能提交数据)
能够提交的url长度是有限的
POST(提交表单数据为主,也可以获取资源)
提交的数据是在正文中的,正文的长度是没有限制的
区别:有无正文
协议头的格式:
协议头中都是一个个以: (冒号空格)间隔的键值对,这些键值对
之间以\r\n来进行间隔
HOST: ......\r\n
Accept: ......\r\n
\r\n(空行)
正文
eg:HTTP/1.1 302 Moved Temporarily
协议版本 状态码 状态码描述
通过五元组(源ip,源端口号,目的ip,目的端口号,协议号)可以用来标识一个通信
(IP协议的协议号为4,TCP的协议号为6)
(1)0~1023:知名端口号,是留着备用的,一把都是用于协议,例如HTTP、FTP、SSH
(2)1024~65535:是操作系统动态分配的端口号,客户端程序的端口号,就是由操作糸统从这个范围来分配的,在TCP与UDP的套接字通信中,客户端的端口号就是在此范围中
自己写程序时要避开这些端口号
1:一个进程能否bind多个端口号
2:一个端口号能否被多个进程bind
如果进程先绑定一个端口号,然后在fork一个子进程,这样的话就可以是实现多个进程绑定一个端口号,但是两个不同的进程绑定同一个端口号是不可以的
两个工具
netstat
//一个用来查看网络状态的工具
t:仅显示tcp相关项
u:仅显示udp相关项
l:仅列出在监听状态的服务状态
pidof
//查看服务器的进程id(通过进程名)
UDP对数据进行原样发送,不会拆分也不会合并,因此如果数据过长,需要我们用户在应用层进行分包
UDP无法保证包序,需要我们用户在应用层进程排序(编号)
udp传输速度快,应用场景就是实时性要求高但是安全性要求不是很高(比如传输视频)
且不会出现沾包的问题
(udp数据报最大长度是64K)
基与udp的应用层协议:DHCP DNS NFS
全称为“传输控制协议”,对数据的传输进行一个详细的控制
CWR(Congestion Window Reduce):拥塞窗口减少标志被发送主机设置,用来表明它接收到了设置ECE标志的TCP包,发送端通过降低发送窗口的大小来降低发送速率
ECE(ECN Echo):ECN响应标志被用来在TCP3次握手时表明一个TCP端是具备ECN功能的,并且表明接收到的TCP包的IP头部的ECN被设置为11。更多信息请参考RFC793。
URG(Urgent):该标志位置位表示紧急(The urgent pointer) 标志有效。该标志位目前已经很少使用参考后面流量控制和窗口管理部分的介绍。
ACK(Acknowledgment):取值1代表Acknowledgment Number字段有效,这是一个确认的TCP包,取值0则不是确认包。后续文章介绍中当ACK标志位有效的时候我们称呼这个包为ACK包,使用大写的ACK称呼。
PSH(Push):该标志置位时,一般是表示发送端缓存中已经没有待发送的数据,接收端不将该数据进行队列处理,而是尽可能快将数据转由应用处理。在处理 telnet 或 rlogin 等交互模式的连接时,该标志总是置位的。
RST(Reset):用于复位相应的TCP连接。通常在发生异常或者错误的时候会触发复位TCP连接。
SYN(Synchronize):同步序列编号(Synchronize Sequence Numbers)有效。该标志仅在三次握手建立TCP连接时有效。它提示TCP连接的服务端检查序列编号,该序列编号为TCP连接初始端(一般是客户端)的初始序列编号。在这里,可以把TCP序列编号看作是一个范围从0到4,294,967,295的32位计数器。通过TCP连接交换的数据中每一个字节都经过序列编号。在TCP报头中的序列编号栏包括了TCP分段中第一个字节的序列编号。类似的后续文章介绍中当这个SYN标志位有效的时候我们称呼这个包为SYN包。
FIN(Finish):带有该标志置位的数据包用来结束一个TCP会话,但对应端口仍处于开放状态,准备接收后续数据。当FIN标志有效的时候我们称呼这个包为FIN包。
另外我们一般称数据链路层的发出去的数据包为帧(frame),
称呼网络层发给链路层的数据包为包(packet),
称呼传输层发给网络层的数据包为段(segment)。
但是正如我们描述所用,段、包、帧也经常统称为数据包或者数据报文。
对应用层来说TCP是一个双向对称的全双工(full-duplex)协议,也就是说应用层可以同时发送数据和接收数据。这就意味着数据流在一个方向上的传输是独立于另一个方向的传输的
1:TIME_WAIT状态,等待的时间是为了保护主动关闭方,一定要等待,因为如果ack没有到达,他要对人家重传的fin进行再次回复,如果没有等待,那么这时候客户端的响应就不是 ack而是rst,对端因为要等的是ack,所以会认为这是要个错误的报文,而继续等待,造成资源浪费
2:等待需要两个MSL时间是为了让网络上迷途的报文彻底消失,防止对后续造成影响
TIME_WAIT状态时主动关闭方出现的,TIME_WAIT说主动关闭方需要等待两个MSL时间(报文最大生存周期)
主动关闭方在最后一次发送ack之后,有可能这个ack会丢掉,被动方等待一段时间之后,没有收到ack会认为fin请求没有发送过去,然后重新发送一个fin到主动方,如果主动方发送ack之后直接关闭,然后又重新建立socket,后续重发的这个fin有可能就会对这个新的socket造成影响,这不是我们希望看到的,因此我们让主动方多等一会,等待多长时间合适,这里用连个MSL时间,确保ack已经发送到对方,或者就算有fin请求,在2MSL时间后也已经被丢弃,不会再传递到主动发(一来一回所以是两个MSL时间)
在socket() 和 bind()调用之间插入如下代码:
int opt = 1;
setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
//选项SO_REUSEADDR为1,表示允许创建端口号相同但是Ip地址不同的多个socket描述符
每一个ACK都带有对应的确认序列号,意思是告诉发送者,我已经收到那些数据,下一次你应该从哪里发
确认应答机制-----发送的每条数据都需要确认回复一下
主机1给主机2发送数据后,可能因为网络拥堵等原因,数据无法到达主机2
主机1在等待一个特定时间后会进行重发(如果主机1未收到2的确认答复,也会重发)
因此主机会收到很多重复数据,但是利用序列号很容易去重。
超时重查收-------发送方等待一段时间后还没有回复,就认为传输失败了,将数据进行重传
(超时时间是递增的,次数也有限制,超过了重传次数就认为网络断开了)
为了高性能通信,因此会动态计算这个最大超时时间
linux中以500ms为一个单位进行控制,每次判定都是500ms的整数倍
窗口大小指的是无需等待确认应答而可以继续发送数据的最大值,窗口越大,网络吞吐率越高
滑动窗口机制:(一次发送多少取决于tcp协议中窗口大小字段,窗口大小在进行数据传输前会进行双方协商)
如果丢包,任何进行重传?
情况1:数据到达,ACK丢了
部分ACK丢了不要紧,可以通过后续的ACK进行确认
情况2:数据包丢了
如果之前某一数据包丢了,之后的应答会一直发送请求丢失的数据包
如果发送端,连续三次收到同样一个应答,就会将数据重发
也叫高速重发控制或快重传
接收端处理数据的速度是有限的,如果发送太快就会导致接收端的缓冲区被打满
这时候继续发送就会发生丢包,继而引起一系列的连锁反应
因此TCP支持根据接收能力,来决定发送端的速度,这个机制就是流量控制
通过不断地重新设定窗口大小,来告诉对方我能一次接收到多少数据,最终达到一个流量控制的效果
避免因为发送太快,而处理太慢,导致接收方缓冲区塞满造成大量的报丢失重传
虽然TCP有滑动窗口,但是如果一开始就发送大量的数据,任有可能引发问题,
网络上的计算机很可能已经处于拥堵的网络状态,在不清楚当前网络状态的情况下,贸然发送大量数据,可能雪上加爽。
双方进行窗口大小协商之后,对于发送方还有一个叫拥塞窗口,一开始非常小,模清当前网络状态,探探路,但增长非常快,而实际发送大小取小的那一
个,也就意味着拥塞窗口控制了tcp传输的一个慢启动和快速增长。
少量丢包仅仅触发超时重传,大量的丢包,我们就认为网络拥塞
对处理速度比较自信,对接收到的数据可以快速处理,只要稍微延时一下进行ack答应,那么能够说设置的窗口大小就
尽可能的大,一直保证一个tcp的最大吞吐量
假如是立即回答,这个回复的窗口大小就会比较小
窗口越大,网络吞吐量就越大,传输效率就越高,我们要保证网络不拥塞的情况下尽量提高效率
在延迟应答的基础上,很多情况下,客户端服务器在应用层是一发一收。这是ACK也可以搭顺风车
将确定应答直接标记在即将发送的数据报内,那么这样不仅传输了数据还对上一次接收到的数据处进行应答(少传输一个
应答)
数据的发送和接受灵活,但是会有粘包问题产生
发送端:为了提高性能,将多个小包和到一块进行发送,但是数据没有边界
接收端:接收到的数据都会放在接收的缓冲区,假如处理的比较慢,导致缓冲区会接受的越来越多淤积合并
如何避免粘包:解决数据无边界的问题
数据定长,特殊字符间隔,tlv格式数据
为什么TCP这么复杂?
因为要保证可靠性,同时尽可能提高性能