本文共--字 阅读约--分钟 | 浏览: -- Last Updated: 2019-11-21
TCP是一种传输层协议,它可以让你讲数据从一台计算机完整有序地传输到另一台计算机。
Node HTTP服务器是构建于Node TCP服务器之上的,从编程角度来说,也就是Node中的http.Server继承自net.Server (net是TCP模块)。还有邮件客户端(SMTP/IMAP/POP)、聊天程序(IRC/XMPP)以及远程shell(SSH)等都基于TCP协议。
面向连接
TCP的首要特性就是它是面向连接的,而TCP协议所基于的IP协议却是面向无连接的,IP是基于数据包的传输,这些数据包都是独立进行传输的,送达的顺序也是无序的。
使用IP协议意味着数据包送达时是无序的,这些数据包不属于任何的数据流或者连接,那么当使用TCP/IP和服务器建立连接后,是怎样做到让数据包送达时是有序的呢?
因为当在TCP连接内进行数据传递时,发送的IP数据包包含了标识该连接以及数据流顺序的信息。
面向字节
TCP对字符和字符编码是完全无知的,TCP允许数据以ASCII字符(每个字符一个字节)或者Unicode(每个字符四个字节)进行传输。
可靠性
由于TCP是基于底层不可靠的服务,因此,它必须要基于确认和超时实现一系列的机制来达到可靠性的要求,当数据发送出去后,发送方就会等待一个确认消息(表示数据包已经收到的简短的确认消息),如果过了指定的窗口时间,还未收到确认消息,发送方就会对数据进行重发。
流控制
要是两台互相通信的计算机中,有一台速度远快于另一台的话,TCP会通过一种叫流控制的方式来确保两点之间传输数据的平衡。
拥堵控制
TCP有一种内置的机制能够控制数据包的延迟率以及丢包率不会太高,以此来确保服务的质量(QoS)。比如,和流控制机制能够避免发送方压垮接受方一样,TCP会通过控制数据包的传输速率来避免拥堵的情况。
需求:
1、成功连接到服务器后,服务器会显示欢迎信息,并要求输入用户名,同时还会告诉你当前还有多少其他客户端也连接到了该服务器。
2、输入用户名,按下回车键,就认为成功连接上了
3、连接后,就可以通过输入信息再按下回车键,来向其他客户端进行消息的收发。
// telnet 命令退出 ctrl + ] 然后输入 quit退出 不会触发close事件
// ctrl + ] 然后 ctrl + c关闭连接 会触发close事件。
// 使用 telnet 127.0.0.1 3000 加入聊天室
var net = require('net');
var count = 0; // 用来追踪连接数
var users = {}; // 记录设置了昵称的用户
var server = net.createServer(function(conn) {
conn.write(
'\n > welcome to \033[92mnode-chat\033[39m!'
+ '\n > ' + count + ' other people are connected at this time.'
+ '\n > please write your name and press enter:'
)
count++;
var nickname;
// 接受用户输入
conn.setEncoding('utf8');
conn.on('data', function(data) {
// 需要注意的是接受到的是一个Buffer 所以需要设置编码
data = data.replace('\r\n', ''); // 删除回车符
if(!nickname) { // 如果还没有设置昵称的情况下
if(users[data]) {
// 用户输入的昵称已经被注册
conn.write('\033[90m> nickname already in use. try again:\033[39m ');
return;
} else {
nickname = data;
// 记录用户的连接
users[nickname] = conn;
// 广播给所有已经连接的用户,不排除自己
broadcast('\033[90m > ' + nickname + ' joined the room\033[39m \n')
}
} else {
// 如果设置了昵称 则之后的输入视为聊天消息 广播给除自己外的其他人
broadcast('\033[96m > ' + nickname + ':\033[39m' + data + '\n', true)
}
})
// 当客户端关闭连接时 计数器要递减
conn.on('close', function() {
count--;
// 当有人断开连接时,需要清除users中对应的元素
delete users[nickname];
// 广播给其他人知道
broadcast('\033[90m > ' + nickname + ' left the room\033[39m \n')
})
// 抽象广播方法
// @params msg [String] 消息
// @params exceptMyself [Boolean] 是否排除自己
function broadcast(msg, exceptMyself) {
for(var n in users) {
if(!exceptMyself || n != nickname) {
// 为false时 不排除自己 在不排除自己时才向自己发送消息
// 在排除自己时,向其他人发送消息
users[n].write(msg);
}
}
}
})
server.listen(3000, function() {
console.log('\033[90m server listening on: 3000\033[39m');
})