意见箱
恒创运营部门将仔细参阅您的意见和建议,必要时将通过预留邮箱与您保持联络。感谢您的支持!
意见/建议
提交建议

【详解使用nodejs+Socket打造P2P实时聊天室】

来源:恒创科技 编辑:恒创科技编辑部
2024-02-04 14:59:59


在此之前我们再次聊聊socket服务

Socket 服务

套接字​​(socket)​​是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O内容插入到网络中,并与网络中(服务器)的其他应用程序进行通信。网络套接字是IP地址与端口的组合。


【详解使用nodejs+Socket打造P2P实时聊天室】

我们将一个小区比作一台计算机,一台计算机里面跑了很多程序,怎么区分程序呢,用的是端口,就好像小区用门牌号区分每一户人家一样。手机送到小明家了,怎么进去呢?从大门进啊,怎么找到大门呢?门牌号呀。不就相当于从互联网来的数据找到接收端计算机后再根据端口判断应该给哪一个程序一样吗。小明家的入口就可以用小区地址+门牌号进行唯一表示,那么同样的道理,程序也可以用IP+端口号进行唯一标识那么这个程序的入口就被称作Socket

现在再来说说什么是Socekt编程,我们将TCP协议简化一下,就只有三个核心功能:建立连接发送数据以及接收数据

第一步:简单创建socket服务与客户端client的连接模块化IP地址和端口号(config.js)(模块化编程更加凸显对程序可维护性的提高)
// 用户的主机号和端口号
module.exports = {
'port': 3000,
"host": '127.0.0.1'
}
在之后一直使用这个模块
/**
* 创建服务器
*/
var net = require('net');
var path = require('path');
var config = require(path.join(__dirname, './config'));
var serve = net.createServer();
serve.on('connection', function (socket) {
/*
*检测一般客户端是否连接成功
socket.on("data", function (data) {
console.log("有客户端接入的信息" + data);
})
*/
socket.on("data", function (data) {
console.log("有客户端接入的信息" + data);
/*此处暂时可省略很多代码*/
/*此处暂时可省略很多代码*/
/*此处暂时可省略很多代码*/
/*此处暂时可省略很多代码*/
/*此处暂时可省略很多代码*/
})
socket.on('error', function () {
console.log("有客户端异常退!!!");
})
})
serve.listen(config.port, config.host, function () {
console.log("服务器在端口号" + config.port + '启动了监听');
})
搭建简单的socket服务器之后,接入客户端检测是否能成功接入到服务器(client.js)
//导入net
var net = require('net');
var path = require('path')
var config = require(path.join(__dirname, './config'));
// 创建连接,产生socket对象
var client = net.createConnection({
host: config.host,
port: config.port
})
var username = '';
// 输入消息
client.on('connect', function () {
console.log('进入聊天室......\n请输入用户名:');
/**
* 准备输入聊天信息
* nodejs中提供了process模块,支持终端设备进行IO操作,全局模块。不需要导入
* stdin对象获得键盘输入(回车后)
* 接收到键盘输入,就将消息发送到服务器
*/
process.stdin.on('data', function (data) {
data = data.toString().trim();

});
})
//接收服务端的socket信息
client.on('data', function (data) {
console.log(JSON.parse(data));

});
client.on('error', function () {
console.log('服务器异常退出。');
})
注册signup.js模块,首次对信息在服务端进行包装
exports.signup = function (socket, data,) {
var username = data.username;
if (!users[username]) {//用户不存在,保存用户名和socket
users[username] = socket;//保存用户名和socket
// console.log(users);
var send = {
mstype: 'signup',
code: 1000,
username: username,
message: "注册成功"
};
socket.write(JSON.stringify(send));
} else {
var send = {
mstype: 'signup',
code: 1001,
message: '用户名已被占用,请重新输入用户名'
};
socket.write(JSON.stringify(send));
}
}
注册完自己的信息后开始编写自己的发送信息第二步:根据不同的类型信息重新构架Message架构根据构建的信息,判断这个是broadcast还是p2p 的mstype
在这里插入代码片/**
* 对广播的消息进行处理
* @param {*} data
* @param {*} users
*/
exports.broadcast = function (data,) {
// 函数的data形参就是一个对象数据
var from = data.from; //获得发消息方
var message = data.message; //获得消息
message = from + '说:' + message; //改变消息架构

// 构建发送消息框架
var send = {
mstype: 'broadcast', //因为消息聊天室消息需要广播,所以在此设置类型
message: message
}

send = Buffer.from(JSON.stringify(send)); //对象解析为字符串给send,buffer缓冲池
for (var username in users) {
if (username != from) {
users[username].write(send); //除发送方之外的人写入框架信息
}
}
}
p2p的处理方式
exports.p2p = function (socket, data,) {
var from = data.from;
var to = data.to;
var message = data.message;
var receiver = users[to];
if (!receiver) {
var send = {
mstype: 'p2p',
code: 2001,
message: "用户" + to + "不存在"
}
socket.write(JSON.stringify(send));
} else {
var send = {
mstype: 'p2p',
code: 2000,
from: from,
message: from + " 对你说" + message
}
socket.write(JSON.stringify(send));
}
}
客户端对他们三种情况(注册,broadcast,p2p)接受的处理
//导入net
var net = require('net');
var path = require('path')
var config = require(path.join(__dirname, './config'));
// 创建连接,产生socket对象
var client = net.createConnection({
host: config.host,
port: config.port
})
var username = '';
// 输入消息
client.on('connect', function () {
console.log('进入聊天室......\n请输入用户名:');
/**
* 准备输入聊天信息
* nodejs中提供了process模块,支持终端设备进行IO操作,全局模块。不需要导入
* stdin对象获得键盘输入(回车后)
* 接收到键盘输入,就将消息发送到服务器
*/
process.stdin.on('data', function (data) {
data = data.toString().trim();
// 判断用户是否已经有来注册用户名
if (!username) {// 没有注册的话
var send = {
mstype: 'singup',
username: data
}
client.write(JSON.stringify(send));
// console.log("注册成功");
return;
}
var regex = /(.{1,18}):(.+)/;
var matches = regex.exec(data);
if (matches) {
var from = username;
var to = matches[1];
var message = matches[2];
var send = {
mstype: 'p2p',
from: from,
to: to,
message: message
}
client.write(JSON.stringify(send));
} else {
var send = {//广播消息
mstype: 'broadcast',
from: username,
message: data
};
client.write(JSON.stringify(send))
}

});
})
//接收服务端的socket信息
client.on('data', function (data) {
//console.log(JSON.parse(data));
data = JSON.parse(data);
// console.log(data.mstype);
switch (data.mstype) {
case "signup":
var code = data.code;
switch (code) {
case 1000:
username = data.username;
console.log(data.message);
break;
case 1001:
console.log(data.message);
break;
default:
break;
}
break;
case 'broadcast':
console.log(data.message);
break;
case 'p2p':
var code = data.code;
switch (code) {
case 2000:
username = data.username;
console.log(data.message);
break;
case 2001:
console.log(data.message);
break;
default:
break;
}
break;
default:
break;
}
});
client.on('error', function () {
console.log('服务器异常退出。');
})

第三步:按照类型的不同向对方发送信息
/**
* 创建服务器
*/
var net = require('net');
var path = require('path');
const { p2p } = require('./p2p');
var config = require(path.join(__dirname, './config'));
var broadcast = require(path.join(__dirname, './broadcast'));
var signup = require(path.join(__dirname, './signup'));

var serve = net.createServer();
var users = {};
/*检测congfig加载是否成功
if(config){
console.log("congfig加载成功");
}
if (signup) {
console.log(typeof (signup.signup));
}*/

serve.on('connection', function (socket) {
/*
*检测一般客户端是否连接成功
socket.on("data", function (data) {
console.log("有客户端接入的信息" + data);
})
*/
socket.on("data", function (data) {
console.log("有客户端接入的信息" + data);
// 将字符串转化为对象
data = JSON.parse(data);
switch (data.mstype) {
case "singup": signup.signup(socket, data, users); break;
case "broadcast": broadcast.broadcast(data, users); break;
case 'p2p': p2p.p2p(socket, data, users); break;
default: break;
}
})
socket.on('error', function () {
console.log("有客户端异常退!!!");
})
})
serve.listen(config.port, config.host, function () {
console.log("服务器在端口号" + config.port + '启动了监听');
})

感悟:
对于net的实时聊天技术,主要的是对socket的理解,启动一个就客户端的服务器会产生一个新的。唯一的socket对象,针对这样的一个对像操作的数据,把数据写入到socket这条“线”上。那么他与服务就会共享这个socket对象上的数据,然后服务器处理这一条信息。从而把达到自定义类型的互传。


上一篇: Node.js的Buffer(缓冲区)和Stream 下一篇: 手机怎么远程登录云服务器?