0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

如何解释TCP报文的内容

FPGA之家 来源:FPGA之家 作者:FPGA之家 2022-08-31 09:12 次阅读

TCP报文格式

TCP协议有着自己的数据包格式,这里把TCP的数据包称为报文段(segment),TCP报文段封装在IP数据报中发送,TCP报文段由TCP首部和TCP数据区组成,首部区域包含了连接建立与断开、数据确认、窗口大小通告、数据发送相关的所有标志和控制信息。如下图:

d0e7bfa2-28c3-11ed-ba43-dac502259ad0.png

首部的大小为20~60字节,在没有任何选项的情况下,首部大小为20字节,与不含选项字段的IP报首部大小相同。TCP报文中的数据部分可以为空,例如在一个连接建立或断开时,双方交换的报文段仅有TCP首部;又如当一方没有任何数据需要发送,则它需要使用不包含任何数据的报文段来发送确认信息。

1、源端口号和目的端口号两个字段用于标识发送端和接收端应用进程分别绑定的端口号。这两个值加上IP数据报首部中的源IP地址和目的IP地址就能唯一确定一个TCP连接

2、32位序号字段标识了从TCP发送端到TCP接收端的数据字节编号,它的值为当前报文段中第一个数据的字节序号。在接收方,先计算出数据区数据的长度,然后使用首部中的序号字段,就能计算出报文最后一个字节数据的序号。当建立一个新连接时,握手报文首部中的SYN标志置1,此时,序号字段包含由发送方随机选择的初始序号ISN(Initial Sequence Number)。建立连接的报文(SYN)将占用一个数据编号,因此发送方随后将要发送数据的第一个字节序号为ISN+1

3、32位确认序号只有ACK标志为1时才有效,它包含了本机所期望收到的下一个数据序号,确认常常和反向数据一起捎带发送

4、4位首部长度指出了TCP首部的长度,以4字节为单位。需要这个值是因为选项字段的长度是可变的。由于这个字段有4bit,因此TCP最多有60字节的首部,如果没有任何选项字段,首部长度应该为5(20字节)

5、在TCP首部中有6个标志bit,它们中的多个可同时被设置为1,它们告诉了接收端应该如何解释报文的内容,比如一些报文段携带了确认信息、一些报文段携带了紧急数据、一些报文包含建立或关闭连接的请求。6个标志位的意义如下图:

d1072b12-28c3-11ed-ba43-dac502259ad0.png

6、窗口大小字段可看作捎带的另一个例子,窗口通告可以附加在任何报文段中发送。在TCP发送一个报文时,可在窗口字段中填写相应值以通知对方自己的可用缓冲区大小(以字节为单位),报文接收方需要根据这个值来调整发送窗口的大小。这个字段是16bit的,所以通告窗口的最大值为65535字节。窗口字段是实现流量控制的关键字段,当接收方向发送方通知一个大小为0的窗口时,将完全阻止发送方的数据发送

7、16位的紧急指针只有当紧急标志位URG置位时才有效,此时报文中包含了紧急数据,紧急数据始终放在报文段数据开始的地方,而紧急指针定义出了紧急数据在数据区中的结束处,用这个值加上序号字段值就得到了最后一个紧急数据的序号

构造TCP报文段

接下来,我们使用结构体来定义TCP报文,该结构体定义在level-ip的include/tcp.h文件中:

struct tcphdr {    uint16_t sport;    uint16_t dport;    uint32_t seq;    uint32_t ack_seq;    uint8_t rsvd : 4;    uint8_t hl : 4;    uint8_t fin : 1,            syn : 1,            rst : 1,            psh : 1,            ack : 1,            urg : 1,            ece : 1,            cwr : 1;    uint16_t win;    uint16_t csum;    uint16_t urp;    uint8_t data[];} __attribute__((packed));

这个结构体的成员变量,与我们刚才介绍的TCP报文格式的每个字段是一一对应的,这里不再重复解析。

tcp报文发送接口

tcp数据的发送接口tcp_transmit_skb,会在tcp建立可靠连接和数据发送时被多次调用,如三次握手、write()、read()等。在level-ip中,该接口函数保存在src cp_output.c文件中。如下图:

static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, uint32_t seq){    struct tcp_sock *tsk = tcp_sk(sk);    struct tcb *tcb = &tsk->tcb;    struct tcphdr *thdr = tcp_hdr(skb);
    /* No options were previously set */    if (thdr->hl == 0) thdr->hl = TCP_DOFFSET;
    skb_push(skb, thdr->hl * 4);
    thdr->sport = sk->sport;    thdr->dport = sk->dport;    thdr->seq = seq;    thdr->ack_seq = tcb->rcv_nxt;    thdr->rsvd = 0;    thdr->win = tcb->rcv_wnd;    thdr->csum = 0;    thdr->urp = 0;
    if (thdr->hl > 5) {        tcp_write_options(tsk, thdr);    }
    tcp_out_dbg(thdr, sk, skb);
    thdr->sport = htons(thdr->sport);    thdr->dport = htons(thdr->dport);    thdr->seq = htonl(thdr->seq);    thdr->ack_seq = htonl(thdr->ack_seq);    thdr->win = htons(thdr->win);    thdr->csum = htons(thdr->csum);    thdr->urp = htons(thdr->urp);    thdr->csum = tcp_v4_checksum(skb, htonl(sk->saddr), htonl(sk->daddr));        return ip_output(sk, skb);}

第12行:设置源端口号

第13行:设置目标端口号

第14行:自己发送的数据的起始序号

第15行:通知对方期望接收的下一字节的序号

在IP协议中,会对每个数据报进行编号,而在TCP中没有报文编号的概念,因为它的目标是数据流传输,数据流由连续的字节流组成,尽管在上层可能用各种各样的数据结构和格式来描述数据,但在TCP看来,数据都是字节流。TCP把一个连接中的所有数据字节都进行了编号,当然两个方向上的编号是彼此独立的,编号的初始值由发送数据的一方随机选取,编号取值是0~2^32-1,比如发送方选择的起始编号为20,且将要发送的数据长度为800字节,那么字节编号将覆盖20到819的范围。当所有字节被编上号后,字节流会被分装在若干个TCP报文中发送,此时TCP报文首部的字段能够记录它所运载数据的起始编号以及运载数据的长度。在接收端,可以根据这些信息对报文中数据进行排序和确认。例如,将上述的800字节放在三个报文段中来发送,前两个报文段各装载了300字节的数据,最后一个报文装载了200字节的数据,则三个报文段携带的数据情况如下:

报文段1:起始序号:20,数据长度:300,序号范围:20~319

报文段2:起始序号:320,数据长度:300,序号范围:320~619

报文段3:起始序号:620,数据长度:200,序号范围:620~819

接收方通过确认的机制来告诉发送方数据的接收状况,这是通过向发送方返回一个确认号来完成的,确认号标识出自己期望接收到的下一个字节的编号,例如在上面的例子中,如果接收方接收到了报文段1,则它返回给发送方的确认号为320,表示自己期望收到数据编号为320的数据;确认号是累计的,即如果发送方接收到确认号的值为620,说明接收方已经正确接收了620编号以前的所有数据,接收方可能是在收到两个报文段后才发送的一个确认;还有一种情况,如果接收方只收到报文段1和3,那么它返回的确认号仍然是320,确认号始终表示接收方期望的下一字节数据,尽管可能已有更高编号的数据被收到

第16行:保留位

第17行:设置滑动窗口大小

在数据发送端,所有数据流按照顺序被组织在发送缓存中,什么时候发送数据以及发送的多少是由滑动窗口决定的,使用窗口滑动的概念可以达到很好的流量控制效果和拥塞控制效果。如下图所示,滑动窗口可以看成定义在数据缓冲上的一个窗口,缓冲中存放了从应用程序传递过来的待发送数据,窗口能在缓冲上滑动,滑动窗口状态决定了TCP能发送哪些数据。滑动窗口较大时, TCP可以在收到确认之前一次性地发送几个报文段,这样,可以使得网络总是处于忙碌状态,提高网络的吞吐率。当发送窗口较小时,能够被发送的报文很少,在极端情况下,当发送窗口为0时,没有任何报文能被发送,减少报文的发送是进行拥塞控制的最直接手段。因此,流量控制和拥塞控制的本质在于对发送窗口的合理调节

第18行:校验和设置为0

第19行:紧急指针设置为0,

TCP采用了缓冲机制来保证协议的高效性,在数据发送时,TCP内核将延迟小分组数据的交付,它将等待足够长的时间,以期待接收更多的应用数据,最后再一起发送;在接收数据时,TCP首先是先将数据放在接收缓冲中,只有在应用程序准备就绪或者TCP协议认为时机恰当的时候,数据才会被交付给应用程序。上述缓冲机制是出于对网络性能提升的考虑,但是它可能会妨碍某些应用程序的使用。例如,两个应用程序进行交互式通信,一端应用程序打算把用户键入的字符发送给另一端应用程序,并且期望对方立即响应。在这种情况下,TCP的数据收集与延迟发送、缓冲递交会给用户带来很不好的体验。为了满足交互式应用场合的需求,TCP可以采用下面的方式解决这个问题。发送方应用程序向TCP传递数据时,请求推送(push)操作,这时,TCP协议不会等待发送缓冲区被填满,而是直接将报文发送出去。同时,被推送出去的报文首部中推送位(PSH)将被置1,接收端在收到这类报文时,会尽快将数据递交给应用程序,而不必缓冲更多的数据再递交

tcp接收接口

tcp数据接收接口为tcp_in()函数。该函数在ip数据帧读取接口ip_rcv()函数中调用。该函数保存在srcip_input.c文件中,我们来了解一下这个函数,如下图:

void tcp_in(struct sk_buff *skb){    struct sock *sk;    struct iphdr *iph;    struct tcphdr *th;
    iph = ip_hdr(skb);    th = (struct tcphdr*) iph->data;
    tcp_init_segment(th, iph, skb);        sk = inet_lookup(skb, th->sport, th->dport);
    if (sk == NULL) {        print_err("No TCP socket for sport %d dport %d
",                  th->sport, th->dport);        free_skb(skb);        return;    }    socket_wr_acquire(sk->sock);
    tcp_in_dbg(th, sk, skb);    /* if (tcp_checksum(iph, th) != 0) { */    /*     goto discard; */    /* } */    tcp_input_state(sk, th, skb);
    socket_release(sk->sock);}

第7行:从sk_buff中读取ip首部信息

第8行:从ip数据包的data区域中读取tcp数据信息

第10行:进行关键字段的大小端变换和计算应答序号等

第12行:根据通信端口,查找管理该次tcp连接的结构体sock

第20行:获取该结构体的读写锁

第26行:数据递交给tcp_input_state()函数处理,该函数会进行tcp通信状态的变化,以及获取把整理好的有序报文递交给应用程序。

总结

在对可靠性要求很高的场合下,使用TCP提供的传输性能是很合适的,TCP将两个进程间传递的数据看作数据流的形式,两个先后发出的TCP报文虽然在网络中也是互不相关的传输,但是它们携带的数据之间却具有关联关系,因为TCP给传输的每个字节数据一个唯一的编号。在接收端,所有数据将按照编号被顺序组织起来,当所有数据接收成功后,TCP才把数据递交给应用层。应用层不必担心报文的乱序、重复、丢失等问题,TCP采用正面确认以及超时重传等机制保证数据流能全部正确到达。

但是,我们这篇文章还没涉及到这部分的内容,但是我们已经找到分析的突破口,那就是上面所提到的tcp_input_state()函数,以后我们将在该函数里面剖析tcp种种可靠性机制。

审核编辑:彭静
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 接口
    +关注

    关注

    33

    文章

    7648

    浏览量

    148523
  • 数据
    +关注

    关注

    8

    文章

    6514

    浏览量

    87610
  • TCP
    TCP
    +关注

    关注

    8

    文章

    1272

    浏览量

    78305

原文标题:level-ip之tcp数据包接口封装

文章出处:【微信号:zhuyandz,微信公众号:FPGA之家】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    Modbus TCP通信报文解析

    Modbus TCP是在TCP/IP网络上运行的Modbus的实现,旨在允许Modbus ASCII/RTU协议在基于TCP/IP的网络上传输。
    发表于 09-20 15:55 1.1w次阅读
    Modbus <b class='flag-5'>TCP</b>通信<b class='flag-5'>报文</b>解析

    TC387模块CAN0的节点2作为CANA使用,CANA既收不到报文也发不出报文何解决?

    的节点2寄存器TX->TRP和TX->BRP在第一次发送报文时被置为1,后续报文无法继续发送。请问如何解决?
    发表于 02-19 06:43

    LWIP TCP报文基础知识及其LWIP中TCP协议的实现

    LWIP TCP报文基础TCP协议(Transmission Control Protocol)传输控制协议在LWIP协议栈中占据了大半的代码,它是最常见的传输层协议,也是最稳定的传输层协议,很多
    发表于 10-18 14:54

    TCP/IP协议,TCP/IP协议内容和作用是什么?

    TCP/IP协议,TCP/IP协议内容和作用是什么? TCP/IP是一组协议的代名词,它还包括许多别的协议,组成了TCP/IP协议簇。一般
    发表于 03-19 13:55 5696次阅读

    ICMP控制报文协议

    TCP-IP详解卷2 ICMP控制报文协议,学习TCP很好的资料。欢迎下载。
    发表于 05-09 14:13 0次下载

    TCP/IP协议栈之路由器简要分析

    读完这个系列的第一篇浅谈TCP/IP协议栈(一)入门知识和第二篇浅谈TCP/IP协议栈(二)IP地址,在第一篇中,可能我对协议栈中这个栈的解释有问题,栈在数据结构中是一种先进后出的常见结构,而在整个
    发表于 10-10 11:46 1次下载

    icmp报文和ip报文分析

    . ICMP允许主机或路由报告差错情况和提供有关异常情况。ICMP是因特网的标准协议,但ICMP不是高层协议,而是IP层的协议。通常ICMP报文被IP层或更高层协议(TCP或UDP)使用。一些ICMP报文把差错
    发表于 11-03 09:09 9452次阅读
    icmp<b class='flag-5'>报文</b>和ip<b class='flag-5'>报文</b>分析

    tcp报文格式详解

    TCP(Transmission ControProtocol)传输控制协议是一种面向连接的、可靠的、基于字节流的传输层协议。TCP报文TCP层传输的数据单元,也称为
    发表于 12-08 11:11 3.2w次阅读
    <b class='flag-5'>tcp</b><b class='flag-5'>报文</b>格式详解

    关于tcp协议栈中rst报文的seq跳变问题

    导致内核协议栈发送了一个rst报文,而rst报文选取seq的时候,并不是选取的确定已经发送的seq,而是当前连接已经用掉的seq,也就是当前seq,哪怕这个报文没有收到回复,也会使用。
    的头像 发表于 07-27 15:26 4784次阅读
    关于<b class='flag-5'>tcp</b>协议栈中rst<b class='flag-5'>报文</b>的seq跳变问题

    TCP IP相关知识的详细资料说明免费下载

    本文档的主要内容详细介绍的是TCP IP相关知识的详细资料说明免费下载。主要内容包括了:TCP报文格式,
    发表于 12-05 11:19 19次下载
    <b class='flag-5'>TCP</b> IP相关知识的详细资料说明免费下载

    tcp数据包接口封装的介绍

    TCP报文格式 TCP协议有着自己的数据包格式,这里把TCP的数据包称为报文段(segment),TCP
    的头像 发表于 03-22 09:39 2986次阅读
    <b class='flag-5'>tcp</b>数据包接口封装的介绍

    详解TCP报文的头部结构

    和两个端口号。一个TCP连接通常分为三个阶段:连接、数据传输、退出(关闭)。通过三次握手建立一个链接,通过四次挥手来关闭一个连接。当一个连接被建立或被终止时,交换的报文段只包含TCP头部,而没有数据。
    的头像 发表于 05-05 15:08 5756次阅读

    tcp报文段首部的结构分析

    `TCP`报文段如`APR`报文、`IP`数据报一样,也是由`首部`与`数据区域`组成,`TCP首部`内容很丰富,各个字段都有特定的含义,一
    的头像 发表于 02-14 10:32 1588次阅读
    <b class='flag-5'>tcp</b><b class='flag-5'>报文</b>段首部的结构分析

    接收UDP报文的过程

    最近工作中遇到某个服务器应用程序 UDP 丢包,在排查过程中查阅了很多资料,总结出来这篇文章,供更多人参考。 在开始之前,我们先用一张图解释 linux 系统接收网络报文的过程。 首先网络报文通过
    的头像 发表于 11-11 11:22 479次阅读
    接收UDP<b class='flag-5'>报文</b>的过程

    CCLink转Modbus TCP网关_MODBUS报文配置

    兴达易控CCLink转Modbus TCP网关是一种功能强大的设备,可实现两个不同通信协议之间的无缝对接。它能够将CCLink协议转换为Modbus TCP协议,并通过报文配置实现灵活的通信设置
    的头像 发表于 11-15 09:05 251次阅读
    CCLink转Modbus <b class='flag-5'>TCP</b>网关_MODBUS<b class='flag-5'>报文</b>配置