TCP/IP协议栈
TCP/IP协议栈,包含了一系列构成互联网服务的网络协议,为计算机间的数据传输定义了从硬件到软件的一系列标准协议。

TCP/IP通信

TCP协议
TCP(Transmission Control Protocol传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。
应用层向TCP层发送用8位字节表示的数据流,TCP把数据流分区成适当的消息段。之后TCP把结果包传送给IP层,继续向后传输。
TCP为了保证不发生丢包,就给每一个包一个序号,同时序号也保证了传送到接收端的包的按序接收。接收端对已成功接收到的包发回一个相应的确认(ACK);如果发送端在合理的往返时延(RTT)内未收到确认,那么对应的数据包就被假设为已经丢失将会被重新重传。TCP用一个校验和函数来检验数据是否有误;在发送和接收时都要计算校验和,从而保证了可靠的数据传输。
TCP通信过程
基于TCP协议的应用程序分为服务端和客户端两部分。二者的工作时序为:
(1)开始监听
服务器端应用绑定在特定主机地址的特定端口上,监听客户端请求;
(2)建立连接
客户端应用主动发起到服务器应用的连接,经过三次握手,建立连接;
(3)数据传输
双方使用创建连接时各自创建的Socket套接字端口进行数据的读写;
(4)断开连接
通信结束,一方发起连接断开消息,经过四次挥手,连接正式断开。
TCP三次握手
TCP协议使用"三次握手"简历客户端到服务器端的连接。具体过程如下:

(1)客户端发送SYN(SEO=x)消息给服务器端,进入SYN-SEND状态。
(2)服务器端收到SYN消息,回应一个SYN(SEO=y)ACK(ACK=x+1)消息,进入SYN—RECV状态。
(3)客户端接收到服务端的SYN消息,回应一个ACK(ACK=y+1)消息,进入Established状态。
TCP四次挥手

建立连接需要三次握手,而终止一个连接需要四次挥手,这是由TCP的半关闭(half-close)造成的。具体过程如下:
(1)某个应用进程首先调用close,称该端执行"主动关闭"。该端的TCP于是发送一个FIN消息,表示数据完毕。
(2)接收到这个FIN的对端执行"被关闭",这个FIN由TCP确认,表示接收端应用进程在相应连接上再无额外数据可接收。
(3)一段时间后,接收到这个文件结束符的应用进程将调用close关闭它的套接字。这导致它的TCP也发送一个FIN。
(4)接收这个最终FIN的原发送端TCP(即执行主动关闭那一端)确认这个FIN。
Socket通信
Socket:套接字,本意插座,TCP应用中的服务器和客户端程序在建立好连接后,创建了一对相互连接的套接字对象,用于向对方写出数据,以及从对方处读取数据。

TCP应用中,服务器端应用为了与多个不同的客户端通信,会为每个连接到自己的客户端创建一个独立的Socket对象,用于与对方传输数据。

net模块
Node.js提供net模块,实现了面向连接的TCP数据通信,可用于创建TCP服务器端应用及客户端应用。
class:net.Server | TCP服务器对象 |
---|---|
class:net.Socket | 通信套接字对象 |
net.connect( ) | 连接到指定TCP服务器 |
net.createServer( ) | 创建TCP服务器 |
net.isIP( ) | 判断参数字符串是否为IP地址 |
net.isIPv4( ) | 判断参数字符串是否为IPv4地址 |
net.isIPv6( ) | 判断参数字符串是否为IPv6地址 |
net.Server对象
server.listen( ) | 监听指定端口和地址 |
---|---|
server.unref( ) | 若没有更多客户端连接了则退出服务器 |
server.ref( ) | 取消服务器退出 |
server.listening | 服务器当前是否处于监听状态 |
server.maxConnections | 设置服务器可以保持的最大连接数 |
server.getConnections( ) | 得到当前客户端连接数 |
server.close( ) | 保持现有连接,拒绝新连接 |
server.address( ) | 返回一个服务器端口和地址描述对象 |
net.Server对象事件
Event:listening | 开始监听端口 |
---|---|
Event:connection | 接收到客户端连接 |
Event:close | 服务器关闭事件;注意:只要还有客户端连接,此事件不会触发 |
Event:error | 服务器运行错误,如监听端口已被占用 |
net.Socket对象
socket.remoteAddress | 远程地址 |
---|---|
socket.remotePort | 远程端口 |
socket.localAddress | 本地地址 |
socket.localPort | 本地端口 |
socket.write | 写出数据 |
socket.end() | 结束写出,进入"半连接"状态 |
socket.destroy() | 在发生错误时,销毁套接字 |
socket.pause( ) | 暂停数据接收,暂存于缓冲区 |
socket.resume() | 继续数据接收 |
net.Socket对象事件
Event:connect | 连接到服务器 |
---|---|
Event:data | 读取到数据 |
Event:drain | 输出缓冲区已空 |
Event:error | 读写错误 |
Event:close | 完全关闭 |
Event:end | 对方已发出Fin包 |
创建TCP服务器
const net=require('net');
var server=net.createServer({
allowHalfOpen:false,//是否允许半连接,默认为false
pauseOnConnect:false//一旦建立连接,是否先暂停Socket,默认为false
},(socket)=>{ //连接监听器
console.log("接收到一个客户端连接!")
socket.on("data",(data)=>{ //读取到数据
console.log("读取到客户端数据:%s",data)
})
socket.write('hello\r\n');
socket.on('end',()=>{
console.log("连接已断开!");
})
});
server.on('error',(err)=>{
throw err;
})
server.listen('8000',()=>{
console.log('正在监听8000端口...')
})
处理连接请求,可以使用上述在createServer的回调中处理,也可以给server绑定监听函数,监听connect连接建立了再处理:
const net=require('net');
var server=net.createServer();//假如默认配置参数不需要修改可以省略不写
server.on('connection',(socket)=>{
server.getConnections((err,count)=>{
if(err) throw err;
console.log("服务器正在处理的客户端请求数为:%d!",count)
})
console.log("接收到一个客户端连接请求...");
})
server.on('error',(err)=>{
throw err;
})
server.listen('8000',()=>{
console.log('正在监听8000端口...')
})
//正在监听8000端口...
//接收到一个客户端连接!
//服务器正在处理的客户端请求数为:1!

假如我们只允许TCP服务器最多连接2个请求:
const net=require('net');
var server=net.createServer();
server.setMaxListeners=2; //设置服务器可以保持的最大连接数
server.on('connection',(socket)=>{
server.getConnections((err,count)=>{
if(err) throw err;
console.log("服务器正在处理的客户端请求数为:%d!",count)
})
console.log("接收到一个客户端连接请求...");
})
server.on('error',(err)=>{
throw err;
})
server.listen('8000',()=>{
console.log('正在监听8000端口...')
})

第一个连接和第二个连接请求连接成功,第三个连接请求无法连接。
我们也可以判断连接数当连接数达到3个或以上关闭服务器:
const net=require('net');
var server=net.createServer();//假如默认配置参数不需要修改可以省略不写
server.on('connection',(socket)=>{
server.getConnections((err,count)=>{
if(err) throw err;
console.log("服务器正在处理的客户端请求数为:%d!",count);
if(count>2){
server.close();
}
})
console.log("接收到一个客户端连接请求...");
})
server.on('error',(err)=>{
throw err;
})
server.listen('8000',()=>{
console.log('正在监听8000端口...')
})

sockte对象属性:
const net=require('net');
var server=net.createServer();//假如默认配置参数不需要修改可以省略不写
server.on('connection',(serverSocket)=>{
console.log("接收到一个客户端连接请求...");
console.log("服务器方端口:%s",serverSocket.localPort);
console.log("服务器方IP地址:%s",serverSocket.localAddress);
console.log("客户端方端口:%s",serverSocket.remotePort);
console.log("客户端方IP地址:%s",serverSocket.remoteAddress);
})
server.listen('8000',()=>{
console.log('正在监听8000端口...')
})
//正在监听8000端口...
//接收到一个客户端连接请求...
//服务器方端口:8000
//服务器方IP地址:::ffff:127.0.0.1
//客户端方端口:49345
//客户端方IP地址:::ffff:127.0.0.1
接收客户端发送的消息和响应客户端消息:
const net=require('net');
var server=net.createServer();//假如默认配置参数不需要修改可以省略不写
server.on('connection',(serverSocket)=>{
console.log("接收到一个客户端连接请求...");
console.log("服务器方端口:%s",serverSocket.localPort);
console.log("服务器方IP地址:%s",serverSocket.localAddress);
console.log("客户端方端口:%s",serverSocket.remotePort);
console.log("客户端方IP地址:%s",serverSocket.remoteAddress);
serverSocket.on("data",(data)=>{//接收到客户端发送给服务器的消息
console.log("接收到客户端消息:%s",data);
serverSocket.write("服务器接收到了数据:"+data);//服务器给客户端发送消息
})
})
server.listen('8000',()=>{
console.log('正在监听8000端口...')
})
//正在监听8000端口...
//接收到一个客户端连接请求...
//服务器方端口:8000
//服务器方IP地址:::ffff:127.0.0.1
//客户端方端口:49381
//客户端方IP地址:::ffff:127.0.0.1
//接收到客户端消息:a
//接收到客户端消息:b
//接收到客户端消息:c
//接收到客户端消息:d
创建TCP客户端
const net =require("net");
var clientSocket=net.connect(8000,'127.0.0.1',()=>{ //连接监听器
clientSocket.write("world!");
console.log("服务器方端口:%s",clientSocket.remotePort);
console.log("服务器方IP地址:%s",clientSocket.remoteAddress);
console.log("客户端方端口:%s",clientSocket.localPort);
console.log("客户端方IP地址:%s",clientSocket.localAddress);//向服务器写出数据
})
clientSocket.on('data',(data)=>{//读取数据
console.log(data.toString());
clientSocket.end(); //主动断开连接
})
clientSocket.on('end',()=>{//发送FIN包完成
console.log("双方进入半连接状态!")
});
clientSocket.on('close',()=>{//完全断开
console.log("连接完全关闭!")
});
//服务器方端口:8000
//服务器方IP地址:127.0.0.1
//客户端方端口:49469
//客户端方IP地址:127.0.0.1
//response-world!
//双方进入半连接状态!
//连接已断开!