TCP(Transimision Control Protocal)是一种可靠的、面向连接的协议,传输效率低。
OSI是Open System Interconnection的缩写,意为开放式系统互联。国际标准化组织(ISO)制定了OSI模型,该模型定义了不同计算机互联的标准,是设计和描述计算机网络通信的基本框架。OSI模型把网络通信的工作分为7层,分别是物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。
上面是基本网络模型的组成,再来看一下tcp/ip的参考模型
TCP/IP是传输控制协议/网络互联协议的简称。早期的TCP/IP模型是一个四层结构,从下往上依次是网络接口层、互联网层、传输层和应用层。后来在使用过程中,借鉴OSI七层参考模型,将网络接口层划分为了物理层和数据链路层,形成五层结构。
我们看到了tcp/ip网络模型把osi中的应用层、表示层、会话层合并成了应用层。
Tcp协议实际上是在传输层。传输层是面向连接的、可靠的的进程到进程通信的协议。TCP提供全双工服务,即数据可在同一时间双向传播。TCP将若干个字节构成一个分组,此分组称为报文段(Segment)。提供了一种端到端的连接。 传输层的协议主要是TCP 。
源端口号和目标端口号,计算机通过端口号识别访问哪个服务,比如http服务或ftp服务,发送方端口号是进行随机端口,目标端口号决定了接收方哪个程序来接收。
32位序列号 TCP用序列号对数据包进行标记,以便在到达目的地后重新重装,假设当前的序列号为s,发送数据长度为 l,则下次发送数据时的序列号为 s + l。在建立连接时通常由计算机生成一个随机数作为序列号的初始值确认应答号 它等于下一次应该接收到的数据的序列号。假设发送端的序列号为 s,发送数据的长度为 l,那么接收端返回的确认应答号也是 s + l。发送端接收到这个确认应答后,可以认为这个位置以前所有的数据都已被正常接收。
首部长度:TCP 首部的长度,单位为 4 字节。如果没有可选字段,那么这里的值就是 5。表示 TCP 首部的长度为 20 字节。
控制位 TCP的连接、传输和断开都受这六个控制位的指挥
PSH(push急迫位) 缓存区将满,立刻传输速度
RST(reset重置位) 连接断了重新连接
URG(urgent紧急位) 紧急信号
ACK(acknowledgement 确认)为1表示确认号
SYN(synchronous建立联机) 同步序号位
TCP建立连接时要将这个值设为1
FIN发送端完成位,提出断开连接的一方把FIN置为1表示要断开连接
窗口值说明本地可接收数据段的数目,这个值的大小是可变的。当网络通畅时将这个窗口值变大加快传输速度,当网络不稳定时减少这个值可以保证网络数据的可靠传输。它是来在TCP传输中进行流量控制的 窗口大小:用于表示从应答号开始能够接受多少个 8 位字节。如果窗口大小为 0,可以发送窗口探测。
效验和: 用来做差错控制,TCP校验和的计算包括TCP首部、数据和其它填充字节。在发送TCP数据段时,由发送端计算校验和,当到达目的地时又进行一次检验和计算。如果两次校验 和一致说明数据是正确的,否则 将认为数据被破坏,接收端将丢弃该数据 紧急指针:尽在 URG(urgent紧急) 控制位为 1 时有效。表示紧急数据的末尾在 TCP数据部分中的位置。通常在暂时中断通信时使用(比如输入 Ctrl + C)。
在Node.js中,net模块实现了基于TCP的数据通信
我们来看一下创建一个简单的tcp服务端的方法
0 1 2 3 4 5 6 7 8 |
创建TCP服务器 let net = require('net'); let server = net.createServer(function(socket){ console.log('客户端已经链接'); }) server.listen('8080',function(){ console.log('server is run in 8080'); }) 复制代码 |
这样就使用nodejs创建了一个简单的tcp服务器; 注意:createServer的回调中有一个参数socket,指的是TCP服务器监听的socket端口对象。
设置最大链接数以及监听客户端的链接数量
0 1 2 3 4 |
server.getConnections((err,count)=>{//获得当前的链接个数 console.log('已经链接'+count+'个用户') }); server.maxConnections = 2;//限制当前的最大连接个数为2 复制代码 |
socket对象
net.Socket代表一个socket端口对象,他是一个双工流(可读可写)
address
获取客户端地址
0 1 2 3 4 5 |
let net = require('net'); let server = net.createServer(function(socket){ console.log(socket.address()) }); server.listen(8080); 复制代码 |
结果:
pipe
0 1 2 3 4 5 6 7 8 9 |
let ws = fs.createWriteStream(path.join(__dirname,'./1.txt')); let server = net.createServer(function(socket){ socket.pipe(ws,{end:false}); // 第二个参数让文件不自动关闭 setTimeout(function(){ ws.end(); //关闭可写流 socket.unpipe(ws); //取消管道 },15000); }); |
可以看到我们创建了一个可写流,我们可以把socket(双工流)中的东西pipe到可写流中。
创建server
0 1 2 3 4 5 6 7 8 9 10 11 12 13 |
let net = require('net'); let server = net.createServer(function(socket){ socket.setEncoding('utf8'); socket.on('data',function(data){ console.log(data); //获取从客户端发来的数据 }) socket.end('服务端关闭'); }); server.on('connection',function(){ console.log('客户端链接'); }) server.listen(8080); |
创建client
0 1 2 3 4 5 6 7 |
let net = require('net'); //createConnection第一个参数是你要连接到服务器的端口号 let socket = net.createConnection(8080,function(){ socket.write('hello'); //向服务端发送 socket.on('data',function(data){ console.log(data); //接受服务端数据 }); }); |