![](https://img-blog.csdnimg.cn/20201014180756780.png?x-oss-process=image/resize,m_fixed,h_64,w_64)
TCP协议
文章平均质量分 81
Remy1119
专注于技术
展开
-
4.5 路径MTU发现
TCP报文需要封装成IP报文才会发送,报文在网络中按照一定路径传输后会抵达目的地。最理想的情况是IP报文的大小正好是这条路径所能容纳的最大尺寸,因为报文小了则数据传输效率不高,大了则会引起分片。分片会使得路由器的负担加重,增加延迟,而且会增加报文丢失的概率。而IP报文的传输路径是事先不知道的,而且在传输过程中也可能发送变化,所以TCP需要动态测路径MTU的大小,这就是路径MTU发现。原创 2015-03-25 22:33:15 · 10386 阅读 · 3 评论 -
6.1 序列号与确认号
由前文可知,TCP的序列号(seq)与确认号(ack_seq)都是无符号32的整数。seq用于为每一字节TCP数据编号(SYN和FIN标记位也会占用一个seq);ack_seq用于告知数据发送端:“接收端已经收到了ack_seq - 1号数据,请发送ack_seq号数据”。下面我们重温一下TCP连接建立和数据传输过程,在这个过程中探究一下seq和ack_seq是如何使用的。首先来介绍一下tcp_s原创 2015-04-02 13:13:13 · 3050 阅读 · 1 评论 -
6.2 TCP滑动窗口
TCP滑动窗口的功能是实现流量控制。数据接收方只接收seq落入窗口范围内的数据;发送方也不会发送窗口之外的数据,一旦发现窗口太小则会停止发送直到窗口变大,这样TCP数据接收方就能通过窗口通告来控制数据发送方发送数据的速度。窗口的值存储在TCP报文段的window字段中,大小为16bit,即窗口的最大值是65535。如果使用窗口扩大选项(后续讨论),则通告窗口的值为window左移窗口扩大因子个位数原创 2015-04-03 08:26:05 · 2125 阅读 · 0 评论 -
5.7 TCP receive copy offload
TCP receive copy offload是一种异步IO技术,这种技术使用网络协议栈中的DMA引擎代替CPU来实现copy-to-user的操作,节约了CPU周期。DMA引擎是指数据移动加速(Data Movement Acceleration)引擎,它将某些传输数据的操作从CPU转移到专用硬件,从而可以进行异步传输并减轻CPU负载。 支持这个功能需要开启CONFIG_NET_DMA内原创 2015-04-02 08:51:43 · 1470 阅读 · 0 评论 -
5.6 TCP prequeue
TCP中用于接收skb的缓存除了sk->sk_receive_queue之外,还有prequeue。TCP prequeue中的包会在进程上下文中处理,而非软中断上下文。TCP prequeue特性会给带来较大的延迟,其优点在于这个特性在理论上给与了别的进程以及别的socket连接更多的公平性,但实际情况如何不得而知。开启这个功能的条件是net.ipv4.tcp_low_latency内核选项为0原创 2015-04-01 21:42:41 · 2585 阅读 · 0 评论 -
5.5 收包系统调用
收到内核发出的“有数据可读”的事件通告后,进程就可以使用read、readv、recv、recvfrom、recvmsg系统调读取TCP数据。这些函数在内核都会调用__sock_recvmsg函数:原创 2015-03-31 23:51:26 · 1235 阅读 · 0 评论 -
5.4 ACK发送与接收
tcp_send_challenge_acktcp_incr_quickack ACK标记的处理由tcp_ack函数完成: 3325 static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)3326 { 3327 struct inet_connection_soc原创 2015-03-30 14:16:22 · 4333 阅读 · 2 评论 -
5.3 慢速路径处理
TCP报文段进入慢速处理的条件是:原创 2015-03-30 10:23:49 · 4841 阅读 · 0 评论 -
5.2 快速路径处理
处理TCP数据段的唯一入口函数是tcp_rcv_established。在Liinux中,有两种方法来处理传入TCP数据段:快速路径(Fast Path)和慢速路径(Slow Path)。使用快速路径只进行最少的处理,如处理数据段、发生ACK、存储时间戳等。使用慢速路径可以处理乱序数据段、PAWS、socket内存管理和紧急数据等。Linux通过预测标志来区分这两种处理模式。见tcp_rcv_es原创 2015-03-29 06:37:07 · 2367 阅读 · 0 评论 -
5.1 GRO(Generic Receive Offload)
GRO(Generic Receive Offload)的功能将多个 TCP 数据聚合在一个skb结构,然后作为一个大数据包交付给上层的网络协议栈,以减少上层协议栈处理skb的开销,提高系统接收TCP数据包的性能。这个功能需要网卡驱动程序的支持。合并了多个skb的超级 skb能够一次性通过网络协议栈,从而减轻CPU负载。 GRO是针对网络收包流程进行改进的,并且只有NAPI类型的驱动才原创 2015-03-27 14:19:33 · 7490 阅读 · 1 评论 -
4.4 TCP Small Queue(TSQ)
TCP发送的数据经过IP层添加IP包头后,会被发送到IP数据栈和网卡之间的队列中,当网卡能够发送数据时会到这个队列中去取skb。当TCP发送数据过快时,或一个带宽很大或者包速率很大的非TCP数据流会把队列里的所有空间占满,造成数据包丢失和延时的问题。更糟的是,这样很可能会使另一个缓存产生,进而产生一个静止队列(standing queue),造成更严重的延时并使TCP的RTT和拥塞窗口的计算出现问原创 2015-03-26 09:38:13 · 4399 阅读 · 0 评论 -
4.3 TCP Splice
分散-聚集IO是一种允许向skb的非连续空间(frag page)中写入数据,并利用网卡支持发送skb中非连续空间中的数据来实现减少数据copy次数,以提高数据发送效率的方法。sendfile系统调用就是利用这一功能来提升性能的。下面以sendfile系统调用为例说明分散-聚集IO功能的使用方法。 通常,发送一个文件中数据的流程是:(1)使用read系统调用读取file中的数据;这个过程原创 2015-03-24 22:08:09 · 2445 阅读 · 0 评论 -
4.1 发包系统调用
连接建立完成后,应用进程可以使用send、sendto、sendmsg、write、writev系统调用来发送TCP数据,其中sendmsg和writev可以发送位于多个不连续内存中的数据。使用这些函数发送数据时需指定socket文件描述符、要发送的数据所在的缓冲区首地址及数据长度等信息。上述发包系统调用对应的内核函数都会调用__sock_sendmsg_nosec函数。现在以send系统调用和w原创 2015-03-23 10:23:15 · 1600 阅读 · 0 评论 -
8.6 TCP Fast Open(TFO)
TFO(TCP Fast Open)是一种能够在TCP连接建立阶段传输数据的机制。使用这种机制可以将数据交互提前,降低应用层事务的延迟。其基本步骤如下:1、客户端发送一个SYN包到服务器,这个包中携带了Fast Open Cookie请求的TCP选项;2、服务器生成一个cookie,这个cookie是通过使用密钥加密客户端的IP地址生成的。服务器给客户端发送SYN|ACK响应,在响应包原创 2015-03-21 13:09:13 · 9437 阅读 · 0 评论 -
6.3 紧急模式
通常情况下,TCP数据由连接的一端发送到另一端,数据的所有字节都是精确排序的,后写入的字节绝不会早于先写入的字节到达。然而socket API提供一种功能可以使得一些数据无阻的先于早写入的数据到达接收端,这种功能就是所谓的发送带外数据 。在TCP中这样的功能是通过紧急数据模式完成的。 一个TCP流通常希望顺序地发送数据字节,那么乱序的发送数据就似乎与流的概念相违背。那么为什么要提供带外数据的原创 2015-04-03 13:48:25 · 1308 阅读 · 0 评论 -
6.4 Nagle算法与CORK算法
6.4.1 算法目的 当发送端应用进程产生数据很慢(比如Telnet应用),就会使应用进程间传送的TCP有效载荷很小(可能只有1个字节),而传输开销有40字节(20字节的IP头+20字节的TCP头) 这种现象就叫糊涂窗口综合症(Silly Windw Syndrome)。如果有大量的小TCP报文在网络中传输会导致网络拥塞。Nagle算法是为了解决TCP数据发送端“糊涂窗口综合症”原创 2015-04-04 15:25:54 · 1819 阅读 · 0 评论 -
3.7 Accept系统调用
在完成三次握手后,server端TCP会创建一个sock结构来与client端的scoket进行一对一的数据传递。但这个sock进存在于内核中,server端用户进程还无法使用。进程要想使用这个新的连接,必须调用accept系统调用生成一个与sock关联的socket,然后进程通过对这个socket进行recv、send等操作来实现与client端的数据传递。连接建立完成后,服务器端的应用进程会从原创 2015-03-22 20:49:27 · 1235 阅读 · 3 评论 -
2.3 Listen系统调用
在C/S模式中,服务器进程必须调用bind系统调用,这样才能确定对外提供服务的地址;客户端进程可以调用bind,这是为了确定客户端在发送请求时使用的源IP地址和端口。而listen系统调用服务器进程必须调用,对于客户端进程而言此函数无用。listen系统调用究竟干了什么? listen系统调用的函数原型:int listen(int sockfd, int backlog); so原创 2015-03-15 22:24:00 · 1431 阅读 · 3 评论 -
3.6 SYN Cookie
在三次握手过程中,server端的TCP收到SYN请求后会建立一个request_sock保存在syn_table中。如果有恶意攻击者大量发送IP地址或端口不同的SYN包,则server端TCP的syn_table很快会被占满,而普通用户对server的正常访问会因为syn_table已满而被拒绝。这就是SYN Flood攻击。SYN Cookie技术就是为了应对SYN Flood攻击而产生的,它原创 2015-03-19 21:45:07 · 2031 阅读 · 0 评论 -
3.5 ICMP不可达报文的处理
在三次握手阶段有两种情况TCP会收到ICMP“目的不可达”报文:1、client端通过connect系统调用发送SYN请求到server端后,server没有进程在相应的地址或端口处理请求,这时client端会收到ICMP不可达报文2、client端通过connect系统调用发送SYN请求后崩溃,server端收到SYN后发送SYN|ACK,client端收到SYN|ACK后会给serve原创 2015-03-19 08:03:30 · 12017 阅读 · 0 评论 -
3.4 同时打开
两个应用程序同时彼此执行connect系统调用向对方发送SYN请求,一方所使用的源|目的IP和源|目的端口恰好是对方的目的|源IP和目的|源端口,这就是“同时打开”。尽管这种情况很少见,但TCP是支持的。 这种场景下,应用进程先发送一个SYN,然后socket进入TCP_SYN_SENT状态,这时收到了对端的SYN,而非SYN|ACK。处理流程与3.3节中客户端处理SYN|ACK的相似:原创 2015-03-18 12:35:13 · 829 阅读 · 0 评论 -
3.3 连接建立完成
服务器发送的SYN|ACK抵达客户端的网卡、经过链路层、网络层的协议处理后,如果网络层协议为IPv4,则会进入到TCPv4的入口函数tcp_v4_rcv:1961 int tcp_v4_rcv(struct sk_buff *skb)1962 {1963 const struct iphdr *iph;1964 const struct tcphdr *th;1965原创 2015-03-18 09:30:10 · 2105 阅读 · 1 评论 -
3.2 SYN的接收与SYN|ACK的发送
客户端发送的SYN请求到达服务器的网卡后,进入服务器操作系统的网络协议栈,经过链路层和网络层的处理后,抵达TCP协议的入口函数。TCPv4的入口函数是tcp_v4_rcv,TCPv6的入口函数是tcp_v6_rcv。下面对tcp_v4_rcv进行分析: 1961 int tcp_v4_rcv(struct sk_buff *skb)1962 { 1963 const struc原创 2015-03-17 15:20:41 · 2257 阅读 · 0 评论 -
3.1 Connect系统调用
在server端完成listen系统调用之后,就处于可以接受请求的状态,这时client端就可以发送SYN请求给server从而开始“三次握手”。SYN请求的发送是通过connect系统调用实现的:int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 其中,sockfd是使用socket系统调用创建的原创 2015-03-16 09:15:12 · 3009 阅读 · 1 评论 -
2.2 Bind系统调用
Socket是用socket系统调用生成的,它指定了地址族(domain)但并没有地址与之关联。Bind系统调用会将一个地址与socket关联。其函数原型为:int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);其中,sockfd为套接字的文件描述符,addr指向存放地址信息的结构体的首地址,addrle原创 2015-03-13 11:13:21 · 1660 阅读 · 0 评论 -
2.1 Socket系统调用
TCP是传输层协议,网络层使用IPv4或IPv6协议,即TCP报文作为IPv4或IPv6报文的数据部分。本文中将使用IPv4协议的TCP称为TCPv4,将使用IPv6的TCP称为TCPv6。使用不同的IP协议对于TCP的影响主要是连接查找、检验和计算等与IP地址相关的部分,核心功能并无差异。故此在以下的代码分析过程中会以TCPv4为主。 先来看看socket系统调用的函数原型:int原创 2015-03-12 14:07:42 · 3016 阅读 · 0 评论 -
1.2 TCP首部格式
TCP是一个通信协议,所谓协议就是通信各方约定好的通信规则。大家必须全部严格遵守这些规则,通信才能顺利完成。TCP的通信规则由一系列RFC来规范。首先是首部格式。在Linux中对应的结构体为: include/uapi/linux/tcp.h 24 struct tcphdr { 25 __be16 source; 26 __be16 dest; 27原创 2015-03-11 13:15:23 · 1920 阅读 · 0 评论 -
1.1 TCP协议简介
提起网络协议栈,首屈一指的自然是TCP/IP协议栈.作为TCP/IP协议栈的核心协议之一,TCP早已成为网络应用程序开发者实现可靠数据传递的首选工具,HTTP, FTP, Telnet等协议都是基于TCP实现的.那么TCP有什么样的协议?它有哪些特点能赢得大家如此的青睐呢? TCP(Transmission Control Protocol)传输控制协议是一种面向连接的、可靠的、基于字节流的原创 2015-03-11 08:54:54 · 1706 阅读 · 0 评论 -
TCP协议探秘
前言 研究TCP实现是为了更好地使用它。通过Unix的socket套接字接口,可以开发基于TCP的应用程序;了解TCP的实现可以更加明晰接口的行为,大大提高开发效率。使用Unix的socket套接字接口和系统提供的接口(sysctl,proc等)调整TCP相关参数,以控制TCP的行为,优化其性能;理解TCP的实现可以更加明确各个参数对TCP行为的影响,使得性能优化事半功倍。了解TCP协议原创 2015-03-10 21:43:44 · 3164 阅读 · 0 评论 -
6.5 TCP Connection Repair
TCP Connection Repair即TCP连接修复,这个功能是通过TCP_REPAIR socket选项实现的。当这个选项开启时,socket就会切换到修复模式,这个模式下对socket的任何操作都不会导致实质的协议行为(发送TCP报文),但会使socket直接进入成功完成动作的状态。6.5.1 相关选项 与TCP修复模式有关的socket选项有4个:TCP_REPA原创 2015-04-05 16:51:53 · 2803 阅读 · 0 评论 -
7.1 连接关闭流程
TCP是全双工协议,关闭连接时两个方向必须单独进行。当一端发送数据完毕后,应用进程就可以调用shutdown或close系统调用发送一个FIN来终止该方向上的连接。另一端收到FIN后,必须告知应用进程对端已经终止了数据连接。得到通知后应用进程可以调用shutdown或close系统调用关闭自己向对端发送数据的连接,这时TCP连接才会被真正关闭。 一端收到FIN,只意味着对端不会再原创 2015-04-05 21:57:00 · 757 阅读 · 0 评论 -
9.6 坚持(Persist)定时器
9.6.1 Why 数据发送方收到接收方的通告窗口为0时,就不能再发送数据,一直等到对方发送窗口更新为止。但对端发送的窗口更新报文可能会丢失,如果发送方只是等待的话会导致数据传输会一直停滞,最后连接会被断开。这时坚持定时器闪亮登场!数据发送方可以设置坚持定时器定时发送1个探测报文,对端收到后会对这个报文发送ACK报文,这样发送方就能及时得知窗口更新事件了。一旦窗口非0则数据传输原创 2015-04-10 10:35:12 · 1514 阅读 · 1 评论 -
9.7 保活(Keepalive)定时器
9.7.1 Why 当单工模式下TCP数据发送方发送了一些数据后就不再发数据,数据接收方也不会发送报文,这时TCP连接处于静止状态(比如Telnet应用)。保活功能可以使用保活定时器向对端发送探测报文来确定对端的连接是否正常,如果对端有回应则继续维持连接,否则关闭连接,释放资源。开启保活功能需要使用SO_KEEPALIVE socket选项。9.7.2 When原创 2015-04-10 13:21:23 · 1554 阅读 · 0 评论 -
9.8 延迟ACK定时器
9.8.1 Why TCP在收到数据后必须发送ACK给对端,但如果每收到一个包就给一个ACK的话会使得网络中被注入过多报文。TCP的做法是在收到数据时不立即发送ACK,而是设置一个定时器,如果在定时器超时之前有数据发送给对端,则ACK会被携带在数据中捎带过去;超时则由定时器发送ACK。这样就减少了报文的发送,提高了协议的效率。9.8.2 When 设置延迟A原创 2015-04-10 14:30:36 · 2163 阅读 · 0 评论 -
9.9 FIN_WAIT2定时器
9.9.1 Why 如果应用进程调用close系统调用关闭socket,但此时socket与对端的通信尚未完成,则这个socket被称为“孤儿socket”。如果孤儿socekt进入FIN_WAIT2状态(或socket进入FIN_WAIT2状态后再成为孤儿socket),会等待对端发送FIN以彻底结束连接。但如果对端一直不发送FIN则这个孤儿socekt会一直存在,从而一直占用原创 2015-04-10 16:06:08 · 1461 阅读 · 0 评论 -
9.10 TIME_WAIT定时器
9.10.1 Why 当socekt进入TIME_WAIT状态后,TIME_WAIT定时器启动。在超时之前,替代socket的tw sock会处理旧连接中的包,阻止其危害新连接。定时器超时后,tw sock被删除,并释放其占用的端口号。原创 2015-04-10 16:48:26 · 1342 阅读 · 2 评论 -
10.1 缓存管理简介
TCP原创 2015-04-12 23:35:12 · 767 阅读 · 0 评论 -
10.2 发送缓存管理
应用进程使用TCP发送的数据会先放入发送缓存中,TCP的发送缓存是一个skb队列。这个队列存在的意义是:保证应用进程交付TCP的数据能够可靠地交付目的端。在收到对端的ACK之前,发送缓存中的数据不能删除。10.2.1 使用缓存 对发送缓存的使用是从tcp_sendmsg函数开始的: 1016 int tcp_sendmsg(struct kiocb *iocb,原创 2015-04-13 13:55:45 · 1550 阅读 · 0 评论 -
10.3 接收缓存管理
TCP收到对端发送的数据后,通常不能立即交付应用进程。在应用进程取走数据之前,数据需要保存在接收缓存之中。如果应用进程取数据的速度比TCP从对端收数据的速度慢,则接收缓存中的数据会越来越多。因此在skb被放入接收缓存之前必须检查接收缓存能容纳的内存数,如果超出限制则必须丢弃skb。10.3.1 缓存占用原创 2015-04-13 17:06:31 · 2024 阅读 · 0 评论 -
11.1 I/O模型概览
在原创 2015-04-13 23:43:15 · 812 阅读 · 0 评论