传输控制协议(TCP)
- TCP 是一个面向连接的协议,它保证了两台计算机之间的数据传输的可靠性,和顺序
- 简而言之,TCP是一种传输层协议,他可以让你将数据从一台计算机完整有序的传输到另一台计算机
- HTTP协议基于TCP协议
- 我们来了解TCP/IP协议
Node HTTP服务器是构构建于 Node TCP服务器之上的,编程角度解释就是node中得
http.Server
继承自net.Server
(not是TCP模块)
- 多多了解tcp 以及如何使用相关node api对书写和理解网络程序会大有裨益
TCP的特点
如若只是使用,无需甚解
理解这些有利于分析高层的协议和服务器,例如 web服务器,数据库等
面向连接是首要特性
面向连接的通信和保证顺序的传递
面向字节
- TCP允许数据以ASCII字符或者Unicode进行传输(前者为每个字符一个字节,后者为每个字符四个字节)
可靠性
- TCP本身是不可靠的服务
- 所以当数据发送出去后,发送者就会等待一个确认消息(表示数据包已经收到的简短的确认消息),如果超时,还未收到确认消息.发送方就会对数据进行重发
- 这种机制有效的解决了如网络错误或者网络阻塞这样的不可预测的情况
- 扩展:TCP协议为什么可靠-季白白白
流控制
- TCP会通过一种叫做流控制的方式来确保两点之间传输数据的平衡(避免出现传输速度不一致)
拥堵控制
- TCP有一种内置机构能够控制数据包的延迟率以及丢包率不会太高,以此来确保 服务的质量(Qos)
- TCP会通过控制数据包的传输速率来避免拥堵的情况
Telnet
- Telnet是一个早期的网络协议,旨在提供双向的怒你终端,在SSH出现以前,它作为一种控制远程计算机的方式被广泛使用,它是TCP协议的上层的协议
- 下面我们用telnet调用一下web服务器
- 编写一串代码开启个服务
require('http').createServer(function (req, res) {
res.writeHeader(200, {'content-type': 'text/html'});
res.end('Hello World
')
}).listen(3000);
直接访问本地3000端口可以看见
我们用telnet来运行这个
-
这里实验没有成功,,尴尬...
-
-
这里总结一下
- 成功创建一个TCP连接
- 创建了一个HTTP请求
- 接收到一个HTTP相应,
- 测试了一些TCP的特性,到达的数据和nodejs中写的一样先谢了响应头.Content-Type,然后是响应体,最后所有的消息都按顺序到达
鉴于没有用telnet实验成功就直接用浏览器了- -!!!
基于Tcp的聊天程序
创建模块
- 创建项目目录 而后创建package.json文件
- npm install 测试
理解NET.SERVER- API
- 创建一个 index.js 文件
const net = require('net');
/*
* 创建服务器
* */
let server = net.createServer(function (conn) {
//处理连接
console.log('\033[95m 新建连接! \033[39m');
});
/*
* 监听
* */
server.listen(3000,function () {
console.log('\033[96m 服务器端口:3000 \033[39m');
});
- 这里createServer指定了一个回调函数,每次有新链接的时候都会被执行
- creatServer回调函数会接收一个对象,该对象是node中一个很常见的实例.流(stream),本例中 它传递的是net.stream 该对象通常是既可读又可写
- 注意listen
接收连接
-
我们在回调函数外部添加一个计数器
-
- 接着我们修改回调函数内容,把计数器递增和打印出欢迎的逻辑添加上去
let server = net.createServer(function (conn) {
//处理连接
console.log('\033[95m 新建连接! \033[39m');
conn.write(
'\n > welcome to \033[95m Node-Chat! \033[39m' +
'\n > '+count + 'other people are connected at this time' +
'\n > please write your name and press enter'
);
count++;
});
- 这里我们用telnet连接测试
直接打印出我们所需要展示的内容(我已经登录四次了 所以数字是 3)
- 当底层套接字关闭时,nodejs 会触发close时间,nodejs中有另个和连接种子相关的时间见end和close,前者是当客户端显示关闭tcp拦截是触发,例如当你关闭telnet是 他会发送一个名为
FIN
的包给服务器,意味着结束连接 - 当连接发生错误时(触发error事件),end事件不会触发,因为服务器端并未接收到
FIN
包信息,不过这两种情况下,close事件都会触发,所以上述例子使用close事件比较好
data事件
- 下面我们处理客户端发送的数据
- 监听data事件,net.Stream是一个事件触发器 EventEmitter
let server = net.createServer(function (conn) {
//处理连接
console.log('\033[95m 新建连接! \033[39m');
conn.write(
'\n > welcome to \033[95m Node-Chat! \033[39m' +
'\n > '+count + 'other people are connected at this time' +
'\n > please write your name and press enter'
);
count++;
conn.on('data',function (data) {
console.log(data);
});
conn.on('close',function () {
count--;
})
});
- 我们启动服务器,启用客户端
当进行输入的时候我们发现,打印出来一串二进制字节码
-
这里我们在creatServer回调里面添加一句
conn.setEncoding('utf8')
这样就可以在控制台打印了
状态以及记录连接的情况
- 此前定义的计数器通常称为状态,在本例中不同连接的用户需要修改同一个状态变量,这个在Node中称为共享状态的并发
- 为了实现这点,我们需要对该状态金星 扩展,来追踪究竟谁连接进来
- 首先我们记录设置昵称的用户
const net = require('net');
/*
* 计数器
* */
let count = 0,users={};
/*
* 创建服务器
* */
let server = net.createServer(function (conn) {
//对二进制进行转码
conn.setEncoding('utf8');
//设置当前连接昵称
let nickname;
//抽取方法
function broadcast(msg, exceptMyself){
for (let i in users){
if (!exceptMyself || i!=nickname){
users[i].write(msg);
}
}
}
//处理连接
console.log('\033[95m 新建连接! \033[39m');
conn.write(
'\n > welcome to Node-Chat! ' +
'\n > '+count + ' other people are connected at this time' +
'\n > please write your name and press enter: '
);
count++;
conn.on('data',function (data) {
//删除回车符
data = data.replace('\r\n', '');
//接收到的第一份数据应当是用户的昵称
if (!nickname){
if (users[data]){
conn.write(' > nickname already in use,try again:');
return;
}else{
nickname=data;
users[nickname] = conn;
// for (let i in users) {
// users[i].write(' > ' +nickname + 'joined the room!! \n')
// }
broadcast(''+nickname+'joined the room! \n');
}
}else{
//视为聊天记录
// for (let i in users){
//这里用来确保消息只发送给除了自己以外的其他客户端
// if (i != nickname){
// users[i].write(' > ' + nickname + ':' + data + '\n');
// }
broadcast(nickname + ': ' + data+'\n', true);
// }
}
});
conn.on('close',function () {
count--;
//当有人离开时我们需要清除users数组中对应的元素
delete users[nickname]
broadcast('' + nickname + 'left the room! \n');
})
});
/*
* 监听
* */
server.listen(23,function () {
console.log('\033[96m 服务器端口:3000 \033[39m');
});
- 这是完整版的 简单的实现了 开启一个服务器 登录客户端实现交流
- 最后在退出的时候逻辑应该是有问题的 一个退出就崩了....
能力不足,还得再看看