深入浅出讲解 NAT 和 UDP/TCP 点对点通讯

2023-11-03



深入浅出讲解 NAT 和 UDP/TCP 点对点通讯

转自:http://blog.csdn.net/g_brightboy/article/details/12704933

一、什么是NAT?为什么要使用NAT?
NAT是将私有地址转换为合法IP地址的技术,通俗的讲就是将内网与内网通信时怎么将内网私有IP地址转换为可在网络中传播的合法IP地址。NAT的出现完美地解决了lP地址不足的问题,而且还能够有效地避免来自网络外部的攻击,隐藏并保护网络内部的计算机。

二、NAT的分类
STUN标准中,根据内部终端的地址(LocalIP:LocalPort)到NAT出口的公网地址(PublicIP:PublicPort)的影射方式,把NAT分为四种类型。

1. Full Cone NAT(完全锥型)
   内网主机建立一个socket(LocalIP:LocalPort)第一次使用这个socket给外部主机发送数据时NAT会给其分配一个公网(PublicIP:PublicPort),以后用这个socket向外面任何主机发送数据都将使用这对(PublicIP:PublicPort)。此外,任何外部主机只要知道这个(PublicIP:PublicPort)就可以发送数据给(PublicIP:PublicPort),内网的主机就能收到这个数据包。
2. Restricted Cone NAT(限制锥型)
   内网主机建立一个socket(LocalIP:LocalPort) 第一次使用这个socket给外部主机发送数据时NAT会给其分配一个公网(PublicIP:PublicPort),以后用这个socket向外面任何主机发送数据都将使用这对(PublicIP:PublicPort)。此外,如果任何外部主机想要发送数据给这个内网主机,只要知道这个(PublicIP:PublicPort)并且内网主机之前用这个socket曾向这个外部主机IP发送过数据。只要满足这两个条件,这个外部主机就可以用自己的(IP,任何端口)发送数据给(PublicIP:PublicPort),内网的主机就能收到这个数据包。
3. Port Restricted Cone NAT(端口限制锥型)
   内网主机建立一个socket(LocalIP:LocalPort) 第一次使用这个socket给外部主机发送数据时NAT会给其分配一个公网(PublicIP:PublicPort),以后用这个socket向外面任何主机发送数据都将使用这对(PublicIP:PublicPort)。此外,如果任何外部主机想要发送数据给这个内网主机,只要知道这个(PublicIP:PublicPort)并且内网主机之前用这个socket曾向这个外部主机(IP:Port)发送过数据。只要满足这两个条件,这个外部主机就可以用自己的(IP:Port)发送数据给(PublicIP:PublicPort),内网的主机就能收到这个数据包。
4. Symmetric NAT(对称型)
   内网主机建立一个socket(LocalIP:LocalPort),当用这个socket第一次发数据给外部主机1时,NAT为其映射一个(PublicIP1:Port1),以后内网主机发送给外部主机1的所有数据都是用这个(PublicIP1:Port1),如果内网主机同时用这个socket给外部主机2发送数据,NAT会为其分配一个(PublicIP2:Port2), 以后内网主机发送给外部主机2的所有数据都是用这个(PublicIP2:Port2)。如果NAT有多于一个公网IP,则 PublicIP1 和 PublicIP2 可能不同,如果NAT只有一个公网IP,则 Port1 和 Port2 肯定不同,也就是说一定不能是 PublicIP1 等于 PublicIP2 且 Port1 等于 Port2。此外,如果任何外部主机想要发送数据给这个内网主机,那么它首先应该收到内网主机发给他的数据,然后才能往回发送,否则即使他知道内网主机的一个(PublicIP:Port)也不能发送数据给内网主机,这种NAT无法实现P2P通信,但是如果另一方是Full Cone NAT,还是可以实现穿透的,下面我会详细分析各种类型NAT穿透的情况。

NAT 功能通常被集成到路由器、防火墙、ISDN路由器或者单独的NAT设备中。所以我们大家很少会知道NAT,上面NAT类型的概念描述是比较通俗的,但为了更便于理解,我再举例阐述一下NAT的原理。
 

现有通信的双方A和B,当A和B都是在公网的时候,通信是不用NAT的。假设A在内网,内网IP是192.168.1.3,端口号是5000,A经过NAT后的IP是221.221.221.100,端口号是8000,B的IP是202.105.124.100,端口是8500。如果B要去主动连接A,即使B知道A经过NAT后的IP和端口也是无法连接成功的,因为A没有向B(202.105.124.100:8500)发送过数据,所以B的数据包会被A的NAT丢弃,于是连接失败。但是A如果去主动连接B,由于B是在公网,所以会连接成功,通信也就会建立。这也就是反弹连接木马“反弹”二字的精髓。
 当客户端A和B都是处在内网的时候,双方由于都不知道对方的公网IP和端口,就会无从下手,所以要在客户端A和B之间架设一台服务器S来为它们牵线,而且S是处在公网,以保证A和B都能连接到S。客户端A和B登录时都首先连接S,S就会知道A和B经过NAT后的IP和端口,当A想要连接B时,就像S发出请求,S会把B经过NAT后的IP和端口告诉A,同时S向B发送A经过NAT后的IP和端口,并要求B发送数据给A,B发送数据到达A时会被A的NAT抛弃,但是B的NAT会有B发送数据到A的记录,这是A再向B发送数据时就会被B的NAT放行,因为B曾经向A的外网IP和端口发送过数据。可能有点乱,下面以故事的形式叙述一下这个情景。
 人物:A(男) NAT_A(A家接线员) B(女) NAT_B (B家接线员) S
场景介绍:A想认识B,但是不知道B的电话,S跟A、B都是朋友,并且知道A和B的电话。接线员的职责:对往外转接的电话不做询问,对往内转接的电话则要过滤以免有骚扰电话。过滤规则:在一定时间内没有拨打过的号码就过滤。
 首先A给S打电话:
A说:我想认识你朋友B,你把她电话给我呗。
S说:行,她的电话是PublicIP_B,我让她先给你打个电话,要不她家接线员不帮你转接。
A说:好。

S跟B打电话:
S说:我有一个朋友A,人挺好的,他想认识你,你给他打个电话,他的电话号码是PublicIP_A。
B说:行,打完告诉你。
S说:好的。

B打电话到A家,B家接线员NET_B看到女主人想往PublicIP_A打电话就转接到A家了,同时把号码PublicIP_A记录下来,A家接线员NAT_A一看号码是个近期没打过的号,就给挂断了。

B给S打电话:
B说:我打完电话了
S说:好,等着吧,一会他就给你打进来了。

S给A打电话:
S说:他给你打完电话了,你快点给她打。

A打电话到B家, A家接线员NET_A看到男主人想往PublicIP_B打电话就转接到B家了,B家接线员NET_B看到是刚刚拨过的PublicIP_A号码打过来的,就转接给B了,A和B的电话也就打通了。
A和B通话:
A说:电话终于打通了,想认识你挺困难的。
B说:是啊。


以上虽然和实际不太一样,但穿透的整体过程基本就是这样。A往B发送数据的唯一阻碍就是NET_B,所以想要成功发送数据,必须把NET_B穿一个洞,A是无法完成这项工作的,所以就得让B完成这个打洞操作,也就是让B往A发送数据,这样NET_B就会误以为A发送的数据是上次会话的一部分从而不予阻拦。
 但是,由于NAT的类型没有一个统一的标准,所以NAT穿透使用的技术有很多种,穿透的成功率也不一样。还有些NAT类型的内网之间几乎无法穿透。下面我们用实例详细分析一下各种NAT类型穿透的可行性。

A机器在私网(192.168.0.3)
A侧NAT服务器(221.221.221.100)
B机器在另一个私网(192.168.0.5)
B侧NAT服务器(210.30.224.70)
C机器在公网(210.202.14.36)作为A和B之间的中介
A机器连接C机器,假使是A(192.168.0.3:5000)-> A侧NAT(转换后221.221.221.100:8000)-> C(210.202.14.36:2000)
B机器也连接C机器,假使是B(192.168.0.5:5000)-> B侧NAT(转换后210.30.224.70:8000)-> C(210.202.14.36:2000)
A机器连接过C机器后,A向C报告了自己的内部地址(192.168.0.3:5000),此时C不仅知道了A的外部地址(C通过自己看到的221.221.221.100:8000)也知道了A的内部地址。同理C也知道了B的外部地址(210.30.224.70:8000)和 内部地址(192.168.0.5:5000)。之后,C作为中介,把A的两个地址告诉了B,同时也把B的两个地址告诉了A。
假设A先知道了B的两个地址,则A从192.168.0.3:5000处同时向B的两个地址192.168.0.5:5000和210.30.224.70:8000发包,由于A和B在两个不同的NAT后面,故从A(192.168.0.3:5000)到B(192.168.0.5:5000)的包肯定不通,现在看A(192.168.0.3:5000)到B(210.30.224.70:8000)的包,分如下两种情况:
1. B侧NAT属于Full Cone NAT
   则无论A侧NAT属于Cone NAT还是Symmetric NAT,包都能顺利到达B。如果程序设计得好,使得B主动到A的包也能借用A主动发起建立的通道的话,则即使A侧NAT属于Symmetric NAT,B发出的包也能顺利到达A。
结论1:只要单侧NAT属于Full Cone NAT,即可实现双向通信。
2. B侧NAT属于Restricted Cone或Port Restricted Cone
则包不能到达B。再细分两种情况
(1)A侧NAT属于Restricted Cone或Port Restricted Cone
     虽然先前那个初始包不曾到达B,但该发包过程已经在A侧NAT上留下了足够的记录:A(192.168.0.3:5000)->(221.221.221.100:8000)->B(210.30.224.70:8000)。如果在这个记录没有超时之前,B也重复和A一样的动作,即向A(221.221.221.100:8000)发包,虽然A侧NAT属于Restricted Cone或Port Restricted Cone,但先前A侧NAT已经认为A已经向B(210.30.224.70:8000)发过包,故B向A(221.221.221.100:8000)发包能够顺利到达A。同理,此后A到B的包,也能顺利到达。
结论2:只要两侧NAT都不属于Symmetric NAT,也可双向通信。换种说法,只要两侧NAT都属于Cone NAT,即可双向通信。
(2)A侧NAT属于Symmetric NAT
     因为A侧NAT属于Symmetric NAT,且最初A到C发包的过程在A侧NAT留下了如下记录:A(192.168.0.3:5000)->(221.221.221.100:8000)-> C(210.202.14.36:2000),故A到B发包过程在A侧NAT上留下的记录为:
A(192.168.0.3:5000)->(221.221.221.100:8001)->B(210.30.224.70:8000)(注意,转换后端口产生了变化)。而B向A的发包,只能根据C给他的关于A的信息,发往A(221.221.221.100:8000),因为A端口受限,故此路不通。再来看B侧NAT,由于B也向A发过了包,且B侧NAT属于Restricted Cone或Port Restricted Cone,故在B侧NAT上留下的记录为:B(192.168.0.5:5000)->(210.30.224.70:8000)->A(221.221.221.100:8000),此后,如果A还继续向B发包的话(因为同一目标,故仍然使用前面的映射),如果B侧NAT属于Restricted Cone,则从A(221.221.221.100:8001)来的包能够顺利到达B;如果B侧NAT属于Port Restricted Cone,则包永远无法到达B。
结论3:一侧NAT属于Symmetric NAT,另一侧NAT属于Restricted Cone,也可双向通信。
反过来想,则可以得出另一个结论:两个都是Symmetric NAT或者一个是Symmetric NAT、另一个是Port Restricted Cone,则不能双向通信,因为NAT无法穿透;(me:symmetricNAT之所以难以穿透是因为:AB“汇报”给S的外部端口与连接A/B时的端口不同所致。因为与不同主机通信时,NAT为其分配不同外部端口。)
上面的例子虽然只是分析了最初发包是从A到B的情况,但是,由于两者的对称性,前面得出的几条结论没有方向性,双向都适用。
我们上面得出了四条结论,natcheck网站则把他归结为一条:只要两侧NAT都属于Cone NAT(含Full Cone、Restricted Cone和Port Restricted Cone三者),即可双向通信。没有把我们的结论3包括进去。
 一般情况下,只有比较注重安全的大公司会使用Symmetric NAT,禁止使用P2P类型的通信,很多地方使用的都是Cone NAT,因此穿透技术还是有发展前景的。

三、使用UDP、TCP穿透NAT
上面讲的情况可以直接应用于UDP穿透技术中,使用TCP 协议穿透NAT 的方式和使用UDP 协议穿透NAT 的方式几乎一样,没有什么本质上的区别,只是将无连接的UDP 变成了面向连接的TCP 。

值得注意是,B在向A打洞时,发送的SYN 数据包,而且同样会被NAT_A 丢弃。同时,B需要在原来的socket 上监听,由于重用socket ,所以需要将socket 属性设置为SO_REUSEADDR 。A向B发送连接请求。同样,由于B到A方向的孔已经打好,所以连接会成功,经过3 次握手后,A到B之间的连接就建立起来了。

具体过程如下:
 (me:UDP过程不需要创建两个socket,过程与上述描述的方法一致,不需再罗列)
1. S启动两个网络侦听,一个叫【主连接】侦听,一个叫【协助打洞】的侦听。
2. A和B分别与S的【主连接】保持联系。
3. 当A需要和B建立直接的TCP连接时,首先连接S的【协助打洞】端口,并发送协助连接申请。同时在该端口号上启动侦听。注意由于要在相同的网络终端上绑定到不同的套接字上,所以必须为这些套接字设置 SO_REUSEADDR 属性(即允许重用),否则侦听会失败。
4. S的【协助打洞】连接收到A的申请后通过【主连接】通知B,并将A经过NAT-A转换后的公网IP地址和端口等信息告诉B。
5. B收到S的连接通知后首先与S的【协助打洞】端口连接,随便发送一些数据后立即断开,这样做的目的是让S能知道B经过NAT-B转换后的公网IP和端口号。
6. B尝试与A的经过NAT-A转换后的公网IP地址和端口进行connect,大多数路由器对于不请自到的SYN请求包直接丢弃而导致connect失败,但NAT-B会纪录此次连接的源地址和端口号,为接下来真正的连 接做好了准备,这就是所谓的打洞,即B向A打了一个洞,下次A就能直接连接到B刚才使用的端口号了。
7. 客户端B打洞的同时在相同的端口上启动侦听。B在一切准备就绪以后通过与S的【主连接】回复消息“我已经准备好”,S在收到以后将B经过NAT-B转换后的公网IP和端口号告诉给A。
8. A收到S回复的B的公网IP和端口号等信息以后,开始连接到B公网IP和端口号,由于在步骤6中B曾经尝试连接过A的公网IP地址和端口,NAT-B纪录了此次连接的信息,所以当A主动连接B时,NAT-B会认为是合法的SYN数据,并允许通过,从而直接的TCP连接建立起来了。

----------------------------------------------------

原文:http://bbs.pediy.com/showthread.php?t=131961

参考网址:
http://midcom-p2p.sourceforge.net/draft-ford-midcom-p2p-01.txt
http://www.vckbase.com/document/viewdoc/?id=1773

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

深入浅出讲解 NAT 和 UDP/TCP 点对点通讯 的相关文章

  • 链接错误:命令行中缺少 DSO

    我对 Linux 使用 Ubuntu 14 04 LTS 64 位 相当陌生 来自 Windows 并且正在尝试移植我现有的 CUDA 项目 当通过链接时 usr local cuda bin nvcc arch compute 30 co
  • Python 无法在 git bash 命令行中工作

    Python 不会在 git bash Windows 中运行 当我在命令行中输入 python 时 它会将我带到一个空行 而不会像在 Powershell 中一样显示它已输入 python 2 7 10 它没有给我错误消息 但 pytho
  • .wav 文件长度/持续时间,无需读入文件

    有没有办法提取有关 wav 文件长度 持续时间的信息 而无需在 R 中读取文件 我有数千个这样的文件 如果我必须阅读每个文件才能找到其持续时间 那将需要很长时间 Windows 文件资源管理器为您提供了打开 长度 字段的选项 并且您可以查看
  • 如何更改选项卡控件的名称

    我在 C WinForms 应用程序中使用选项卡控件 我想更改选项卡的标题 默认情况下它们是 tabPage1 tabPage2 等 一种无需代码即可实现的懒惰方法 选择选项卡控件 Go to properties use F4 to do
  • 正则表达式删除块注释也删除 * 选择器

    我正在尝试使用 bash 从 css 文件中删除所有块注释 我有以下 sed 命令的正则表达式 sed r s w s w d 这可以很好地去除块注释 例如 This is a comment this is another comment
  • LINUX:如何锁定内存中进程的页面

    我有一个 LINUX 服务器 运行一个具有大量内存占用的进程 某种数据库引擎 该进程分配的内存太大 需要将其中一部分换出 换出 我想做的是将所有其他进程 或正在运行的进程的子集 的内存页面锁定在内存中 以便只有数据库进程的页面被换出 例如
  • 嵌入清单文件以要求具有 mingw32 的管理员执行级别

    我正在 ubuntu 下使用 i586 mingw32msvc 交叉编译应用程序 我很难理解如何嵌入清单文件以要求 mingw32 具有管理员执行级别 对于我的例子 我使用了这个hello c int main return 0 这个资源文
  • 如何从 C++ 程序中重新启动 Linux?

    我有一个 Qt 4 GUI 我需要在下拉菜单中提供一个选项 允许用户选择重新启动计算机 我意识到这对于以其他方式重新启动计算机的能力来说似乎是多余的 但选择需要保留在那里 我尝试使用 system 来调用以下内容 suid root she
  • Linux无法删除文件

    当我找到文件时 我在删除它们时遇到问题 任务 必须找到带有空格的文件并将其删除 我的尝试 rm find L root grep i 但我有错误 rm cannot remove root test No such file or dire
  • 为什么 fork 炸弹没有使 android 崩溃?

    这是最简单的叉子炸弹 我在许多 Linux 发行版上执行了它 但它们都崩溃了 但是当我在 android 终端中执行此操作时 即使授予后也没有效果超级用户权限 有什么解释为什么它没有使 Android 系统崩溃吗 一句话 ulimit Li
  • 如何在不使用 IDE 的情况下在 Linux 上运行 Java 项目

    我是 Java 新手 基本上 我开发了一个java项目 其中包含Eclipse中的多个Java包 该项目在我安装了 redhat Linux 的桌面上运行正常 然而 我需要在一个更强大的没有安装X11的Linux服务器 redhat ent
  • 在 docker 中重定向命令输出

    我想为我的服务器做一些简单的日志记录 它是一个在 Docker 容器中运行的小型 Flask 应用程序 这是 Dockerfile Dockerfile FROM dreen flask MAINTAINER dreen WORKDIR s
  • 从命令行运行 R 代码 (Windows)

    我在名为 analysis r 的文件中有一些 R 代码 我希望能够从命令行 CMD 运行该文件中的代码 而无需通过 R 终端 并且我还希望能够传递参数并在我的代码中使用这些参数 例如就像下面的伪代码 C gt execute r scri
  • vmsplice() 和 TCP

    在原来的vmsplice 执行 有人建议 http lwn net Articles 181169 如果您的用户态缓冲区是管道中可容纳的最大页面数的 2 倍 则缓冲区后半部分成功的 vmsplice 将保证内核使用缓冲区的前半部分完成 但事
  • 设置 Form.KeyPreview = true 的缺点?

    我想知道 Form KeyPreview 属性实际上有什么用处 它为什么存在以及将其设置为 true 会带来什么 风险 我想它一定有some负面影响 否则它根本不应该存在 或者至少默认情况下是正确的 EDIT 我很清楚what确实如此 我问
  • 取消后调用 boost::asio 异步处理程序没有错误

    我的代码在单个线程中使用 boost asio 和 io service 来执行各种套接字操作 所有操作都是异步的 每个处理程序都依赖于boost system error code 特别boost asio error operation
  • 在 Cygwin 软件包列表中找不到 Openssl

    这里说的是https github com joyent node wiki Building node js on Cygwin Windows https github com joyent node wiki Building nod
  • 找不到包“gdk-pixbuf-2.0”

    我正在尝试在 Amazon Linux 发行版实例上构建 librsvg 我已经通过 yum 安装了大部分依赖项 其中一些在实例上启用的默认 yum 存储库中不可用 因此必须从头开始构建它们 我已经走了很远 但还停留在最后一点 跑步时sud
  • 仅使用containerd(不使用Docker)修剪容器镜像

    如果我刚刚containerd安装在 Linux 系统上 即 Docker 是not安装 如何删除未使用的容器映像以节省磁盘空间 Docker 就是这么方便docker system prune https docs docker com
  • 如何从Windows阻止社交媒体[关闭]

    Closed 这个问题需要调试细节 help minimal reproducible example 目前不接受答案 我想根据时间阻止我的电脑上的社交媒体 晚上 9 点后屏蔽 上午 11 点后解锁 如家长控制 我尝试过关注但失败了 创建了

随机推荐

  • XSS漏洞的与标签的那些个事~

    XSS漏洞的与标签的那些个事 0x00 div 0x01 textarea 0x02 input 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F 0x10 0
  • 使用随机森林进行特征选择

    绘制随机森林每棵树的决策边界 首先导入必要的库函数 from sklearn ensemble import RandomForestClassifier from sklearn datasets import make moons fr
  • 简易职工管理系统

    简易职工管理系统 问题描述 设计思路 代码部分 总结 样例 问题描述 1 问题描述 对单位的职工进行管理 包括插入 删除 查找 排序等功能 2 要求 职工对象包括姓名 性别 出生年月 工作年月 学历 职务 住址 电话等信息 1 新增一名职工
  • java 反转链表、合并链表

    第一个问题 反转链表 1 题目描述 输入一个链表 反转链表后 输出新链表的表头 2 解题思路 定义三个指针 第一个指针指向当前正在处理的节点 第二个指针指向当前处理节点的下一个节点 该指针是为了保证正常的遍历完顺序链表的所有节点 第三个指针
  • SQLServer创建用户登录

    创建用户登录注意事项 密码是区分大小写的 只有创建SQL Server登录时 才支持对密码预先进行哈希运算 如果指定MUST CHANGE 则CHECK EXPIRATION和 CHECK POLICY必须设置为 ON 否则 该语句将失败
  • 硬件问题总结

    STM32最小系统 F103 1 STM32最小系统 1 1 供电 3 3V 通常使用ASM1117进行5V转3 3V 注意 给单片机供电时要加0 1uf滤波电容 电容要尽可能靠近单片机 1 2外部晶振电路 一般使用外部8M晶振来作为STM
  • C++访问类中私有成员变量的方法

    原则上 C 类中私有变量不允许在类之外的其他任何地方访问 一般来说功能完善的类都会提供get set方法来操作类属性值 还有就是就是通过友元访问 但是 但如果没有get set方法都没有提供 也没有定义友元 比如使用的是第三方提供的 o 或
  • 华为OD机试(B卷)

    华为OD机试 B卷 有幸接到了华为OD的机试邀请 三道算法题 比较幸运的是 最后一题 也相对来说不算太难 没有抽到动态规划 还是花了大约90分钟 三道题都通过了 最终拿到了满分 第一题第二题都相对来说比较基础 由于平时也没有刷题的习惯 用自
  • Using MySQL Enterprise Backup Tools

    本文主要介绍MySQL Enterprise Backup的安装以及使用 1 安装 从官方网站下载MySQL Enterprise Backup安装包 推荐下载rpm软件包 root mydb01 rpm qpl meb 4 1 1 el7
  • Uncaught (in promise) DOMException: Failed to execute 'open' on 'XMLHttpRequest': Invalid URL

    解决方案 url前面一定要加http
  • Python版的BS期权定价模型和希腊值分析

    我比较懒 主要是打理自己的github的更新 是关于量化投资 机器学习策略相关的项目 https github com Neural Finance 这次更新一个我在学习期权定价过程中 Black Scholes Model 和相关的希腊值
  • webpack 中压缩代码

    阅读 深入浅出的webpack 压缩代码 一 为什么要压缩代码 浏览器通过服务器访问网页时获取的JavaScript CSS资源都是文本形式的 文件越大 网页加载的时间越长 对这些资源进行压缩 1 可以提升网页加载速度和减少网络传输流量 2
  • Java基础知识查阅表(四)[线程、网络编程、注解、java8新特性]

    文章目录 Java中的线程 线程的分类 线程调度规则 获取线程的优先级 其他几个方法 线程的通信 守护线程 线程的生命周期 线程安全问题 线程安全的类 ReentrantLock加锁 关于锁的面试题 定时器Timer Java网络编程 两个
  • 数据结构—顺序表基本操作(c语言代码)

    顺序表 计算机内部存储一张线性表是用一组连续地址内存单元 这种存储结构即为顺序存储结构 这种结构下的线性表叫顺序表 顺序表有两种定义方法 1 静态定义 2 动态生成 顺序表是最简单的一种线性存储结构 优点 构造简单 操作方便 通过顺序表的首
  • python装饰器原理

    装饰器作用 装饰器在实际开发中应用广发 如 1 引入日志 2 函数执行时间统计 3 执行函数前预备处理 4 执行函数后清理功能 5 权限校验等场景 6 缓存 装饰器可以实现在不修改之前已经写好并且封装好的代码的前提下对之前的代码进行功能上的
  • LASlib/LAStools:Win10 + VS2017 编译LASlib/LAStools

    一 下载解压 下载地址 http lastools github io download LAStools zip 解压地址 G LAStools 二 编译 2 1 打开 用VS2017打开lastools dsw 历史原因 一直点确定就可
  • Linux shell 从文件中随机选择内容

    如果需要从文件中随机选择一定行的内容 可以借助sort 命令 如下 使用sort 命令将文件随机排序 选择前100行 sort random sort file head n 100
  • 《自然语言处理》第二次作业:语言模型和文本分类

    文章目录 作业要求 代码 读取数据集 建立二元语法模型 朴素贝叶斯分类 分类和评估 计算困惑度 完整代码 运行结果 作业要求 题目 语言模型和文本分类 数据集 text classification data用户评论 包括训练集 开发测试集
  • 三位数除以两位数怎么算竖式_四年级数学上册三位数除以两位数竖式笔算专项练习(10套)...

    四年级数学上册三位数除以两位数竖式笔算专项练习 一 三位数除以两位数的除法 包括以下两部分 一 三位数除以整十数 如 二 三位数除以两位数 二 除数是两位数的除法法则 从被除数左边的高位起 先用除数试除被除数的前两位数 如果它比除数小 再试
  • 深入浅出讲解 NAT 和 UDP/TCP 点对点通讯

    深入浅出讲解 NAT 和 UDP TCP 点对点通讯 转自 http blog csdn net g brightboy article details 12704933 一 什么是NAT 为什么要使用NAT NAT是将私有地址转换为合法I