tcp 报文 java封装_Java EE网络基础(1)—TCP/IP协议分层模型以及数据的封装和分用...

简单介绍了TCP/IP协议族的基本概念以及分层模型,以及数据传输的封装和分用!

1 Java EE网络基础的概述

虽然Java是一种高级计算机语言,平时编程的时候很难接触到网络协议等比较底层的知识,但是在Web应用开发过程中难免和Web网络打交道,具备一定的网络基础知识对于扩充Java程序员的知识面来说是非常不错的。

在系统的学习Java EE之前,简单的学习一些重要的网络协议基础知识是非常有必要的。特别是TCP/IP协议族,以及内部的TCP协议、IP协议、HTTP协议。

我们首先学习TCP/IP协议族,以及分层模型,以及数据传输时的封装和分用。

计算机与网络设备要相互通信,双方就必须基于相同的方式。比如,如何探测到通信目标、由哪一边先发起通信、使用哪种语言进行通信、怎样结束通信等规则都需要事先确定。不同的硬件、操作系统之间的通信,所有的这一切都需要一种规则。而我们就把这种规则称为协议(protocol)。

我们通常使用的网络(包括互联网)是在TCP/IP协议族的基础上运作,或者说TCP/IP协议族是一个包含了一系列构成互联网基础的相关网络协议的集合。简单的说,TCP/IP 是在 IP 协议的通信过程中,使用到的协议的统称。

2 TCP/IP分层模型

TCP/IP 协议族里重要的知识点之一就是TCP/IP分层模型。 把 TCP/IP 层次化是有好处的。比如,如果互联网只由一个协议统筹,某个地方需要改变设计时,就必须把所有部分整体替换掉。而分层之后只需把变动的层替换掉即可。把各层之间的接口部分规划好之后,每个层次内部的设计就能够自由改动了。

值得一提的是,层次化之后,设计也变得相对简单了。处于应用层上的应用可以只考虑分派给自己的任务,而不需要弄清对方在地球上哪个地方、对方的传输路线是怎样的、是否能确保传输送达等问题。

分层模型显示协议如何在每个层上工作,以及层如何与它们上方或下方的层交互。因此,这对于后来学习的人来说,能够更好地理解各种协议以及它们如何相互协作。

在现代计算机网络中,常备使用的分层模型是两个:

OSI模型(Open System Interconnection Reference Model,开放式通信系统互联参考模型),是国际标准化组织(ISO)提出的一个试图使各种计算机在世界范围内互连为网络的标准框架,简称OSI。OSI是制定的适用于全世界计算机网络的统一标准,是一种理想状态,因此它的结构复杂,层次间存在严格的调用关系,一共有七层模型。

TCP/IP模型(Transmission Control Protocol/Internet Protocol,传输控制协议/互联网协议),它使用更少的四层模型,是目前应用最广泛的协议,已成为事实上的国际标准。

TCP/IP四层模型和OSI七层模型有比较明确对应的关系:

OSI七层模型

TCP/IP四层模型

应用层:为应用软件提供各种通用应用服务

应用层

表示层:对接收的数据进行解释、加密与解密、压缩与解压缩等

会话层:发起、接受、终止会话(连接)

传输层:为端到端连接提供传输服务

传输层

网络层:IP地址(如例192.168.0.1)的封装与解封装服务,寻找传输路线和选择路由

网络层

数据链路层:MAC地址(网卡的地址)的封装与解封装服务

数据链路层

物理层:将比特流中的0、1,与电压的高低、灯光的闪灭之间的转换,以及规定连接器、网线等物理接口、规格

TCP/IP 协议族各层的作用如下:

应用层

预存了各类通用的应用服务,保证用户(应用程序)能够正常运行。不同的的应用程序会根据自己的需要来使用应用层的不同协议。

文件传输可以使用FTP(File Transfer Protocol)文件传输协议;万维网请求-响应传输超文本可以使用HTTP(HyperText Transfer Protocol)超文本传输协议,平时浏览网页时都是用这个协议;远程登录服务可以使用TELNET协议,用户可在本地计算机上控制远程主机(Web服务器);用户发送电子邮件可以使用SMTP(Simple Mail Transfer Protocol)简单邮件传输协议;从服务器接收电子邮件可以使用POP3(Post Office Protocol 3)邮局协议;用户想要通过域名方便的访问服务器则可以使用DNS(Domain Name System)域名系统服务。

传输层

传输层主要对上层应用层的应用程序提供处于网络连接中的两台计算机之间的数据传输。

在传输层主要包括两个性质不同的协议:TCP(Transmission Control Protocol,传输控制协议)和 UDP(User Data Protocol,用户数据报协议)。

网络层(又名网络互连层)

该层主要负责通过IP寻找地址和路由选择。或者说,与对方计算机之间通过多台计算机或网络设备进行传输时,网络层所起的作用就是在众多的选项内选择一条传输路线。比如路由器就工作在网络层。

包括IP (Internet Protocol)协议,即互联网协议,以及它的子协议ICMP(Internet Control Message Protocol),即Internet控制报文协议。

链路层(又名数据链路层,网络接口层)

包括了数据链路层和物理层。负责物理层面上的互联的、节点间的通信传输,包括查找物理(MAC)地址,数据成帧,规定了电平、速度和电缆针脚,最终通过物理介质传输比特流。

3 分层信息传输

利用 TCP/IP 协议族进行网络通信时,会通过分层顺序与对方进行通信。发送端从应用层往下走,接收端则从数据链路层往上走。

用 HTTP 举例来说明,首先作为发送端的客户端在应用层 (HTTP 协议)发出一个想看某个 Web 页面的 HTTP 请求。接着,为了传输方便,在传输层(TCP 协议)把从应用层处收到的数据(HTTP 请求报文)进行分割,并在各个报文上打上标记序号及端口号后转发给网络层。在网络层(IP 协议),增加作为通信目的地的 MAC 地址后转发给链路层。这样一来,发往网络的通信请求就准备齐全了。接收端的服务器在链路层接收到数据,按序往上层发送,一直到应用层。当传输到应用层,才能算真正接收到由客户端发送过来的 HTTP请求。

688e490727009553ef03c663b0b9d094.png

4 数据的封装和分用

实际上,当应用程序用TCP传送数据时,数据被送入协议栈中,然后逐个通过每一层直到被当作一串比特流送入网络,其中每一层对收到的数据都要增加一些首部信息(以太网包括尾部),这个过程称作封装(Encapsulation)。

85a2149b2672a6ab81d5620859d3b118.png

每一层数据是由上一层数据+本层首部信息组成的,其中每一层的数据,称为本层的协议数据单元,即PDU(Protocol Data Unit);

应用层:用户数据在应用层中会被加密、编码传输;

传输层:在传输层中,经过TCP协议封装的数据将会加上TCP首部,此时的PDU被称为TCP报文段或简称为TCP段(TCP segment)。经过UDP协议封装的数据将会加上UDP首部,此时的PDU被称为UDP数据报(UDP datagram)。该层的数据单元也可以统称为断(segment)。TCP/UDP首部主要包含源进程端口号和目的进程端口号。

网络层:经过IP协议封装的PDU被称为IP数据报(IP datagram),也被称为包(packet)。IP首部主要包含源IP地址和目的IP地址,以及上层传输层协议的类型。

链路层:链路层中PDU被进一步封装为帧(frame),传输媒介不同,帧的类型也不同,比如通过以太网传输的就是以太网帧,而令牌环网上传输的则是令牌环帧。以太网帧首部主要包含源MAC地址和目的MAC地址,以及帧类型(用于确定上层协议类型)。最终,帧被以比特流(BitTorrent)的形式通过物理传输介质进行传输给目的主机,此时数据传输的单位就是比特(bit)。

当目的主机收到一个以太网数据帧时,并且通过匹配帧中的MAC地址发现目的地就是本机,那么数据就开始从协议栈中由底向上升,同时去掉各层协议加上的报文首部。每层协议盒都要去检查报文首部中的协议标识,以确定接收数据的上层协议。这个过程称作分用(Demultiplexing)。

847637c66576e640203643556d17b4cb.png

如有需要交流,或者文章有误,请直接留言。另外希望点赞、收藏、关注,我将不间断更新各种Java学习博客!

本文同步分享在 博客“L-Java”(CSDN)。

如有侵权,请联系 support@oschina.cn 删除。

本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要经过TCP/IP协议栈发送802.1Q报文,需要在数据包中添加802.1Q VLAN标签,并将其发送到网络接口。以下是一个简单的示例程序,演示如何使用TCP/IP协议栈发送带有802.1Q VLAN标签的数据包: ```c++ #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <arpa/inet.h> #include <net/ethernet.h> #include <net/if.h> #include <netinet/in.h> #include <netinet/ip.h> #include <netinet/tcp.h> #include <netinet/udp.h> #include <netinet/ip_icmp.h> #include <linux/if_packet.h> #include <linux/if_ether.h> #include <linux/if_vlan.h> int main() { int sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (sock < 0) { perror("socket"); exit(1); } char ifname[IFNAMSIZ] = "eth0"; struct ifreq ifr; strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1); if (ioctl(sock, SIOCGIFINDEX, &ifr) < 0) { perror("ioctl"); exit(1); } struct sockaddr_ll addr; memset(&addr, 0, sizeof(addr)); addr.sll_family = AF_PACKET; addr.sll_ifindex = ifr.ifr_ifindex; // 构造802.1Q VLAN标签 struct vlan_tag { uint16_t tci; uint16_t ether_type; } __attribute__((packed)); struct vlan_tag vlan; vlan.tci = htons(0x100); vlan.ether_type = htons(ETH_P_IP); // 构造IP数据包 char ip_packet[ETH_FRAME_LEN]; struct iphdr *iph = (struct iphdr *)ip_packet; iph->version = 4; iph->ihl = 5; iph->tos = 0; iph->tot_len = htons(sizeof(struct iphdr) + sizeof(struct udphdr)); iph->id = htons(0); iph->frag_off = htons(0); iph->ttl = 64; iph->protocol = IPPROTO_UDP; iph->check = htons(0); iph->saddr = inet_addr("192.168.1.100"); iph->daddr = inet_addr("192.168.1.1"); // 构造UDP数据包 char udp_packet[ETH_FRAME_LEN]; struct udphdr *udph = (struct udphdr *)udp_packet; udph->source = htons(1234); udph->dest = htons(5678); udph->len = htons(sizeof(struct udphdr)); udph->check = htons(0); // 将IP头和UDP数据拷贝到数据包中 memcpy(ip_packet + sizeof(struct iphdr), udph, sizeof(struct udphdr)); // 将802.1Q VLAN标签和IP/UDP数据拷贝到数据包中 char packet[ETH_FRAME_LEN]; memcpy(packet, "\xff\xff\xff\xff\xff\xff", ETH_ALEN); memcpy(packet + ETH_ALEN, "\x00\x11\x22\x33\x44\x55", ETH_ALEN); memcpy(packet + 2 * ETH_ALEN, &vlan, sizeof(struct vlan_tag)); memcpy(packet + 2 * ETH_ALEN + sizeof(struct vlan_tag), ip_packet, sizeof(struct iphdr) + sizeof(struct udphdr)); // 发送数据包 if (sendto(sock, packet, 2 * ETH_ALEN + sizeof(struct vlan_tag) + sizeof(struct iphdr) + sizeof(struct udphdr), 0, (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("sendto"); exit(1); } close(sock); return 0; } ``` 该程序使用socket API创建了一个PF_PACKET类型的socket,并将其绑定到网络接口上。然后,程序构造了一个带有802.1Q VLAN标签的IP/UDP数据包,并将其封装成一个以太网帧。在构造802.1Q VLAN标签时,程序使用了vlan_tag结构体来定义标签格式,并将其添加到数据包的头部。最后,程序调用sendto函数将数据包发送到目标MAC地址。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值