您的位置:首页 > 软件问答

数据包发送少收到多(弱电工程人员必备的常用网络命令)

导读数据包发送少收到多文章列表:1、弱电工程人员必备的常用网络命令2、手把手教你IP数据包接收过程超详细3、一分钟实现内网穿透ngrok服务器搭建4、Si4463无线模块收发超长数据

数据包发送少收到多文章列表:

数据包发送少收到多(弱电工程人员必备的常用网络命令)

弱电工程人员必备的常用网络命令

前言:

现在的弱电工程可以成为网络工程了,基本上全是数字化、网络化了,所以我们需要掌握一定的网络知识,今天就分享一些常用的网络命令,设备调试、维护的时候用的到网络命令。

ping命令

ping是个使用频率极高的实用程序,主要用于确定网络的连通性。这对确定网络是否正确连接,以及网络连接的状况十分有用。简单的说,ping就是一个测试程序,如果ping运行正确,大体上就可以排除网络访问层、网卡、Modem的输入输出线路、电缆和路由器等存在的故障,从而缩小问题的范围。

ping能够以毫秒为单位显示发送请求到返回应答之间的时间量。如果应答时间短,表示数据报不必通过太多的路由器或网络,连接速度比较快。ping还能显示TTL(Time To Live,生存时间)值,通过TTL值可以推算数据包通过了多少个路由器。

1、命令格式

ping 主机名

ping 域名

ping IP地址

如图所示,使用ping命令检查到IP地址210.43.16.17的计算机的连通性,该例为连接正常。共发送了四个测试数据包,正确接收到四个数据包。

2、ping命令的基本应用

一般情况下,用户可以通过使用一系列ping命令来查找问题出在什么地方,或检验网络运行的情况。下面就给出一个典型的检测次序及对应的可能故障:

① ping 127.0.0.1

如果测试成功,表明网卡、TCP/IP协议的安装、IP地址、子网掩码的设置正常。如果测试不成功,就表示TCP/IP的安装或设置存在有问题。

② ping 本机IP地址

如果测试不成功,则表示本地配置或安装存在问题,应当对网络设备和通讯介质进行测试、检查并排除。

③ ping局域网内其他IP

如果测试成功,表明本地网络中的网卡和载体运行正确。但如果收到0个回送应答,那么表示子网掩码不正确或网卡配置错误或电缆系统有问题。

④ ping 网关IP

这个命令如果应答正确,表示局域网中的网关路由器正在运行并能够做出应答。

⑤ ping 远程IP

如果收到正确应答,表示成功的使用了缺省网关。对于拨号上网用户则表示能够成功的访问Internet(但不排除ISP的DNS会有问题)。

⑥ ping localhost

local host是系统的网络保留名,它是127.0.0.1的别名,每台计算机都应该能够将该名字转换成该地址。否则,则表示主机文件(/Windows/host)中存在问题。

⑦ ping www.yahoo.com(一个著名网站域名)

对此域名执行Ping命令,计算机必须先将域名转换成IP地址,通常是通过DNS服务器。如果这里出现故障,则表示本机DNS服务器的IP地址配置不正确,或它所访问的DNS服务器有故障。

如果上面所列出的所有ping命令都能正常运行,那么计算机进行本地和远程通信基本上就没有问题了。但是,这些命令的成功并不表示你所有的网络配置都没有问题,例如,某些子网掩码错误就可能无法用这些方法检测到。

3、ping命令的常用参数选项

ping IP -t:连续对IP地址执行ping命令,直到被用户以Ctrl C中断。

ping IP -l 2000:指定ping命令中的特定数据长度(此处为2000字节),而不是缺省的32字节。

ping IP -n 20:执行特定次数(此处是20)的ping命令。

ipconfig命令

ipconfig实用程序可用于显示当前的TCP/IP配置的设置值。这些信息一般用来检验人工配置的TCP/IP设置是否正确。

而且,如果计算机和所在的局域网使用了动态主机配置协议DHCP,使用ipconfig命令可以了解到你的计算机是否成功地租用到了一个IP地址,如果已经租用到,则可以了解它目前得到的是什么地址,包括IP地址、子网掩码和缺省网关等网络配置信息。

下面给出最常用的选项:

1、ipconfig

当使用不带任何参数选项ipconfig命令时,显示每个已经配置了的接口的IP地址、子网掩码和缺省网关值。

2、ipconfig /all

当使用all选项时,ipconfig能为DNS和WINS服务器显示它已配置且所有使用的附加信息,并且能够显示内置于本地网卡中的物理地址(MAC)。如果IP地址是从DHCP服务器租用的,ipconfig将显示DHCP服务器分配的IP地址和租用地址预计失效的日期。图为运行ipconfig /all命令的结果窗口。

3、ipconfig /release和ipconfig /renew

这两个附加选项,只能在向DHCP服务器租用IP地址的计算机使用。如果输入ipconfig /release,那么所有接口的租用IP地址便重新交付给DHCP服务器(归还IP地址)。如果用户输入ipconfig /renew,那么本地计算机便设法与DHCP服务器取得联系,并租用一个IP地址。大多数情况下网卡将被重新赋予和以前所赋予的相同的IP地址。

ARP命令(地址转换协议)

ARP是TCP/IP协议族中的一个重要协议,用于确定对应IP地址的网卡物理地址。

使用arp命令,能够查看本地计算机或另一台计算机的ARP高速缓存中的当前内容。此外,使用arp命令可以人工方式设置静态的网卡物理地址/IP地址对,使用这种方式可以为缺省网关和本地服务器等常用主机进行本地静态配置,这有助于减少网络上的信息量。

按照缺省设置,ARP高速缓存中的项目是动态的,每当向指定地点发送数据并且此时高速缓存中不存在当前项目时,ARP便会自动添加该项目。

常用命令选项:

① arp –a:用于查看高速缓存中的所有项目。

② arp -a IP:如果有多个网卡,那么使用arp -a加上接口的IP地址,就可以只显示与该接口相关的ARP缓存项目。

③ arp -s IP 物理地址:向ARP高速缓存中人工输入一个静态项目。该项目在计算机引导过程中将保持有效状态,或者在出现错误时,人工配置的物理地址将自动更新该项目。

④ arp -d IP:使用本命令能够人工删除一个静态项目。

traceroute命令

掌握使用traceroute命令测量路由情况的技能,即用来显示数据包到达目的主机所经过的路径。

traceroute命令的基本用法是,在命令提示符后键入“tracert host_name”或“tracert ip_address”,其中,tracert是traceroute在Windows操作系统上的称呼。

输出有5列:

第一列是描述路径的第n跳的数值,即沿着该路径的路由器序号;

第二列是第一次往返时延;

第三列是第二次往返时延;

第四列是第三次往返时延;

第五列是路由器的名字及其输入端口的IP地址。

如果源从任何给定的路由器接收到的报文少于3条(由于网络中的分组丢失),traceroute在该路由器号码后面放一个星号,并报告到达那台路由器的少于3次的往返时间。

此外,tracert命令还可以用来查看网络在连接站点时经过的步骤或采取哪种路线,如果是网络出现故障,就可以通过这条命令查看出现问题的位置。

思 考:

【测试大型网络的路由】:

(1)多尝试几次“ping www.sina.com.cn”操作,比较得到的新浪网的IP地址。如果两次ping得到的IP地址不同,试考虑其中的原因(如考虑到负载均衡)。然后,针对这些不同的IP地址,执行“tracert ip_address”命令,观察分析输出的结果是否有差异。

(2)对于大型网络中的某站点进行traceroute测试,记录测试结果。观察其中是否出现第n跳的时延小于第n-1跳的时延情况。试分析其中原因(提示:可分别考虑时延的各个构成成分在总时延中所起的作用)。

(3)在一天的不同时段内,用traceroute程序多次测试从固定主机到远程固定IP地址的主机的路由。试分析比较测量数据,观察该路由是否有变化?如果有变化,该变化频繁吗?

route命令

大多数主机一般都是驻留在只连接一台路由器的网段上。由于只有一台路由器,因此不存在选择使用哪一台路由器将数据包发送到远程计算机上去的问题,该路由器的IP地址可作为该网段上所有计算机的缺省网关。

但是,当网络上拥有两个或多个路由器时,用户就不一定想只依赖缺省网关了。实际上可能想让某些远程IP地址通过某个特定的路由器来传递,而其他的远程IP则通过另一个路由器来传递。在这种情况下,用户需要相应的路由信息,这些信息储存在路由表中,每个主机和每个路由器都配有自己独一无二的路由表。大多数路由器使用专门的路由协议来交换和动态更新路由器之间的路由表。但在有些情况下,必须人工将项目添加到路由器和主机上的路由表中。route命令就是用来显示、人工添加和修改路由表项目的。该命令可使用如下选项:

1、route print

本命令用于显示路由表中的当前项目,在单个路由器网段上的输出结果如图所示。

2、route add

使用本命令,可以将路由项目添加给路由表。

例如,如果要设定一个到目的网络209.99.32.33的路由,其间要经过5个路由器网段,首先要经过本地网络上的一个路由器IP为202.96.123.5,子网掩码为255.255.255.224,那么用户应该输入以下命令:

route add 209.99.32.33 mask 255.255.255.224 202.96.123.5 metric 5

3、route change

可以使用本命令来修改数据的传输路由,不过,用户不能使用本命令来改变数据的目的地。下面这个例子将上例路由改变采用一条包含3个网段的路径:

route add 209.99.32.33 mask 255.255.255.224 202.96.123.250 metric 3

4、route delete

使用本命令可以从路由表中删除路由。例如:route delete 209.99.32.33

nslookup命令

命令nslookup的功能是查询任何一台机器的IP地址和其对应的域名。它通常需要一台域名服务器来提供域名。如果用户已经设置好域名服务器,就可以用这个命令查看不同主机的IP地址对应的域名。

(1)在本地机上使用nslookup命令查看本机的IP及域名服务器地址。

直接键入命令,系统返回本机的服务器名称(带域名的全称)和IP地址,并进入以“>”为提示符的操作命令行状态;键入“?”可查询详细命令参数;若要退出,需键入exit。

(2)查看www.haut.edu.cn的IP。在提示符后输入要查询的IP地址或域名并回车即可。

nbtstat命令

使用nbtstat命令可以查看计算机上网络配置的一些信息。使用这条命令还可以查找出别人计算机上一些私人信息。如果想查看自己计算机上的网络信息,可以运行nbtstat -n,可以得到你所在的工作组,计算机名以及网卡地址等等;想查看网络上其他的电脑情况,就运行nbtstat -a *.*.*.*,此处的*.*.*.*用IP地址代替就会返回得到那台主机上的一些信息。

netstat命令

学习使用netstat命令,以了解网络当前的状态。

netstat命令能够显示活动的TCP连接、计算机侦听的端口、以太网统计信息、IP路由表、IPv4统计信息(对于IP、ICMP、TCP和UDP协议)以及IPv6统计信息(对于IPv6、ICMPv6、通过IPv6的TCP以及UDP协议)。使用时如果不带参数,netstat显示活动的TCP连接。

下面给出netstat的一些常用选项:

① netstat –a:-a选项显示所有的有效连接信息列表,包括已建立的连接(ESTABLISHED),也包括监听连接请求(LISTENING)的那些连接。

② netstat –n:以点分十进制的形式列出IP地址,而不是象征性的主机名和网络名。

③ netstat -e:-e选项用于显示关于以太网的统计数据。它列出的项目包括传送的数据包的总字节数、错误数、删除数、数据包的数量和广播的数量。这些统计数据既有发送的数据包数量,也有接收的数据包数量。使用这个选项可以统计一些基本的网络流量。

④ netstat -r:-r选项可以显示关于路由表的信息,类似于route print命令时看到的信息。除了显示有效路由外,还显示当前有效的连接。

上图显示的是一个路由表,其中:Network Destination表示目的网络,0.0.0.0表示不明网络,这是设置默认网关后系统自动产生的;127.0.0.0表示本机网络地址,用于测试;224.0.0.0表示组播地址;255.255.255.255表示限制广播地址;Netmask表示网络掩码,Gateway表示网关,Interface表示接口地址,Metric表示路由跳数。

⑤ netstat -s:-s选项能够按照各个协议分别显示其统计数据。这样就可以看到当前计算机在网络上存在哪些连接,以及数据包发送和接收的详细情况等等。如果应用程序(如Web浏览器)运行速度比较慢,或者不能显示Web页之类的数据,那么可以用本选项来查看一下所显示的信息。仔细查看统计数据的各行,找到出错的关键字,进而确定问题所在。

net命令

了解Net服务的功能,学会使用Net服务命令解决有关网络问题。

在命令行键入net help command,可以在命令行获得net命令的语法帮助。例如,要得到关于net accounts命令的帮助信息,可键入“net help accounts”。

所有net命令都可以使用/y和/n命令行选项。例如,net stop server命令用于提示用户确认停止所有依赖的服务器服务,net stop server/y表示确认停止并关闭服务器服务。

下表列出了基本的NET命令及它们的作用:

NET命令的执行结果有许多与其它Windows Server 2003管理工具所得到的结果相似。但是,NET命令可以在一个地方提供所有信息,并可以把结果重定向到打印机或一个标准的文本文件中。

许多服务所使用的网络命令都以net开头,这些net命令有一些公用属性。要看到所有可用的net命令的列表,可以在命令提示符窗口键入net/?得到。

手把手教你IP数据包接收过程超详细

在《深度剖析Linux 网络中断下半部处理(看完秒懂)》一文中介绍过,当网卡接收到网络数据包后,会由网卡驱动通过调用 netif_rx 函数把数据包添加到待处理队列中,然后唤起网络中断下半部处理。

而网络中断下半部处理由 net_rx_action 函数完成的,其主要功能就是从待处理队列中获取一个数据包,然后根据数据包的网络层协议类型来找到相应的处理接口来处理数据包。我们通过 图1 来展示 net_rx_action 函数的处理过程:

图1 net_rx_action 处理过程

更多linux内核视频教程文档资料免费领取后台私信【内核】自行获取.

网络层的处理接口保存在 ptype_base 数组中,其元素的类型为 packet_type 结构,而索引为网络层协议的类型,所以通过网络层协议的类型就能找到对应的处理接口,我们先来看看 packet_type 结构的定义:

struct packet_type{ unsigned short type; // 网络层协议类型 struct net_device *dev; // 绑定的设备对象, 一般设置为NULL int (*func)(struct sk_buff *, struct net_device *, struct packet_type *); // 处理接口 void *data; // 私有数据域, 一般设置为NULL struct packet_type *next;};

ptype_base 数组的定义如下:

static struct packet_type *ptype_base[16];

从 ptype_base 数组的定义可知,其只有 16 个元素,那么如果内核超过 16 种网络层协议怎么办?我们可以从网络层处理接口的注册函数 dev_add_pack 中找到答案:

void dev_add_pack(struct packet_type *pt){ int hash; br_write_lock_bh(BR_NETPROTO_LOCK); // 如果类型为ETH_P_ALL, 表示需要处理所有协议的数据包 if (pt->type == htons(ETH_P_ALL)) { netdev_nit ; pt->next = ptype_all; ptype_all = pt; } else { // 对网络层协议类型与15进行与操作, 得到一个 0 到 15 的整数 hash = ntohs(pt->type) & 15; // 通过 next 指针把冲突的处理接口连接起来 pt->next = ptype_base[hash]; ptype_base[hash] = pt; } br_write_unlock_bh(BR_NETPROTO_LOCK);}

从 dev_add_pack 函数的实现可知,内核把 ptype_base 数组当成了哈希表,而键值就是网络层协议类型,哈希函数就是对协议类型与 15 进行与操作。如果有冲突,就通过 next 指针把冲突的处理接口连接起来。

我们再来看看 IP 协议是怎样注册处理接口的,如下代码:

/* /net/ipv4/ip_output.c */static struct packet_type ip_packet_type = { __constant_htons(ETH_P_IP), NULL, ip_rcv, // 处理 IP 协议数据包的接口 (void*)1, NULL, }; void __init ip_init(void){ // 注册 IP 协议处理接口 dev_add_pack(&ip_packet_type); ... }

从上面的代码可以看到,在 ip_init 函数中调用了 dev_add_pack(&ip_packet_type) 函数来注册了 IP 协议的处理接口,而 IP 协议的处理接口为 ip_rcv 函数。

我们再看看 net_rx_action 函数的处理:

static void net_rx_action(struct softirq_action *h){ ... for (;;) { ... { struct packet_type *ptype, *pt_prev; unsigned short type = skb->protocol; // 网络层协议类型 pt_prev = NULL; ... // 从 ptype_base 数组中查找网络层处理接口 for (ptype = ptype_base[ntohs(type) & 15]; ptype; ptype = ptype->next) { if (ptype->type == type // 如果协议类型匹配 && (!ptype->dev || ptype->dev == skb->dev)) { if (pt_prev) { atomic_inc(&skb->users); // 如处理IP协议数据包的 ip_rcv() 函数 pt_prev->func(skb, skb->dev, pt_prev); } pt_prev = ptype; } } ... } ... } ... return;}

现在就非常清晰了,就是根据数据包的网络层协议类型,然后从 ptype_base 数组中找到对应的处理接口处理数据包,如 IP 协议的数据包就调用 ip_rcv 函数处理。

处理IP数据包

通过上面的分析,我们知道当内核接收到一个 IP 数据包后,会调用 ip_rcv 函数处理这个数据包,下面我们来分析一下 ip_rcv 函数的实现:

int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt){ struct iphdr *iph = skb->nh.iph; // 如果数据包不是发送给本机的, 丢弃数据包 if (skb->pkt_type == PACKET_OTHERHOST) goto drop; // 如果其他地方也在使用数据包, 复制一份新的数据包 if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) goto out; // 数据包的长度比IP头部的长度还小,不合法丢弃 if (skb->len < sizeof(struct iphdr) || skb->len < (iph->ihl<<2)) goto inhdr_error; // 判断IP头部是否合法 if (iph->ihl < 5 // IP头部长度是否合法 || iph->version != 4 // IP协议版本是否合法 || ip_fast_csum((u8 *)iph, iph->ihl) != 0) // IP校验和是否正确 goto inhdr_error; { __u32 len = ntohs(iph->tot_len); // 如果数据包的长度比IP头部的总长度小, 说明数据包不合法, 需要丢弃 if (skb->len < len || len < (iph->ihl<<2)) goto inhdr_error; __skb_trim(skb, len); } // 如果所有验证都通过, 那么调用 ip_rcv_finish 函数继续处理数据包 return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL, ip_rcv_finish); // 这里是丢弃数据包的处理inhdr_error: IP_INC_STATS_BH(IpInHdrErrors);drop: kfree_skb(skb);out: return NET_RX_DROP;}

ip_rcv 函数主要对数据包的合法性进行验证,如果数据包是合法的,那么就调用 ip_rcv_finish 函数继续对数据包进行处理。

我们继续分析 ip_rcv_finish 函数的实现:

static inline int ip_rcv_finish(struct sk_buff *skb){ struct net_device *dev = skb->dev; struct iphdr *iph = skb->nh.iph; if (skb->dst == NULL) { // 如果数据包的输入路由缓存还没设置 // 根据目标IP地址获取数据包的输入路由缓存 if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev)) goto drop; } ... // 如果数据包是发送给本机的,那么就调用 ip_local_deliver 进行处理 return skb->dst->input(skb); drop: kfree_skb(skb); return NET_RX_DROP;}

为了简单起见,我们去掉了对 IP 选项的处理。在上面的代码中,如果数据包的输入路由缓存还没设置,那么先调用 ip_route_input 函数获取数据包的输入路由缓存(ip_route_input 函数将会在 路由子系统 一章介绍,暂时可以忽略这个函数)。

设置好数据包的路由缓存后,就调用路由缓存的 input 方法处理数据包。如果数据包是发送给本机的,那么路由缓存的 input 方法将会被设置为 ip_local_deliver(由 ip_route_input 函数设置)。

所有,如果数据包是发送给本机,那么最终会调用 ip_local_deliver 函数处理数据包,我们继续来分析 ip_local_deliver 函数:

int ip_local_deliver(struct sk_buff *skb){ struct iphdr *iph = skb->nh.iph; // 如果当前数据包是一个IP分片, 那么先对数据包进行分片处理 if (iph->frag_off & htons(IP_MF|IP_OFFSET)) { skb = ip_defrag(skb); if (!skb) return 0; } // 接着调用 ip_local_deliver_finish 函数处理数据包 return NF_HOOK(PF_INET, NF_IP_LOCAL_IN, skb, skb->dev, NULL, ip_local_deliver_finish);}

ip_local_deliver 函数首先判断数据包是否为一个 IP 分片(IP 分片将在下一篇文章介绍,暂时可以忽略),如果是就调用 ip_defrag 函数对数据包进行分片重组处理。如果数据包不是一个分片或者分片重组成功,那么最终调用 ip_local_deliver_finish 函数处理数据包。

ip_local_deliver_finish 函数是 IP 层处理数据包的最后一步,我们接着分析 ip_local_deliver_finish 函数的实现:

static inline int ip_local_deliver_finish(struct sk_buff *skb){ struct iphdr *iph = skb->nh.iph; // 获取数据包的IP头部 ... skb->h.raw = skb->nh.raw iph->ihl * 4; // 设置传输层协议头部指针 { int hash = iph->protocol & (MAX_INET_PROTOS - 1); // 从IP头部获取传输层协议类型 struct sock *raw_sk = raw_v4_htable[hash]; struct inet_protocol *ipprot; int flag; ... ipprot = (struct inet_protocol *)inet_protos[hash]; // 传输层协议处理函数 flag = 0; if (ipprot != NULL) { // 调用传输层协议处理函数处理数据包 if (raw_sk == NULL && ipprot->next == NULL && ipprot->protocol == iph->protocol) { return ipprot->handler(skb, (ntohs(iph->tot_len)-(iph->ihl*4))); } else { flag = ip_run_ipprot(skb, iph, ipprot, (raw_sk != NULL)); } } ... } return 0;}

在上面代码中,我们省略对原始套接字的处理(原始套接字将会在 原始套接字 一章中介绍)。ip_local_deliver_finish 函数的主要工作如下:

    通过数据包的 IP 头部获取到上层协议(传输层)类型。

    根据传输层协议类型从 inet_protos 数组中查找对应的处理函数。

    调用传输层协议的处理函数处理数据包。

inet_protos 数组保存了传输层协议的处理函数,其的定义如下:

struct inet_protocol{ int (*handler)(struct sk_buff *skb, unsigned short len); // 协议的处理函数 unsigned char protocol; // 协议类型 struct inet_protocol *next; // 解决冲突 ...};#define MAX_INET_PROTOS32struct inet_protocol *inet_protos[MAX_INET_PROTOS];

不同的传输层协议处理函数,会根据其协议类型的值保存到 inet_protos 数组中。由于 inet_protos 数组只有32个元素,所以保存处理函数时,需要将协议值与32进行取模操作,得到一个 0 ~ 31 的值,然后把处理函数保存到 inet_protos 数组对应位置上。如果有多个协议发生冲突,那么就通过 next 字段连接起来。

通过调用 inet_add_protocol 函数,可以向 inet_protos 数组注册传输层协议的处理函数。例如 TCP协议 的处理函数定义如下:

static struct inet_protocol TCP_protocol = { tcp_v4_rcv, /* TCP handler */ IPPROTO_TCP, /* protocol ID */ ...};

所以,当接收到一个 TCP 协议数据包时,将会调用 tcp_v4_rcv 函数处理此数据包。

最后,我以一幅图来展示处理 IP 数据包的函数调用链:

一分钟实现内网穿透ngrok服务器搭建

一分钟实现内网穿透(ngrok服务器搭建)

简单来说内网穿透的目的是:让外网能访问你本地的应用,例如在外网打开你本地http://127.0.0.1指向的Web站点。

最近公司的花生壳到期了,要续费,发现价格一直在涨,都是5年以上的老用户,旗舰版都没有实现内网完全穿透,打算自己动手替换这个服务,中间走了不少的弯路,这里记录一些文字为大家提供参考。

随着开发与运行移动互联网的应用越来越多对打通内外网的需要也更加迫切,如微信开发、IOS与Android开发等。

虽然租用VPS、ECS等服务器可以解决很多问题但高性能的外网服务器价格非常贵还有数据安全问题,我选择的是公网服务器仅做代理与轻量应用,复杂的应用部署到内网服务器再穿透访问。

一、内网穿透概要

为了理解内网穿透我们先来了解几个概念:

1.1、IP地址

网络中唯一定位一台设备的逻辑地址,类似我们的电话号码

在互联网中我们访问一个网站或使用一个网络服务最终都需要通过IP定位到每一台主机,如访问baidu网站:

其中119.75.213.61就是一个公网的IP地址,他最终指向了一台服务器。

IP地址是IP协议提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此来屏蔽物理地址的差异。

内网IP可以同时出现在多个不同的局域网络中,如A公司的U1用户获得了192.168.0.5,B公司的U3用户也可以获得192.168.0.5;但公网IP是唯一的,因为我们只有一个Internet。

//局域网可使用的网段(私网地址段)有三大段:10.0.0.0~10.255.255.255(A类)172.16.0.0~172.31.255.255(B类)192.168.0.0~192.168.255.255(C类)

1.2、域名

域名是IP的别名,便于记忆,域名最终通过DNS解析成IP地址。

IP V4是一个32位的数字,IP V6有128位,要记住一串毫无意义的数字非常困难,域名解决了这个问题。

如www.zhangguo.com.cn就是一个域名,cn表示地区,com表示商业机构,zhangguo是公司名称,www是主机名

DNS查询过程如下,最终将域名变成IP地址

1.3、NAT

NAT(Network Address Translation)即网络地址转换,NAT能将其本地地址转换成全球IP地址。

内网的一些主机本来已经分配到了本地IP地址(如局域网DHCP分配的IP),但现在又想和因特网上的主机通信(并不需要加密)时,可使用NAT方法。

通过使用少量的公有IP 地址代表较多的私有IP 地址的方式,将有助于减缓可用的IP地址空间的枯竭。

NAT不仅能解决了lP地址不足与共享上网的问题,而且还能够有效地避免来自网络外部的攻击,隐藏并保护网络内部的计算机。

多路由器可完成NAT功能。

NAT的实现方式:

静态转换是指将内部网络的私有IP地址转换为公有IP地址,IP地址对是一对一。

动态转换是指将内部网络的私有IP地址转换为公用IP地址时,IP地址是不确定的,是随机的。

端口多路复用(Port address Translation,PAT),内部网络的所有主机均可共享一个合法外部IP地址实现对Internet的访问,从而可以最大限度地节约IP地址资源。同时又可隐藏网络内部的所有主机,有效避免来自internet的攻击。因此,目前网络中应用最多的就是端口多路复用方式。

应用程序级网关技术(Application Level Gateway)ALG:传统的NAT技术只对IP层和传输层头部进行转换处理,ALG它能对这些应用程序在通信时所包含的地址信息也进行相应的NAT转换。

1.4、Proxy

Proxy即代理,被广泛应用于计算机领域,主要分为正向代理与反向代理:

1.4.1、正向代理

比如X花店代A,B,C,D,E五位男生向Candy女生送匿名的生日鲜花,这里的X花店就是5位顾客的代理,花店代理的是客户,隐藏的是客户。这就是我们常说的代理。

正向代理隐藏了真实的请求客户端。服务端不知道真实的客户端是谁,客户端请求的服务都被代理服务器代替来请求,某些科学上网工具扮演的就是典型的正向代理角色。用浏览器访问http://www.google.com时被墙了,于是你可以在国外搭建一台代理服务器,让代理帮我去请求google.com,代理把请求返回的相应结构再返回给我。

当多个客户端访问服务器时服务器不知道真正访问自己的客户端是那一台。正向代理中,proxy和client同属一个LAN,对server透明;

1.4.2、反向代理

拨打10086客服电话,接线员可能有很多个,调度器会智能的分配一个接线员与你通话。这里的调度器就是一个代理,只不过他代理的是接线员,客户端不能确定真正与自己通话的人,隐藏与保护的是目标对象。

反向代理隐藏了真实的服务端,当我们请求 ww.baidu.com 的时候,就像拨打10086一样,背后可能有成千上万台服务器为我们服务,但具体是哪一台,你不知道,也不需要知道,你只需要知道反向代理服务器是谁就好了,ww.baidu.com 就是我们的反向代理服务器,反向代理服务器会帮我们把请求转发到真实的服务器那里去。Nginx就是性能非常好的反向代理服务器,用来做负载均衡。

反向代理中,proxy和server同属一个LAN,对client透明。

了解更多关于代理内容请点击这里。

1.5、DDNS

DDNS即动态域名解析,是将用户的动态IP地址映射到一个固定的域名解析服务上,用户每次连接网络的时候,客户端程序就会通过信息传递把该主机的动态IP地址传送给位于服务商主机上的服务器程序,服务程序负责提供DNS服务并实现动态域名解析。就是说DDNS捕获用户每次变化的IP地址,然后将其与域名相对应,这样域名就可以始终解析到非固定IP的服务器上,互联网用户通过本地的域名服务器获得网站域名的IP地址,从而可以访问网站的服务。

1.6、为什么需要内网穿透

当内网中的主机没有静态IP地址要被外网稳定访问时可以使用内网穿透

在互联网中唯一定位一台主机的方法是通过公网的IP地址,但固定IP是一种非常稀缺的资源,不可能给每个公司都分配一个,且许多中小公司不愿意为高昂的费用买单,多数公司直接或间接的拨号上网,电信部门会给接入网络的用户分配IP地址,以前上网用户少的时候基本分配的都是临时的静态IP地址,租约过了之后可能会更换成另一个IP地址,这样外网访问就不稳定,因为内网的静态IP地址一直变化,为了解决这个问题可以使用动态域名解析的办法变换域名指向的静态IP地址。但是现在越来越多的上网用户使得临时分配的静态IP地址也不够用了,电信部门开始分配一些虚拟的静态IP地址,这些IP是公网不能直接访问的,如以125开头的一些IP地址,以前单纯的动态域名解析就不好用了。

1.7、内网穿透的定义与障碍

简单来说实现不同局域网内的主机之间通过互联网进行通信的技术叫内网穿透。

障碍一:位于局域网内的主机有两套 IP 地址,一套是局域网内的 IP 地址,通常是动态分配的,仅供局域网内的主机间通信使用;一套是经过网关转换后的外网 IP 地址,用于与外网程序进行通信。

障碍二:位于不同局域网内的两台主机,即使是知道了对方的 IP 地址和端口号,“一厢情愿”地将数据包发送过去,对方也是接收不到的。

因为出于安全起见,除非是主机主动向对方发出了连接请求(这时会在该主机的数据结构中留下一条记录),否则,当主机接收到数据包时,如果在其数据结构中查询不到对应的记录,那些不请自来的数据包将会被丢弃。

解决办法:要想解决以上两大障碍,我们需要借助一台具有公网 IP 的服务器进行桥接。

二、常见的内网穿透产品

2.1、花生壳

花生壳既是内网穿透软件、内网映射软件,也是端口映射软件。规模最大,较正规,完善。

收费高,使用简单

官网:http://www.oray.com/

2.2、Nat123

nat123是内网端口映射与动态域名解析软件,在内网启动映射后,可在外网访问连接内网网站等应用。整个网站我都没有找到客服电话,网友发了一些反面的评价

收费,使用简单

官网:http://www.nat123.com

2.3、NATAPP

NATAPP基于ngrok的国内内网穿透服务,免费版会强制更换域名,临时用一下可以

收费,使用简单

官网:https://natapp.cn/

2.4、frp与其它

frp 是一个高性能的反向代理应用,可以帮助您轻松地进行内网穿透,对外网提供服务,支持 tcp, http, https 等协议类型,并且 web 服务支持根据域名进行路由转发。

开源免费

使用相对复杂,需要代理服务器支持

官网:https://github.com/fatedier/frp

文档:查看帮助文档,简书示例

利用处于内网或防火墙后的机器,对外网环境提供 http 或 https 服务。

对于 http, https 服务支持基于域名的虚拟主机,支持自定义域名绑定,使多个域名可以共用一个80端口。

利用处于内网或防火墙后的机器,对外网环境提供 tcp 和 udp 服务,例如在家里通过 ssh 访问处于公司内网环境内的主机。

因为frp 仍然处于前期开发阶段,未经充分测试与验证,不推荐用于生产环境,所有我选择了ngrok,资料比较多。

还有如圣剑内网通、ngrok(开源免费)、更多办法

三、ngrok

ngrok是一个反向代理,通过在公共的端点和本地运行的Web服务器之间建立一个安全的通道。ngrok可捕获和分析所有通道上的流量,便于后期分析与响应。

开源免费

官网:https://ngrok.com/

源码:https://github.com/inconshreveable/ngrok

ngrok1.x开源,ngrok2.x不开源

ngrok使用go语言开发,源代码分为客户端与服务器端。

国内免费服务器:http://ngrok.ciqiuwl.cn/,更多免费服务器请大家挖掘,资源共享,我随时更新:)

如果有服务器,仅客户端的使用是不复杂的,以上面的免费服务器为示例完成内网穿透

现在假定我的本地已成功部署了一个网站,访问地址为127.0.0.1,想内网穿透后被公网上的用户访问,一般步骤如下:

步骤1、下载windows版本的客户端,解压。一般在为你提供代理服务器的网站上找你要下载的客户端:

步骤2、在命令(cmd)行下进入到ngrok客户端目录下

步骤3、执行 ngrok -config=ngrok.cfg -subdomain xxx 80 //(xxx 是你自定义的域名前缀),建议批处理

如果连接成功,会提示如下信息:

这一步如果你认为太麻烦,可以直接运行目录下的start.bat批处理文件就不用进DOS环境了。运行start.bat直接跳过2,3步

步骤4、如果开启成功 你就可以使用 xxx.ngrok.xiaomiqiu.cn 来访问你本机的 127.0.0.1:80 的服务了,当然你必须确定的是你本机的Web是可以正常访问的。

注意:

如果你自己有顶级域名,想通过自己的域名来访问本机的项目,那么先将自己的顶级域名解析到120.25.161.137(域名需要已备案哦,80端口必须备案),然后执行 ngrok -config=ngrok.cfg -hostname xxx.xxx.xxx 80 //(xxx.xxx.xxx是你自定义的顶级域名)

四、ubuntu下生成ngrok服务器主程序

4.1、步骤与先决条件

如果你只是临时穿透或调试用,到第三步基本就可以了,但如果想作为稳定的商业服务,用别人的服务器还是受制于人,这里我们准备搭建自己的ngrok服务器。大致的步骤如下:

ngrok服务器可以是多种平台,如windows、linux(CentOS、Debian、Ubuntu等)、Mac OS等。

编译源代码生成应用强烈建议大家使用linux环境,windows肯定可以成功,但非常麻烦,我在windows操作系统上兜了一个大圈圈。

先决条件:

a)、您有一台公网上的服务器,如阿里云的ECS

b)、您有一个域名,最好ICP备案成功,不然80端口没有办法使用,不过像微信开发是不使用80端口的,可以用nginx代理转换。

4.2、安装ubuntu操作系统

在linux环境下编译ngrok的源代码比windows下 方便很多,这里我们选择使用ubuntu,获得ubuntu的方法有如下几种:

1)、全新安装ubuntu系统

2)、申请VPS服务器, 阿里云、腾讯云、华为云、百度云、新浪云等,仅编译一下这种方法不错

3)、在虚拟机中安装ubuntu系统

综合考虑我选择了在虚拟机中安装ubuntu操作系统

4.2.1、安装VMware虚拟机

VMware Workstation是一款功能强大的虚拟机软件,在不影响本机操作系统的情况下,用户可以在虚拟机中同时运行不同版本的操作系统,用于开发、测试以及部署工作。

VMware Workstation 12 pro下载:VMware-workstation-full-12.1.0-3272444.exe

序列号:5A02H-AU243-TZJ49-GTC7K-3C61N(商业应用请购买正式版权,这里仅为学习使用)

1)、双击VMware Workstation 12安装文件,或者右键管理员身份打开,提示是否允许更改,点击是;

2)、打开VMware安装向导,点击下一步;

 

3)、VMware Workstation 12激活步骤:

  方法一、首次开启直接输入上文密钥,即可激活;
  方法二、首次开启选择试用,进入试用后按一下步骤激活:

  a、打开虚拟机主界面,点击“帮助”—“输入许可证密钥”;

  

  b、在密钥输入框输入永久许可证密钥5A02H-AU243-TZJ49-GTC7K-3C61N,确定;更多

4.2.2、安装ubuntu到虚拟机

1)、下载ubuntu操作系统镜像

下载地址:https://www.ubuntu.com/download/desktop

这里我下载的是ubuntu-16.04.3-desktop-amd64.iso

2)、在VMware中安装ubuntu

打开VMware点击“创建新的虚拟机”

向导选择自定义

然后下一步再下一步,直到这里,稍后再安装系统

后面设置处理器和内存的,电脑配置好的可以试试,否则采用默认的,博主这里是采用默认的,然后下一,直到这里,选择将虚拟机存储为单个磁盘:

个人建议至少20G硬盘空间,内存建议给1.5G,当然也要看电脑本身的配置,1G的内存跑起来比较卡。

其它的步骤比较简单,更多细节可以参考这里,《VMware Ubuntu安装详细过程》。

4.2.3、配置ubuntu系统

当ubuntu系统安装成功后,在虚拟机中可以启动ubuntu系统,启动后的系统如下:

ubuntu系统的使用还是有许多内容的,这里需要设置的内容如下:

a)、设置上网

就是在ubuntu中可以访问外网,可以使用多种形式

b)、设置语言

可以选择使用中文版的ubuntu语言环境

c)、设置屏幕分辨率

如果不设置默认的屏幕比较小

d)、设置以root超级管理员的身份登录

许多操作要求管理身份

e)、安装VMware Tools工具

只有在VMware虚拟机中安装好了VMware Tools,才能实现主机与虚拟机之间的文件共享,同时可支持自由拖拽的功能,鼠标也可在虚拟机与主机之间自由移动(不用再按ctrl alt),且虚拟机屏幕也可实现全屏化。
VMware Tools是VMware虚拟机中自带的一种增强工具,相当于VirtualBox中的增强功能(Sun VirtualBox Guest Additions),是VMware提供的增强虚拟显卡和硬盘性能、以及同步虚拟机与主机时钟的驱动程序。

注意如果这里是灰色的需要您将linux.iso镜像加载到虚拟光驱中,一般在VM的安装目录下有,如果没有您需要自行下载。

说明:ubuntu的使用不是本文的重点,相关操作请大家自行查找。

4.3、生成ngrok服务器与客户端应用程序

4.3.1. 导出源代码

ngrok的源代码托管在github上,可以先在ubuntu下安装git再将ngrok的源代码克隆到本地。

其实也可以直接下载到本地后解压,这里使用命令行完成。

启动ubuntu,开打命令行(终端),如下所示:

以root身份执行如下命令:

mkdir ngrok #创建名称为ngrok的目录 apt-get update #更新包管理器 apt-get install git #安装git git clone https://github.com/inconshreveable/ngrok.git ngrok2 #将ngrok源代码克隆回本地

成功执行后如下所示:

导出成功后的源代码:

PS. 直接在服务器上下载的话实在太慢,可以先在本地下载好,然后用ftp放到服务器上去直接用,如果安装了VMware tools直接拖进去就可以了。

4.3.2. 安装Go语言开发环境

直接在命令模式下执行如下指令:

apt-get install golang #安装go语言

执行结果如下:

4.3.3. 更改ngrok域名

在自己的域名管理中添加解析A记录,如下所示:

将*.ngrok与ngrok都指向您的主机IP。

默认的域名是ngrok自己的,要替换成您自己的域名

export GOPATH=/usr/local/ngrok/ #设置环境变量,Go语言的安装位置export NGROK_DOMAIN="ngrok.yourdomain.com" #设置环境变量,ngrok域名

PS. ngrok名称可以任意,推荐名称为ngrok或者tunnel

4.3.4. 为域名生成证书

openssl genrsa -out rootCA.key 2048openssl req -x509 -new -nodes -key rootCA.key -subj "/CN=$NGROK_DOMAIN" -days 5000 -out rootCA.pemopenssl genrsa -out server.key 2048openssl req -new -key server.key -subj "/CN=$NGROK_DOMAIN" -out server.csropenssl x509 -req -in server.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out server.crt -days 5000

生成后的结果如下:

证书如下:

4.3.5. 拷贝证书到指定位置

cp rootCA.pem assets/client/tls/ngrokroot.crt #复制rootCA.pem到assets/client/tls/并更名为ngrokroot.crtcp server.crt assets/server/tls/snakeoil.crt #复制server.crt到assets/server/tls/并更名为snakeoil.crtcp server.key assets/server/tls/snakeoil.key #复制server.key到assets/server/tls/并更名为snakeoil.key

运行结果:

4.3.6. 编译

由于go语言的特性,在编译时直接生成机器码,所以在运行过程中并不需要go的环境(非托管应用)。在ngrok目录下,运行一下命令分别生成对应的客户端与服务端。

#win服务端GOOS=windows GOARCH=386 make release-server #win客户端GOOS=windows GOARCH=386 make release-client#linux服务端GOOS=linux GOARCH=386 make release-server#linux客户端GOOS=linux GOARCH=386 make release-client

生成完成后,在工作目录的bin文件夹下,产生对应的文件。以编译windows平台为例,会产生“ngrok.exe”与“ngrokd.exe”这两个文件,前者客户端,后者需要运行在公网服务器上。

因为项目中引用了一些外部资源,生成会耗费一些时间,对网络也有一定的要求,太慢会中短,命令执行下如:

生成结果:

这里我还生成了两个运行在windows服务器与客户端的应用:

ngrok.exe是客户端,ngrokd.exe是服务端,下面是比较连续的操作结果。

五、部署服务器端主程序

5.1、部署到Windows Server服务器

将生成的ngrokd.exe文件复制到windows服务器中,当然如果要部署到linux中也是没有问题的。

这里我将ngrokd.exe放在c:grokeServer目录下:

为了方便,我编写了一个批处理文件:ngrokserver2.bat

ngrokd.exe -tlsKey="snakeoil.key" -tlsCrt="snakeoil.crt" -domain="ngrok.你的域名.com" -httpAddr=":801" -httpsAddr=":802"

点击批处理运行结果如下:

绑定的域名换成自己的域名,http使用801端口,https使用802端口,供客户端连接的管道端口设置为4443端口,必须前面的域名相同。

为了安全许多服务器会将端口屏蔽,我使用的是ECS服务器,默认801,802都是关闭的,需要手动开启,在阿里云的后台添加开放的端口就可了:

5.2、一键部署ngrok服务器(CentOS、Debian、Ubuntu)

如果编译生成ngrok的源代码生成应用太麻烦,你可以选择网友写的工具,支持一键部署到安装平台:CentOS、Debian、Ubuntu。

https://github.com/clangcn/ngrok-one-key-install

六、部署ngrok客户端

这里的客户端就是您的web应用程序所运行的主机,将ubuntu生成的ngrok.exe客户端应用复制到您的系统中:

添加配置文件ngrok.cfg:

server_addr: "ngrok.你的域名.com:4443"trust_host_root_certs: false

添加批处理start.bat,如果只运行一次直接在命令行下输入命令也是一样的效果,内容如下:

ngrok.exe -subdomain kyt -config=ngrok.cfg 8987

其中8987为端口号,运行成功的结果如下所示:

看到这个界面时说明已成功了。

七、启动客户端并测试

打开浏览器,输入您映射后的域名就可以穿透内网访问您的web服务器了。

八、总结

一开始选择错了平台,在windows花了不少时间,在ubuntu下顺利完成。

无论是客户端还是服务器端最好都做成服务,更方便与稳定。

由于服务器上同时运行着IIS,故服务端Ngrok启动时无法使用80端口,所以在上面,我使用了801作为Ngrok服务器的http端口,使用IIS的代理功能可以解决这个问题,点击这里。当然也可以使用nginx将80转换成其它端口。

许多内容都参考了网友的文章。

如果服务器搭建好了,只运行客户端穿透内网一分钟够了:)。

Si4463无线模块收发超长数据包

Silicon LabsEZRadioPro 系列的Si4463无线模块,TX FIFO 和 RX FIFO只有64字节。那么如何实现超过64字节的数据包收发呢?需要繁琐的去把长包拆分成小于等于64字节的小包,每个小包单独发送,然后接收端把这些小包数据拼接回原始的长包数据吗?回答是:不!因为长包的收发在Si446X 收发芯片实现是非常简单的。

打开WDS软件或者查看API寄存器描述文档,有两个中断非常有用,就是TX_FIFO_ALMOST_EMPTY_PEND和RX_FIFO_ALMOST_FULL_PEND,这个两个中断代表的意思就是TX FIFO 的数据即将发送完,或者RX FIFO即将被填充满。那么我们只需要根据这两个中断,就可以实现超长数据包收发了。

例如,在TX_FIFO_ALMOST_EMPTY_PEND中断产生时,立刻填充数据到TX FIFO中,新填充的数据会紧跟着之前的数据发送出去,直到数据包完全发送完成,最终产生一个发送完成中断,表示这包数据发完。接收时也是类似,当收到的数据不断往RX FIFO中填充,快要填满的时候,就会产生RX_FIFO_ALMOST_FULL_PEND中断,这时立刻把RX FIFO读取出来,那么空出来的FIFO又可以继续接收新的数据,直到接收到的总数据等于整个完整的数据包长度,产生一个接收完成中断。整个过程都是连续的,根据包长度和设定的阀值,会产生多个TX_FIFO_ALMOST_EMPTY_PEND和RX_FIFO_ALMOST_FULL_PEND中断,但是每包数据发送完成和接收完成中断只会有一个,和短包的收发是一样的。

WDS的设置界面如下:

设置配置TX_FIFO_ALMOST_EMPTY和RX_FIFO_ALMOST_FULL 阀值

开启对应的中断

用户需要再代码中添加检测到TX_FIFO_ALMOST_EMPTY和RX_FIFO_ALMOST_FULL两种中断时系统对应的反应。

以上是对Si4463发送超长数据包的一些总结,希望能对你有所帮助。

常见常用的TCP知识点有些啥

/ 计网分层结构 /
考虑最简单的情况:两台主机之间的通信。这个时候只需要一条网线把两者连起来,规定好彼此的硬件接口,如都用USB、电压10v、频率2.4GHz等,这一层就是物理层,这些规定就是物理层协议


我们当然不满足于只有两台电脑连接,因此我们可以使用交换机把多个电脑连接起来,如下图:


这样连接起来的网络,称为局域网,也可以称为以太网(以太网是局域网的一种)。在这个网络中,我们需要标识每个机器,这样才可以指定要和哪个机器通信。这个标识就是硬件地址MAC。硬件地址随机器的生产就被确定,永久性唯一。在局域网中,我们需要和另外的机器通信时,只需要知道他的硬件地址,交换机就会把我们的消息发送到对应的机器。
这里我们可以不管底层的网线接口如何发送,把物理层抽离,在他之上创建一个新的层次,这就是

数据链路层


我们依然不满足于局域网的规模,需要把所有的局域网联系起来,这个时候就需要用到路由器来连接两个局域网:


但是如果我们还是使用硬件地址来作为通信对象的唯一标识,那么当网络规模越来越大,需要记住所有机器的硬件地址是不现实的;同时,一个网络对象可能会频繁更换设备,这个时候硬件地址表维护起来更加复杂。这里使用了一个新的地址来标记一个网络对象:

IP地址 。
通过一个简单的寄信例子来理解IP地址。
我住在北京市,我朋友A住在上海市,我要给朋友A写信:

    写完信,我会在信上写好我朋友A的地址,并放到北京市邮局(给信息附加目标IP地址,并发送给路由器)

    邮局会帮我把信运输到上海市当地邮局(信息会经过路由传递到目标IP局域网的路由器)

    上海市当地路由器会帮我把信交给朋友A(局域网内通信)


因此,这里IP地址就是一个网络接入地址(朋友A的住址),我只需要知道目标IP地址,路由器就可以把消息给我带到。在局域网中,就可以动态维护一个MAC地址与IP地址的映射关系,根据目的IP地址就可以寻找到机器的MAC地址进行发送 。
这样我们不需管理底层如何去选择机器,我们只需要知道IP地址,就可以和我们的目标进行通信。这一层就是网络层。网络层的核心作用就是 提供主机之间的逻辑通信 。这样,在网络中的所有主机,在逻辑上都连接起来了,上层只需要提供目标IP地址和数据,网络层就可以把消息发送到对应的主机。
一个主机有多个进程,进程之间进行不同的网络通信,如边和朋友开黑边和女朋友聊微信。我的手机同时和两个不同机器进行通信。那么当我的手机收到数据时,如何区分是微信的数据,还是王者的数据?那么就必须在网络层之上再添加一层:运输层


运输层通过socket(套接字),将网络信息进行进一步的拆分,不同的应用进程可以独立进行网络请求,互不干扰。这就是运输层的最本质特点:

提供进程之间的逻辑通信 。这里的进程可以是主机之间,也可以是同个主机,所以在android中,socket通信也是进程通信的一种方式。
现在不同的机器上的应用进程之间可以独立通信了,那么我们就可以在计算机网络上开发出形形式式的应用:如web网页的http,文件传输ftp等等。这一层称为应用层。
应用层还可以进一步拆分出表示层、会话层,但他们的本质特点都没有改变:完成具体的业务需求

。和下面的四层相比,他们并不是必须的,可以归属到应用层中。
最后对计网分层进行小结:

    最底层物理层,负责两个机器之间通过硬件的直接通信;

    数据链路层使用硬件地址在局域网中进行寻址,实现局域网通信;

    网络层通过抽象IP地址实现主机之间的逻辑通信;

    运输层在网络层的基础上,对数据进行拆分,实现应用进程的独立网络通信;

    应用层在运输层的基础上,根据具体的需求开发形形式式的功能。


这里需要注意的是,分层并不是在物理上的分层,而是逻辑上的分层。通过对底层逻辑的封装,使得上层的开发可以直接依赖底层的功能而无需理会具体的实现,简便了开发。
这种分层的思路,也就是责任链设计模式,通过层层封装,把不同的职责独立起来,更加方便开发、维护等等。okHttp中的拦截器设计模式,也是这种责任链模式。
/ 运输层 /
本文主要是讲解TCP,这里需要增加一些运输层的知识。

本质:提供进程通信


在运输层之下的网络层,是不知道该数据包属于哪个进程,他只负责数据包的接收与发送。运输层则负责接收不同进程的数据交给网络层,同时把网络层的数据拆分交给不同的进程。从上往下汇聚到网络层,称为

多路复用,从下往上拆分,称为多路拆分 。
运输层的表现,受网络层的限制。这很好理解,网络层是运输层的底层支持。所以运输层是无法决定自己带宽、时延等的上限。但可以基于网络层开发更多的特性:如可靠传输。网络层只负责尽力把数据包从一端发送到另一端,而不保证数据可以到达且完整。

底层实现:socket


前面讲到,最简单的运输层协议,就是提供进程之间的独立通信 ,但底层的实现,是socket之间的独立通信

。在网络层中,IP地址是一个主机逻辑地址,而在运输层中,socket是一个进程的逻辑地址;当然,一个进程可以拥有多个socket。应用进程可以通过监听socket,来获取这个socket接受到的消息。


socket并不是一个实实在在的东西,而是运输层抽象出来的一个对象。运输层增加了

端口这个概念,来区分不同的socket。端口可以理解为一个主机上有很多的网络通信口,每个端口都有一个端口号,端口的数量由运输层协议确定。
不同的运输层协议对socket有不同的定义方式。在UDP协议中,使用目标IP 目标端口号来定义一个socket;在TCP中使用目标IP 目标端口号 源IP 源端口号来定义一个socket。我们只需要在运输层报文的头部附加上这些信息,目标主机就会知道我们要发送给哪个socket,对应监听该socket的进程就可获得信息。

运输层协议


运输层的协议就是大名鼎鼎的TCP和UDP。其中,UDP是最精简的运输层协议,只实现了进程间的通信;而TCP在UDP的基础上,实现了可靠传输、流量控制、拥塞控制、面向连接等等特性,同时也更加复杂。
当然除此之外,还有更多更优秀的运输层协议,但目前广为使用的,就是TCP和UDP。UDP在后面也会总结到。
/ TCP协议首部 /


TCP协议,表现在报文上,就是会在应用层传输下来的数据前附加上一个TCP首部,这个首部附加了TCP信息,先来整体看一下这个首部的结构:


这张图是来自我大学老师的课件, 非常好用,所以一直拿来学习。最下面部分表示了报文之间的关系,TCP数据部分就是应用层传下来的数据。
TCP首部固定长度是20字节,下面还有4字节是可选的。内容很多,但其中有一些我们比较熟悉的:源端口,目标端口。嗯?socket不是还需要IP进行定位吗?IP地址在网络层被附加了。其他的内容后面都会慢慢讲解,作为一篇总结文章,这里放出查阅表,方便复习:


选项字段中包含以下其他选项:


讲完下面内容,再回来看这些字段就熟悉了。

/ TCP面向字节流特性 /
TCP并不是把应用层传输过来的数据直接加上首部然后发送给目标,而是把数据看成一个字节 流,给他们标上序号之后分部分发送。这就是TCP的 面向字节流

特性:

TCP会以流的形式从应用层读取数据并存放在自己的发送缓存区中,同时为这些字节标上序号

TCP会从发送方缓冲区选择适量的字节组成TCP报文,通过网络层发送给目标

目标会读取字节并存放在自己的接收方缓冲区中,并在合适的时候交付给应用层


面向字节流的好处是无需一次存储过大的数据占用太多内存,坏处是无法知道这些字节代表的意义,例如应用层发送一个音频文件和一个文本文件,对于TCP来说就是一串字节流,没有意义可言,这会导致粘包以及拆包问题,后面讲。
/ 可靠传输原理 /


前面讲到,TCP是可靠传输协议,也就是,一个数据交给他,他肯定可以完整无误地发送到目标地址,除非网络炸了。他实现的网络模型如下:


对于应用层来说,他就是一个可靠传输的底层支持服务;而运输层底层采用了网络层的不可靠传输。虽然在网络层甚至数据链路层就可以使用协议来保证数据传输的可靠性,但这样网络的设计会更加复杂、效率会随之降低。把数据传输的可靠性保证放在运输层,会更加合适。
可靠传输原理的重点总结一下有:

滑动窗口、超时重传、累积确认、选择确认、连续ARQ 。

停止等待协议


要实现可靠传输,最简便的方法就是:我发送一个数据包给你,然后你跟我回复收到,我继续发送下一个数据包。传输模型如下:


这种“一来一去”的方法来保证传输可靠就是

停止等待协议(stop-and-wait)。不知道还记不记得前面TCP首部有一个ACK字段,当他设置为1的时候,表示这个报文是一个确认收到报文。
然后再来考虑一种情况:丢包。网络环境不可靠,导致每一次发送的数据包可能会丢失,如果机器A发送了数据包丢失了,那么机器B永远接收不到数据,机器A永远在等待。解决这个问题的方法是:超时重传 。当机器A发出一个数据包时便开始计时,时间到还没收到确认回复,就可以认为是发生了丢包,便再次发送,也就是重传。

但重传会导致另一种问题:如果原先的数据包并没有丢失,只是在网络中待的时间比较久,这个时候机器B会受到两个数据包,那么机器B是如何辨别这两个数据包是属于同一份数据还是不同的数据?这就需要前面讲过的方法:给数据字节进行编号。这样接收方就可以根据数据的字节编号,得出这些数据是接下来的数据,还是重传的数据。
在TCP首部有两个字段:序号和确认号,他们表示发送方数据第一个字节的编号,和接收方期待的下一份数据的第一个字节的编号。前面讲到TCP是面向字节流,但是他并不是一个字节一个字节地发送,而是一次截取一整段。截取的长度受多种因素影响,如缓存区的数据大小、数据链路层限制的帧大小等。

连续ARQ协议


停止等待协议已经可以满足可靠传输了,但有一个致命缺点:效率太低。发送方发送一个数据包之后便进入等待,这个期间并没有干任何事,浪费了资源。解决的方法是:连续发送数据包

。模型如下:


和停止等待最大的不同就是,他会源源不断地发送,接收方源源不断收到数据之后,逐一进行确认回复。这样便极大地提高了效率。但同样,带来了一些额外的问题:
发送是否可以无限发送直到把缓冲区所有数据发送完?不可以。因为需要考虑接收方缓冲区以及读取数据的能力。如果发送太快导致接收方无法接受,那么只是会频繁进行重传,浪费了网络资源。所以发送方发送数据的范围,需要考虑到接收方缓冲区的情况。这就是TCP的

流量控制 。解决方法是:滑动窗口

。基本模型如下:

发送方需要根据接收方的缓冲区大小,设置自己的可发送窗口大小,处于窗口内的数据表示可发送,之外的数据不可发送。

当窗口内的数据接收到确认回复时,整个窗口会往前移动,直到发送完成所有的数据


在TCP的首部有一个窗口大小字段,他表示接收方的剩余缓冲区大小,让发送方可以调整自己的发送窗口大小。通过滑动窗口,就可以实现TCP的流量控制,不至于发送太快,导致太多的数据丢失。
连续ARQ带来的第二个问题是:网络中充斥着和发送数据包一样数据量的确认回复报文,因为每一个发送数据包,必须得有一个确认回复。提高网络效率的方法是:累积确认 。接收方不需要逐个进行回复,而是累积到一定量的数据包之后,告诉发送方,在此数据包之前的数据全都收到。例如,收到 1234,接收方只需要告诉发送方我收到4了,那么发送方就知道1234都收到了。

第三个问题是:如何处理丢包情况。在停止等待协议中很简单,直接一个超时重传就解决了。但,连续ARQ中不太一样。例如:接收方收到了消息 123 567,六个字节,编号为4的字节丢失了。按照累积确认的思路,只能发送3的确认回复,567都必须丢掉,因为发送方会进行重传。这就是GBN(go-back-n) 思路。
但是我们会发现,只需要重传4即可,这样不是很浪费资源,所以就有了:选择确认SACK 。在TCP报文的选项字段,可以设置已经收到的报文段,每一个报文段需要两个边界来进行确定。这样发送方,就可以根据这个选项字段重传丢失的数据了。

可靠传输小结


到这里关于TCP的可靠传输原理就已经介绍的差不多。最后进行一个小结:

通过连续ARQ协议与发送-确认回复模式来保证每一个数据包都到达接收方

通过给字节编号的方法,来标记每一个数据是属于重传还是新的数据

通过超时重传的方式,来解决数据包在网络中丢失的问题

通过滑动窗口来实现流量控制

通过累积确认 选择确认的方法来提高确认回复与重传的效率


当然,这只是可靠传输的冰山一角,感兴趣的可以再深入去研究(和面试官聊天已经差不多了[狗头])。/ 拥塞控制 /
拥塞控制考虑的是另外一个问题:避免网络过分拥挤导致丢包严重,网络效率降低 。
拿现实的交通举例子:
高速公路同一时间可通行的汽车数量是一定的,当节假日时,就会发生严重的堵车。在TCP中,数据包超时,会进行重传,也就是会进来更多的汽车,这时候更堵,最后导致的结果就是:丢包-重传-丢包-重传。最后整个网络瘫痪了。
这里的拥塞控制和前面的流量控制不是一个东西,流量控制是拥塞控制的手段:为了避免拥塞,必须对流量进行控制。拥塞控制目的是:限制每个主机的发送的数据量,避免网络拥塞效率下降。就像广州等地,限制车牌号出行是一个道理。不然大家都堵在路上,谁都别想走。

拥塞控制的解决方法是流量控制,流量控制的实现是滑动窗口,所以拥塞控制最终也是通过限制发送方的滑动窗口大小来限制流量 。当然,拥塞控制的手段不只是流量控制,导致拥塞的因素有:路由器缓存、带宽、处理器处理速度等等。提升硬件能力(把4车道改成8车道)是其中一个方法,但毕竟硬件提升是有瓶颈的,没办法不断提升,还是需要从tcp本身来增加算法,解决拥塞。
拥塞控制的重点有4个:慢开始、快恢复、快重传、拥塞避免

。这里依旧献祭出大学老师的ppt图片:


Y轴表示的是发送方窗口大小,X轴表示的是发送的轮次(不是字节编号)。

最开始的时候,会把窗口设置一个较小的值,然后每轮变为原来的两倍。这是慢开始。

当窗口值到达ssthresh值,这个值是需要通过实时网络情况设置的一个窗口限制值,开始进入拥塞避免,每轮把窗口值提升1,慢慢试探网络的底线。

如果发生了数据超时,表示极可能发生了拥塞,然后回到慢开始,重复上面的步骤。

如果收到三个相同的确认回复,表示现在网络的情况不太好,把ssthresh的值设置为原来的一半,继续拥塞避免。这部分称为快恢复。

如果收到丢包信息,应该尽快把丢失的包重传一次,这是快重传。

当然,窗口的最终上限是不能无限上涨的,他不能超过接收方的缓存区大小。


通过这个算法,就可以在很大程度上,避免网络拥挤。
除此之外,还可以让路由器在缓存即将满的时候,告知发送方我快满了,而不是等到出现了超时再进行处理,这是主动队列管理AQM。此外还有很多方法,但是上面的算法是重点。
/ 面向连接 /
这一小节讲的就是无人不晓的TCP三次握手与四次挥手这些,经过前面的内容,这一小节其实已经很好理解。
TCP是面向连接的,那连接是什么?这里的连接并不是实实在在的连接,而是通信双方彼此

之间的一个记录 。TCP是一个全双工通信,也就是可以互相发送数据,所以双方都需要记录对方的信息。根据前面的可靠传输原理,TCP通信双方需要为对方准备一个接收缓冲区可以接收对方的数据、记住对方的socket知道怎么发送数据、记住对方的缓冲区来调整自己的窗口大小等等,这些记录,就是一个连接。
在运输层小节中讲到,运输层双方通信的地址是采用socket来定义的,TCP也不例外。TCP的每一个连接只能有两个对象,也就是两个socket,而不能有三个。所以socket的定义需要源IP、源端口号、目标IP、目标端口号四个关键因素,才不会发生混乱。

假如TCP和UDP一样只采用目标IP 目标端口号来定义socket,那么就会出现多个发送方同时发送到同一个目标socket的情况。这个时候TCP无法区分这些数据是否来自不同的发送方,就会导致出现错误。


既然是连接,就有两个关键要点:建立连接、断开连接。

建立连接


建立连接的目的就是交换彼此的信息,然后记住对方的信息。所以双方都需要发送彼此的信息给对方:


但前面的可靠传输原理告诉我们,数据在网络中传输是不可靠的,需要对方给予我们一个确认回复,才可以保证消息正确到达。如下图:


机器B的确认收到和机器B信息可以进行合并,减少次数;而且发送机器B给机器A本身就代表了机器B已经收到了消息,所以最后的示例图是:


步骤如下:

    机器A发送syn包向机器B请求建立TCP连接,并附加上自身的接收缓冲区信息等,机器A进入SYN_SEND状态,表示请求已经发送正在等待回复;

    机器B收到请求之后,根据机器A的信息记录下来,并创建自身的接收缓存区,向机器A发送syn ack的合成包,同时自身进入SYN_RECV状态,表示已经准备好了,等待机器A 的回复就可以向A发送数据;

    机器A收到回复之后记录机器B 的信息,发送ack信息,自身进入ESTABLISHED状态,表示已经完全准备好了,可以进行发送和接收;

    机器B收到ACK数据之后,进入ESTABLISHED状态。


三次消息的发送,称为三次握手。

断开连接


断开连接和三次握手类似,直接上图:


1. 机器A发送完数据之后,向机器B请求断开连接,自身进入FIN_WAIT_1状态,表示数据发送完成且已经发送FIN包(FIN标志位为1);
2. 机器B收到FIN包之后,回复ack包表示已经收到,但此时机器B可能还有数据没发送完成,自身进入CLOSE_WAIT状态,表示对方已发送完成且请求关闭连接,自身发送完成之后可以关闭连接;
3. 机器B数据发送完成之后,发送FIN包给机器B ,自身进入LAST_ACK状态,表示等待一个ACK包即可关闭连接;
4. 机器A收到FIN包之后,知道机器B也发送完成了,回复一个ACK包,并进入TIME_WAIT状态
TIME_WAIT状态比较特殊。当机器A收到机器B的FIN包时,理想状态下,确实是可以直接关闭连接了;但是:

    我们知道网络是不稳定的,可能机器B 发送了一些数据还没到达(比FIN包慢);

    同时回复的ACK包可能丢失了,机器B会重传FIN包;

如果此时机器A马上关闭连接,会导致数据不完整、机器B无法释放连接等问题。所以此时机器A需要等待2个报文生存最大时长,确保网络中没有任何遗留报文了,再关闭连接
5. 最后,机器A等待两个报文存活最大时长之后,机器B 接收到ACK报文之后,均关闭连接,进入CLASED状态
双方之间4次互相发送报文来断开连接的过程,就是四次挥手。
现在,对于为什么握手是三次挥手是四次、一定要三次/四次吗、为什么要停留2msl再关闭连接等等这些问题,就都解决了。
/ UDP协议 /
运输层协议除了TCP,还有大名鼎鼎的UDP。如果说TCP凭借他完善稳定的功能独树一帜,那UDP就是精简主义乱拳打死老师傅。
UDP只实现了运输层最少的功能:进程间通信。对于应用层传下来的数据,UDP只是附加一个首部就直接交给网络层了。UDP的头部非常简单,只有三部分:

源端口、目标端口:端口号用来区分主机的不同进程

校验码:用于校验数据包在传输的过程中没有出现错误,例如某个1变成了0

长度:报文的长度


所以UDP的功能也只有两个:校验数据报是否发生错误、区分不同的进程通信。但,TCP的功能虽然多,但同时也是要付出相对应的代价。例如面向连接的特性,在建立和断开连接的时候会有开销;拥塞控制的特性,会限制传输的上限等等。下面来罗列一下UDP的优缺点:

UDP的缺点

无法保证消息完整、正确到达,UDP是一个不可靠的传输协议;

缺少拥塞控制容易互相竞争资源导致网络系统瘫痪

UDP的优点

效率更快;不需要建立连接以及拥塞控制

连接更多的客户;没有连接状态,不需要为每个客户创建缓存等

分组首部字节少,开销小;TCP首部固定首部是20字节,而UDP只有8字节;更小的首部意味着更大比例的数据部分

在一些需要高效率允许可限度误差的场景下可以使用。如直播场景,并不需要保证每个数据包都完整到达,允许一定的丢包率,这个时候TCP的可靠特性反而成为了累赘;精简的UDP更高的效率是更加适合的选择

可以进行广播;UDP并不是面向连接的,所以可以同时对多个进程进行发送报文

UDP适用场景


UDP适用于对传输模型需要应用层高度自定义、允许出现丢包、需要高效率的场景、需要广播;例如

视屏直播

DNS

RIP路由选择协议


/ 其他补充 /

分块传输


我们可以发现,运输层在传输数据的时候,并不是把整个数据包加个首部直接发送过去,而是会拆分成多个报文分开发送;那他这样做原因是什么?
有读者可能会想到:数据链路层限制了数据长度只能有1460。那数据链路层为什么要这么限制?他的本质原因就是:网络是不稳定的。如果报文太长,那么极有可能在传输一般的时候突然中断了,这个时候就要整个数据重传,效率就降低了。把数据拆分成多个数据报,那么当某个数据报丢失,只需要重传该数据报即可。
那是不是拆分得越细越好?报文中数据字段长度太低,会使得首部的占比太大,这样首部就会成为网络传输最大的负担了。例如1000字节,每个报文首部是40字节,如果拆分成10个报文,那么只需要传输400字节的首部;而如果拆分成1000个,那么需要传输40000字节的首部,效率就极大地降低了。

路由转换


先看下图:

正常情况下,主机A的数据包可以又 1-3-6-7路径进行传送

如果路由3坏掉了,那么可以从 1-4-6-7进行传送

如果4也坏掉了,那么只能从2-5-6-7传送

如果5坏掉了,那么就中断线路了


可以看出来,使用路由转发的好处是:提高网络的容错率,本质原因依旧是网络是不稳定的 。即使坏掉几个路由器,网络依旧畅通。但是如果坏掉路由器6那就直接导致主机A和主机B无法通信,所以要避免这种核心路由器的存在。
使用路由的好处还有:分流。如果一条线路太拥堵,可以从别的路线进行传输,提高效率。

粘包与拆包


在面向字节流那一小节讲过,TCP不懂这些数据流的意义,他只知道从应用层拿到数据流,切割成一份份报文,然后发送给目标对象。而如果应用层传输下来的是两个数据包,那么极有可能出现这种情况:

应用层需要向目标进程发送两份数据,一份音频,一份文本

TCP只知道接收到一个流,并把流拆分成4段进行发送

中间第二个报文的数据就出现两个文件的数据混在一起,这就是粘包

目标进程应用层在接收到数据之后,需要把这些数据拆分成正确的两个文件,就是拆包


粘包与拆包都是应用层需要解决的问题,可以在每个文件的最后附加上一些特殊的字节,如换行符;或者控制每个报文只包含一个文件的数据,不足的用0补充等等。

恶意攻击


TCP的面向连接特点可能会被恶意的人利用,对服务器进行攻击。
前面我们知道,当我们向一个主机发送syn包请求创建连接时,服务器会为我们创建缓冲区等,然后向我们返回syn ack报文;如果我们伪造IP和端口,向一个服务器进行海量的请求,会使得服务器创建了大量的创建一半的TCP连接,使得其无法正常响应用户的请求,导致服务器瘫痪。
解决的方法可以有限制IP的创建连接数、让创建一半的tcp连接在更短的时间内自行关闭、延缓接收缓冲区内存的分配等等。

长连接


我们向服务器的每一次请求都需要创建一个TCP连接,服务器返回数据之后就会关闭连接;如果在短时间内有大量的请求,那么频繁创建TCP连接关闭TCP连接是一个很浪费资源的行为。所以我们可以让TCP连接不要关闭,在这个期间进行请求,提高效率。需要注意长连接维持时间、创建条件等,避免被恶意利用创建大量的长连接,消耗殆尽服务器的资源。
/ 最后 /
以前学习的时候觉得这些东西好像没什么卵用,貌似就是用来考试的。事实上,在没应用到的时候,对这些知识很难有更深层次的认知,例如现在我看上面的总结,很多只是表面上的认知,不知道他背后代表的真正含义。

但当我学习得更加广泛、深入,会对这些知识有越来越深刻的认识。有那么几个瞬间觉得:哦原来那个东西是这样运用的,那个东西是这样的啊,原来学了是真的有用。
现在可能学了之后没有什么感觉,但是当用到或者学到相关的应用时,会有一个顿悟感,会瞬间收获很多。
1
来源:https://juejin.cn/user/3931509313252552/posts

免责声明:本文由用户上传,如有侵权请联系删除!