深入理解TCP协议及其源代码

深入理解TCP协议及其源代码

前言

在前面实验我们分别实现了Socket 通信工具,探讨了Socket API、Socket 调用原理等。但是还没有针对某一实例进行讲解,在本实验我们将针对TCP协议进行详细分析,期待在Linux内核进行分析TCP原理。

1.Tcp基本原理

TCP是一种面向连接、可靠、基于字节流的传输协议,位于TCP/IP模型的传输层。

  • 面向连接:不同于UDP,TCP协议需要通信双方确定彼此已经建立连接后才可以进行数据传输;
  • 可靠:连接建立的双方在进行通信时,TCP保证了不会存在数据丢失,或是数据丢失后存在拯救丢失的措施;
  • 字节流:实际传输中,不论是何种数据,TCP都按照字节的方式传输,而非以数据包为单位。

针对它的这三种特性,本小节我们将对其原理进行探究。

1.1面向连接(三次握手)

  • 第一次握手。如图,TCP双方在进行连接时首先由Client(客户端)发起连接请求,请求中附带连接参数,包括随机数字起点Seq(预防传输时字节序列被预测收到攻击),连接请求标志位SYN(占用1字节序号)等。
  • 第二次握手:当Server(服务器)分配资源打开监听请求,收到客户端请求后,对请求头进行解析。若连接建立成功则分配相应资源,并返回针对客户端请求的确认报文,其中响应报文头部参数包括:连接建立标志位SYN、Server端针对该通信过程的随机Seq、针对该请求的确认号ack、可附加接收窗口大小信息等。
  • 第三次握手。客户端收到服务端的确认连接请求后将会发送对该确认请求的确认(简单来说也就是A请求B,B告诉A准许,A再告诉B我知道你准许了),试想若不对该请求进行响应那么服务端将白白分配资源并等待。
    若以上三次握手都没问题则连接建立,在第三次握手的时候即可开始传送数据。

    1.2可靠

2.基本原理探究

在上次实验,我们通过追踪qemu底层的sys_call入口观察系统态和内核态之间的联系,理清了系统层面是怎样对底层接口进行调用的。在本小节,详细分析一下TCP协议在内核中的基本原理。

TCP协议的初始化及socket创建TCP套接字描述符

如图所示,上面展示了TCP调用系统内核中的相关函数进行资源分配和通信。经过上次实验对qemu的跟踪不难发现在建立连接及通信时在服务端经历了socket()->bind()->listen()->accept()四个步骤,在accet() 函数之后会进行客户端的数据通信。
为了验证连接建立的过程,我们对gdb跟踪的函数过程进行抓包,只要出现三次握手就能够捕捉到,同时对系统调用接口函数打断点,就能够知道在那个函数调用之间进行了三次通信。
初始执行过程,在wireshark中捕捉不到TCP通信,不断跳过断点,当客户端执行到connect(),服务端执行到accept()后捕获到TCP通信过程如下图。

不难发现开始建立三次握手的过程发生在服务端accpet()后,当连接建立后,执行send()及rev()进行数据通信。
知道TCP建立连接的过程,接下来我们来探究一下它的初始化和套接字初始化过程。在文件/net/tcp /下找到accpet函数定义发现它最终调用了__sys_accept4,同时connect函数调用了__sys_connect函数。
TCP的三次握手从用户程序的角度看就是客户端connect和服务端accept建立起连接时背后完成的工作,在内核socket接口层这两个socket API函数对应着sys_connect和sys_accept函数,进一步对应着sock->opt->connect和sock->opt->accept两个函数指针,在TCP协议中这两个函数指针对应着tcp_v4_connect函数和inet_csk_accept函数。
分析到这里,应该能够想到我们可以通过MenuOS的内核调试环境设置断点跟踪tcp_v4_connect函数和inet_csk_accept函数来进一步验证三次握手的过程。

tcp_v4_connect函数

tcp_v4_connect函数的主要作用就是发起一个TCP连接,建立TCP连接的过程自然需要底层协议的支持,因此我们从这个函数中可以看到它调用了IP层提供的一些服务,比如ip_route_connect和ip_route_newports从名称就可以简单分辨,这里我们关注在TCP层面的三次握手,不去深究底层协议提供的功能细节。我们可以看到这里设置了 TCP_SYN_SENT并进一步调用了 tcp_connect(sk)来实际构造SYN并发送出去。
tcp_connect函数具体负责构造一个携带SYN标志位的TCP头并发送出去,同时还设置了计时器超时重发。
其中tcp_transmit_skb函数负责将tcp数据发送出去,这里调用了icsk->icsk_af_ops->queue_xmit函数指针,实际上就是在TCP/IP协议栈初始化时设定好的IP层向上提供数据发送接口ip_queue_xmit函数,这里TCP协议栈通过调用这个icsk->icsk_af_ops->queue_xmit函数指针来触发IP协议栈代码发送数据,感兴趣的读者可以查找queue_xmit函数指针是如何初始化为ip_queue_xmit函数的。具体ip_queue_xmit函数内部的实现我们在后续内容中会专题研究,这里聚焦在TCP协议的三次握手。

inet_csk_accept函数

服务端调用inet_csk_accept函数会从请求队列中取出一个连接请求,如果队列为空则通过inet_csk_wait_for_connect阻塞住等待客户端的连接。
inet_csk_wait_for_connect函数就是无限for循环,一旦有连接请求进来则跳出循环。
按如上思路跟踪调试代码的话,会发现connect之后将连接请求发送出去,accept等待连接请求,connect启动到返回和accept返回之间就是所谓三次握手的时间。

这俩函数核心是通过sock->opt->connect和sock->opt->accept这两个函数指针调用了某个函数。

  其中sock是struct socket类型的,为了追踪下去我们还得找到struct socket这个结构体的定义。
和TCP初始化基本参数的过程,我们将这几个底层函数拆分开来,进行单独的gdb跟踪调试

原文地址:https://www.cnblogs.com/xshun/p/12093145.html

时间: 2024-10-10 11:20:43

深入理解TCP协议及其源代码的相关文章

深入理解TCP协议及其源代码-拥塞控制算法分析

这是我的第五篇博客,鉴于前面已经有很多人对前四个题目如三次握手等做了很透彻的分析,本博客将对拥塞控制算法做一个介绍. 首先我会简要介绍下TCP协议,其次给出拥塞控制介绍和源代码分析,最后结合源代码具体分析拥塞控制算法. 一.TCP协议 关于TCP协议,其实在我的第二篇博客中:https://www.cnblogs.com/xiaofengustc/p/12012638.html 已有简要的介绍,并且在该博客中我还拿TCP协议与HTTP协议.UDP协议做了相关对比.有兴趣的同学可以参见我的第二篇博

深入理解TCP协议及其源代码——connect及bind、listen、accept背后的“三次握手”

一.TCP简介 TCP(Transmission Control Protocol,传输控制协议)是一个传输层(Transport Layer)协议,它在TCP/IP协议族中的位置如图1所示.它是专门为了在不可靠的互联网络上提供一个面向连接的且可靠的端到端(进程到进程)字节流而设计的.互联网络与单个网络不同,因为互联网络的不同部分可能有截然不同的拓扑.带宽.延迟.分组大小和其他参数.TCP的设计目标是能够动态地适应互联网络的这些特性,而且当面对多种失败的时候仍然足够健壮. 图1 TCP在TCP/

深入理解TCP协议及其源代码——三次握手

Wireshark分析报文 对TCP三次握手过程进行抓包分析,并通过Wireshark的Analyze分析出tcp握手过程,通过截图体现传输内容. 1.捕获大量的由本地主机到远程服务器的TCP分组: 2.浏览追踪信息 在显示筛选规则编辑框中输入“tcp”,可以看到在本地主机和服务器之间传输的一系列tcp和HTTP消息,你应该能看到包含SYN Segment的三次握手.通过Analyze的Follow TCP Stream分析出传输内容.写出其中某TCP数据包的源IP地址,目的IP地址,源端口,目

深入理解TCP协议及其源代码-send和recv背后数据的收发过程

send和recv背后数据的收发过程 send和recv是TCP常用的发送数据和接受数据函数,这两个函数具体在linux内核的代码实现上是如何实现的呢? ssize_t recv(int sockfd, void buf, size_t len, int flags) ssize_t send(int sockfd, const void buf, size_t len, int flags) 理论分析 对于send函数,比较容易理解,捋一下计算机网络的知识,可以大概的到实现的方法,首先TCP是

深入理解TCP协议及其源代码——send和recv背后数据的收发过程

TCP数据发送和接收的原理 TCP连接的建立过程 TCP Socket的连接的过程是服务端先通过socket()函数创建一个socket对象,生成一个socket文件描述符,然后通过bind()函数将生成的socket绑定到要监听的地址和端口上面.绑定好了之后,使用listen()函数来监听相应的端口.而客户端是在通过socket()函数创建一个socket对象之后,通过connect()函数向被服务端监听的socket发起一个连接请求,即发起一次TCP连接的三次握手.接下来就可以就可以通过TC

深入理解TCP协议的三次握手,分析源码并跟踪握手过程

1.TCP三次握手建立连接 在TCP中,面向连接的传输需要经过三个阶段:连接建立.数据传输和连接终止. 三次握手建立连接 在我们的例子中,一个称为客户的应用程序希望使用TCP作为运输层协议来和另一个称为服务器的应用程序建立连接. 这个过程从服务器开始.服务器程序告诉它的TCP自己已准备好接受连接.这个请求称为被动打开请求.虽然服务器的TCP已准备好接受来自世界上任何一个机器的连接,但是它自己并不能完成这个连接. 客户程序发出的请求称为主动打开.打算与某个开放的服务器进行连接的客户告诉它的TCP,

通俗大白话来理解TCP协议的三次握手和四次分手

通俗理解: 但是为什么一定要进行三次握手来保证连接是双工的呢,一次不行么?两次不行么?我们举一个现实生活中两个人进行语言沟通的例子来模拟三次握手. 引用网上的一些通俗易懂的例子,虽然不太正确,后面会指出,但是不妨碍我们理解,大体就是这么个理解法. 第一次对话: 老婆让甲出去打酱油,半路碰到一个朋友乙,甲问了一句:哥们你吃饭了么? 结果乙带着耳机听歌呢,根本没听到,没反应.甲心里想:跟你说话也没个音,不跟你说了,沟通失败.说明乙接受不到甲传过来的信息的情况下沟通肯定是失败的. 如果乙听到了甲说的话

深入理解TCP协议:三次握手详解

1.什么是三次握手? TCP协议建立连接时,需要三次发送数据包: 第一次:客户机向服务器端请求建立连接 第二次:服务器收到客户机的请求,发出响应 第三次:客户机收到响应 认为连接建立成功 详细过程: 名词解释: SYN - 标志位 只有第一次和第二次为1,第三次和其他任何情况都是0 ACK - 标志位 只有第一次不为1,第二,三次和其他任何情况都是1 Sequence Number 顺序号,初始值为随机数 Acknowledgment Number 确认号,下一次对收到的数据顺序号的期望 第一次

如何理解TCP协议是无边界的,以及粘包?

更新记录 时间 版本修改 2020年4月2日 初稿 我们从经典的计算机科学丛书上阅到的知识,都说:TCP协议是没有消息边界的.但是这个要怎么理解呢?在我没有接触底层的套接字相关逻辑时.我对此也没有特别的了解.直到阅读了套接字的相关逻辑源码,才对此有了一定的了解 TCP的发包和我们业务层所发出的协议数据是不一定吻合的.也就是说,我们发的数据库可能会被分拆成不同的包.然后再和别的协议(这里当然是只发往同一个端口)的数据封装同一个TCP包体. 因此.对于我们业务网络层而言,我们需要在一个TCP包体里面