几乎所有的HTTP通信都是由TCP/IP承载的,TCP/IP是一种常用的分组交换网络分层协议集。
TCP为HTTP提供了一条可靠(是因为 确认延迟)的比特传输管道。从TCP连接一端填入的字节会从另一端以原有的顺序、正确的传送出来。
<源IP地址、源端口号、目的IP地址、目的端口号>
这4个值一起唯一地定义了一条连接。两条不同的TCP连接不能拥有4个完全相同的地址组件值。
HTTP要传送一条报文时,会以流的形式将报文数据的内容通过一条打开的TCP连接按序传输。TCP收到数据流之后,会将数据流砍成被称作段的小数据块,并将段封装在IP分组中,通过因特网进行传输。
建立连接可能需要花费一些时间,时间长短取决于服务器距离的远近、服务器的负载情况、以及因特网的拥挤程度。
(1)客户端向服务器发送一个小的TCP分组(设置了一个特殊的SYN标记);
(2)如果服务器接受连接,会向客户端会送一个TCP分组(设置SYN和ACK标记);
(3)客户端向服务器回送一条确认信息,通知其已成功建立(设置ACK标记)。
小的HTTP事务可能会在TCP建立上花费50%,或更多的时间
因特网自身无法确认可靠的分组传输(因特网路由器超负荷的话,可以随意丢弃分组),所以TCP实现了自己的确认机制来确保数据的成功传输。
每个TCP段都有一个序列号和数据完整性校验和。每个段的接收者收到完好的段时,都会向发送者回送小的确认分组。如果发送者没有在指定的窗口时间内收到确认信息,发送者就认为分组已被破坏或损坏,并重发数据。
由于确认报文很小,所以TCP允许在发往相同方向的输出数据分组中对其进行”捎带“。这样可以更有效的利用网络。延迟确认算法 会在一个特定的窗口时间(通常是100~200毫秒)内将输出确认存放在缓冲区中,以寻找能够捎带它的输出数据分组;如果再那个时间段内没有输出数据分组,就将确认信息放在单独的分组中传送,这会导致延时,所以可以根据使用操作系统的不同,调整或禁止延迟确认算法。
TCP连接会随着时间进行自我”调谐“,起初会限制连接的最大速度,如果数据成功传输,会随着时间的推移提高传输的速度,这种调谐称为TCP慢启动,用于防止因特网的突然过载或拥塞。
由于存在这种特性,所以新连接的传输速度会比已经交换过一定数据的、已调谐连接慢一些,所以可以重用现存连接的工具来提高(如,持久连接)。
每个TCP段中都至少装载了40个字节的标记和首部,所以如果TCP发送了大量包含少量数据的分组,网络的性能就会严重下降。
Nagle算法鼓励发送全尺寸(LAN上最大尺寸的分组大约是1500字节,在因特网上是几百字节)的段。其实现是将部分数据缓存起来,只有当挂起分组被确认或者缓存中积累了足够发送一个全尺寸分组的数据时,才会将缓存的数据发送出去。当然这有时会导致,小的HTTP报文无法填满一个分组,可能会因为等待那些永远不会到来的额外数据而产生时延。 HTTP应用程序可在自己的栈中设置参数TCP_NODELAY,禁用Nagle算法。
当某个TCP端点关闭TCP连接时,会在内存中维护一个小的控制块,用来记录最近所关闭连接的IP地址和端口号。这类信息只会维持一小段时间,通常是所估计的最大分段使用期的两倍(称为2MSL,通常为2分钟)。
2MSL的连接关闭延迟通常不是什么问题。进行性能基础测试时,通常只有一台或几台用来产生流量的计算机连接到某系统中去,这样就限制了连接到服务器的客户端IP地址数。由于可以源端口的数量有限(比如,6000个),而且在2MSL秒内连接是无法重用的,连接率60000/120=500次/秒。
克服单条连接的空载时间和带宽限制,加载速度也会有所提高,时延可以重叠起来。
并行连接不一定更快,打开大量连接会消耗很多内存资源,且客户端网络带宽不足时,并行加载对象会互相竞争有限的带宽,每个对象都会以较慢的速度按比例加载。
实际上,浏览器确实使用了并行连接,但它们会将连接的总数限制为一个较小的值(通常为4个,Chrome为6个)。
HTTP/1.1允许HTTP设备在事务处理结束之后将TCP连接保持在打开状态,以便为未来的HTTP请求重用现存的连接。其可以避开缓慢的连接建立阶段,已打开的连接还可以避免慢启动的拥塞适应阶段,以便更快速地进行数据的传输。(HTTP/1.1持久连接在默认情况下是激活的,Connection: keep-alive;
)
与持久连接相比,并行连接的缺点:
- 每个事务都会打开/关闭一条新的连接,会耗费时间和带宽;
- 由于TCP慢启动特征的存在,每条新连接的性能都会有所降低;
- 可打开的并行连接数量实际上是有限的。
对管道化连接的几条限制:
服务器永远都无法确定在它关闭空闲连接的那一刻,在线路的那一头客户端有没有数据要发送。
要实现正常关闭的应用程序首先应该关闭它们的输出信道,然后等待连接另一端的对等实体关闭它的输出信道。然后周期性地检查其输入信道的状态(查找数据,或流的末尾)。如果在一定的时间区间内对端没有关闭输入信道,应用程序可以强制关闭连接,以节省资源。
Node中使用net模块可以实现基于TCP的数据通信
var net = require('net')
var server = net.createServer((socket) => {
server.getConnections((err, count) => {
server.maxConnections = 2
console.log(`当前存在${count}个客户端连接;其允许最大连接数为${server.maxConnections}`)
})
socket.on('data', (data) => {
console.log(`收到客户端数据${data}`)
socket.write('Hello')
})
socket.on('close', (data) => {
console.log(`客户端已关闭`)
})
})
server.listen(8431, 'localhost')
console.log('TCP服务器已创建!')
var net = require('net')
var client = new net.Socket()
client.setEncoding('utf8')
client.connect(8431, 'localhost', () => {
console.log('已连接到tcp服务器')
client.write('你好')
setTimeout(() => {
client.end('再见!')
}, 3000)
})
client.on('data', (data) => {
console.log(`收到服务器数据${data}`)
})
将网络中传送的数据包完全截获下来提供分析。其针对网络层、协议、主机、网络或端口的过滤,并提供and、or、not等逻辑语句来帮助你去掉无用的信息。
# 对任意接口进行抓包
$ sudo tcpdump -i any
# 限制抓包数量 -c
$ sudo tcpdump -i any -c10
tcpdump
默认是将 IP 地址和端口号解析为对应的接口名以及服务协议名称。而通常在网络故障排查中,使用 IP 地址和端口号更便于分析问题;用 -n
选项显示 IP 地址,-nn
选项显示端口号
$ sudo tcpdump -i any -c100 -nn port 8431
tcp建立连接-三次握手:客户端SYN => 服务端SYN+ACK => 客户端ACK
21:54:39.669669 IP 127.0.0.1.62354 > 127.0.0.1.8431: Flags [S], seq 863434451, win 65535, options [mss 16344,nop,wscale 5,nop,nop,TS val 906578166 ecr 0,sackOK,eol], length 0
21:54:39.669730 IP 127.0.0.1.8431 > 127.0.0.1.62354: Flags [S.], seq 4212941000, ack 863434452, win 65535, options [mss 16344,nop,wscale 5,nop,nop,TS val 906578166 ecr 906578166,sackOK,eol], length 0
21:54:39.669738 IP 127.0.0.1.62354 > 127.0.0.1.8431: Flags [.], ack 1, win 12759, options [nop,nop,TS val 906578166 ecr 906578166], length 0
数据传输:客户端PUSH(你好)=> 服务端ACK+PUSH(Hello)=>客户端ACK+PUSH(再见!) => 服务端ACK+PUSH(Hello)=> 客户端ACK
21:54:39.671731 IP 127.0.0.1.62354 > 127.0.0.1.8431: Flags [P.], seq 1:7, ack 1, win 12759, options [nop,nop,TS val 906578168 ecr 906578166], length 6
21:54:39.671755 IP 127.0.0.1.8431 > 127.0.0.1.62354: Flags [.], ack 7, win 12759, options [nop,nop,TS val 906578168 ecr 906578168], length 0
21:54:39.672276 IP 127.0.0.1.8431 > 127.0.0.1.62354: Flags [P.], seq 1:6, ack 7, win 12759, options [nop,nop,TS val 906578168 ecr 906578168], length 5
21:54:39.672293 IP 127.0.0.1.62354 > 127.0.0.1.8431: Flags [.], ack 6, win 12759, options [nop,nop,TS val 906578168 ecr 906578168], length 0
21:54:42.678860 IP 127.0.0.1.62354 > 127.0.0.1.8431: Flags [P.], seq 7:16, ack 6, win 12759, options [nop,nop,TS val 906581170 ecr 906578168], length 9
21:54:42.678912 IP 127.0.0.1.8431 > 127.0.0.1.62354: Flags [.], ack 16, win 12758, options [nop,nop,TS val 906581170 ecr 906581170], length 0
21:54:42.679377 IP 127.0.0.1.8431 > 127.0.0.1.62354: Flags [P.], seq 6:11, ack 16, win 12758, options [nop,nop,TS val 906581170 ecr 906581170], length 5
21:54:42.679425 IP 127.0.0.1.62354 > 127.0.0.1.8431: Flags [.], ack 11, win 12759, options [nop,nop,TS val 906581170 ecr 906581170], length 0
tcp关闭(客户端发起)-四次挥手:客户端FIN => 服务端ACK(seq+1) => 服务端FIN => 客户端ACK(seq+1)
21:54:42.680094 IP 127.0.0.1.62354 > 127.0.0.1.8431: Flags [F.], seq 16, ack 11, win 12759, options [nop,nop,TS val 906581171 ecr 906581170], length 0
21:54:42.680177 IP 127.0.0.1.8431 > 127.0.0.1.62354: Flags [.], ack 17, win 12758, options [nop,nop,TS val 906581171 ecr 906581171], length 0
21:54:42.682657 IP 127.0.0.1.8431 > 127.0.0.1.62354: Flags [F.], seq 11, ack 17, win 12758, options [nop,nop,TS val 906581173 ecr 906581171], length 0
21:54:42.682762 IP 127.0.0.1.62354 > 127.0.0.1.8431: Flags [.], ack 12, win 12759, options [nop,nop,TS val 906581173 ecr 906581173], length 0
值 | 标志类型 | 描述 |
---|---|---|
S | SYN | Connection Start |
F | FIN | Connection Finish |
P | PUSH | Data push |
R | RST | Connection reset |
. | ACK | Acknowledgment |
# 只要抓取 ICMP 报文
$ sudo tcpdump -i any -c5 icmp
# 只抓取和特定主机相关的数据包
$ sudo tcpdump -i any -c5 -nn host 54.204.39.132
# 根据源 IP 地址或者目的 IP 地址或者主机名
$ sudo tcpdump -i any -c5 -nn src/dst 192.168.122.98
# 根据服务类型或者端口号来筛选数据包
$ sudo tcpdump -i any -c5 -nn port 80
# 多条件筛选 and or
$ sudo tcpdump -i any -c5 -nn src 192.168.122.98 and port 80
使用 -w
选项来保存数据包而不是在屏幕上显示出抓取的数据包:
$ sudo tcpdump -i any -c10 -nn -w webserver.pcap port 80
# 阅读内容
$ tcpdump -nn -r webserver.pcap
$ tcpdump -nn -r webserver.pcap src 54.204.39.132
参考地址: