node网络通信

发布时间:2023-05-19 09:30

网络通信

一、网络通信

1.网络通信基本原理

通信必要条件:

主机之间需要有传输介质(数据信号的传输)
主机上必须有网卡设备(数据信号的调制与解调制)
主机之间需要协商网络速率

图示:
\"node网络通信_第1张图片\"

2.网络通信方式

交换机通讯
路由器通讯

图示:(利用Mac地址,定位主机)
\"node网络通信_第2张图片\"
缺点:

交换机的接口数量有上限
局域网存在大量主机会造成广播风暴(任意一条消息都需要其它机器接收,再确认受否有效)

路由器使用(连接局域网)图示:
\"node网络通信_第3张图片\"

3.网络层次模型

OSI七层模型:
\"node网络通信_第4张图片\"
数据的封装与解封装(TCP/IP五层协议模式):
\"node网络通信_第5张图片\"
参考:https://blog.csdn.net/weixin_...

4.tcp协议

tcp报文结构图解:
\"node网络通信_第6张图片\"
常见控制字段:
\"node网络通信_第7张图片\"
三次握手图解:
\"node网络通信_第8张图片\"
四次挥手图解:
\"node网络通信_第9张图片\"

为什么挥手中,中间两次不能合并呢?

一个服务端会服务于多个客户端,服务端接收到消息后,不一定能即使将结果及时传回

TCP协议总结:

TCP处于传输层,基于端口,面向连接
主机之间要想通信需要先建立双向数据通道
TCP的握手与挥手本质上都是四次

二、创建TCP通信

Net模块实现了底层通信接口

通信过程:

创建服务端:接收和回写客户端数据
创建客户端:发送和接收服务端数据
数据传输:内置服务事件和方法读写数据

核心内置通信事件:

listening事件:调用server.listen方法之后触发
connection事件:新的连接建立时触发
close事件:当server关闭时触发
error事件:当错误出现的时候触发

通信事件&方法:

data事件:当接收到数据的时候触发该事件
write方法:在socket上发送数据,默认是UTF8编码
end操作:当socket的一端发送FIN包时触发,结束可读端

1.net模块使用

node中tcp连接的创建与通信:
server:

const net = require(\'net\');

// 创建服务端实例
const server = net.createServer();

const PORT = 1234;
const HOST = \'localhost\';

// 监听该端口
server.listen(PORT, HOST);

server.on(\'listening\',()=>{
    console.log(`服务端已经开启在 ${HOST}:${PORT}`);
});

// 就收消息,回写消息       socket 双工流
server.on(\'connection\',(socket)=>{
    console.log(\'有客户端连接\');
    socket.on(\'data\',(chunk)=>{
        const msg = chunk.toString();
        console.log(\'client:\'+msg);

        // 回写消息
        socket.write(Buffer.from(\'hello client\'));
    })
});


server.on(\'close\',()=>{
    console.log(\'服务端已经关闭\');
})

server.on(\'error\',(err)=>{
    console.log(\'服务端发生错误\',err);
})

client:

const net = require(\'net\');

const client = net.createConnection({
    port: 1234,
    host: \'127.0.0.1\'
})

client.on(\'connect\', () => {
    client.write(\'hello server\');
})

client.on(\'data\',(chunk)=>{
    console.log(\'server:\'+chunk.toString());
});


client.on(\'err\',()=>{
    console.log(\'客户端发生错误\');
});

client.on(\'close\',()=>{
    console.log(\'客户端已经关闭\');
})

2.TCP粘包问题

问题展示:
client代码:

client.on(\'connect\', () => {
    client.write(\'hello server\');
    client.write(\'hello server\');
    client.write(\'hello server\');
    client.write(\'hello server\');
})

server代码:

server.on(\'connection\',(socket)=>{
    console.log(\'有客户端连接\');
    socket.on(\'data\',(chunk)=>{
        const msg = chunk.toString();
        console.log(\'client:\'+msg);

        // 回写消息
        socket.write(Buffer.from(\'hello client\'));
    })
});

结果展示:
\"image.png\"
\'client:\'打印了一次,出现了粘包问题,

1.间断发送数据

client代码:

let dataArr = [
    \'hello server\',
    \'hello server-2\',
    \'hello server-3\',
    \'hello server-4\',
]

client.on(\'connect\', () => {
    for (let i = 0; i < dataArr.length; i++) {
        (function (val, index) {
            setTimeout(() => {
                client.write(val)
            }, 1000 * (index + 1))
        })(dataArr[i], i)
    }

})

正确效果:
\"node网络通信_第10张图片\"

2.数据的封包与拆包

约定包的结构:

\"node网络通信_第11张图片\"

数据传输过程:

进行数据编码,获取二进制数据包
按规则拆解数据,获取指定长度的数据

Buffer数据读写:

writeInt16BE:将value从指定位置写入
readInt16BE:从指定位置开始读取数据

封包与拆包类实现:

class MyTransformCode {
    constructor() {
        this.packageHeaderLen = 4;
        this.serialNum = 0;
        this.serialLen = 2;
    }

    // 编码
    encode(data, serialNum) {
        let body = Buffer.from(data);


        // 01 先按照指定的长度申请一个缓冲区
        const headerBuf = Buffer.alloc(this.packageHeaderLen);

        // 02 再把数据写入缓冲区
        headerBuf.writeInt16BE(serialNum || this.serialNum);
        headerBuf.writeInt16BE(body.length, this.serialLen);


        if (serialNum === undefined) {
            this.serialNum++;
        }

        return Buffer.concat([headerBuf, body]);
    }

    decode(buffer) {
        let headerBuf = buffer.slice(0, this.packageHeaderLen);
        const bodyBuf = buffer.slice(this.packageHeaderLen);
        return {
            serialNum: headerBuf.readInt16BE(),
            bodyLength: headerBuf.readInt16BE(this.serialLen),
            body: bodyBuf.toString()
        }
    }

    // 获取包长度
    getPackageLen(buffer){
        if(buffer.length < this.packageHeaderLen){
            return 0;
        }else{
            return this.packageHeaderLen+buffer.readInt16BE(this.serialLen);
        }
    }
}

使用:

const MyTransform = require(\'./03-myTransform\');
let ts = new MyTransform();
let str1 = \'江江学习\';

let encodeBuf = ts.encode(str1,1);
console.log(ts.decode(encodeBuf));

let len = ts.getPackageLen(encodeBuf);
console.log(len)

封包解决粘包:

// server
server.on(\'connection\', (socket) => {
    console.log(\'有客户端连接\');
    socket.on(\'data\', (chunk) => {
        if (overageBuffer) {
            chunk = Buffer.concat([overageBuffer, chunk]);
            overageBuffer = null;
        }
        let packageLen = 0;
        while (packageLen = ts.getPackageLen(chunk)) {
            const packageCon = chunk.slice(0, packageLen);
            chunk = chunk.slice(packageLen);
            const ret = ts.decode(packageCon);
            console.log(ret);
            // 回写消息
            socket.write(ts.encode(ret.body, ret.serialNum));
        }
        overageBuffer = chunk;
    })
});


// client 
client.on(\'connect\', () => {
   client.write(ts.encode(\'拉钩教育\'));
   client.write(ts.encode(\'拉钩教育\'));
   client.write(ts.encode(\'拉钩教育\'));
   client.write(ts.encode(\'拉钩教育\'));
   client.write(ts.encode(\'拉钩教育\'));
   client.write(ts.encode(\'拉钩教育\'));

})

client.on(\'data\', (chunk) => {
    if (overageBuffer) {
        chunk = Buffer.concat([overageBuffer, chunk]);
        overageBuffer = null;
    }
    let packageLen = 0;
    while (packageLen = ts.getPackageLen(chunk)) {
        const packageCon = chunk.slice(0, packageLen);
        chunk = chunk.slice(packageLen);
        const ret = ts.decode(packageCon);
        console.log(ret);
    }
    overageBuffer = chunk;
});

三、Http协议

1.使用http模块

开启一个http服务器:

const http = require(\'http\');

let server = http.createServer((req,res)=>{
    res.end(\'hello world\');
});

server.listen(1234,()=>{
    console.log(\'server is listening 1234\');
})

获取http请求信息:

const http = require(\'http\');
const url = require(\'url\');

const server = http.createServer((req,res)=>{

    // 请求路径
    let {pathname,query} = url.parse(req.url,true);
    console.log(\'pahtinfo:\',pathname,\'----\',query)

    // 请求方法
    console.log(\'method:\',req.method);

    // 版本号
    console.log(\'httpVersion:\',req.httpVersion);

    // 请求头
    console.log(\'headers:\',req.headers);


    let arr = []
    // 请求体
    req.on(\'data\',(data)=>{
        arr.push(data);
    });
    req.on(\'end\',()=>{
        console.log(\'body:\',Buffer.concat(arr).toString())
    })
    
    res.end(\'hello client\')
});

server.listen(1234,()=>{
    console.log(\'server is listening 1234\');
})

设置http响应:

const http = require(\'http\');

const server = http.createServer((req,res)=>{
    console.log(\'request enter\');

     // 设置响应状态码
     res.statusCode = 302;

    // 设置响应头信息
    res.setHeader(\'Content-Type\',\'text/html;charset=utf-8\');

    // res.write(\'ok\');
    res.end(\'江江\');
});

server.listen(1234,()=>{
    console.log(\'servet is listening 1234\');
})

2.客户端代理

agent-server代码:

const http = require(\'http\');
const url = require(\'url\');
const qeurystring = require(\'querystring\');

const server = http.createServer((req, res) => {
    let { pathname, query } = url.parse(req.url, true);
    console.log(pathname, \'---\', query);

    let arr = [];
    req.on(\'data\', (data) => {
        arr.push(data);
    })
    req.on(\'end\', () => {
        let obj = Buffer.concat(arr).toString();
        if(req.headers[\'content-type\'] == \'application/json\'){
            let ret = JSON.parse(obj);
            ret.add = \'add\';
            res.end(JSON.stringify(ret));
        }else if(req.headers[\'content-type\'] == \'application/x-www-form-urlencoded\'){
            let ret = qeurystring.parse(obj);
            res.end(JSON.stringify(ret));
        }

    })

})
server.listen(1234, () => {
    console.log(\'server is listening 1234\');
})

agent-client代码:

const http = require(\'http\');

let options = {
    host: \'localhost\',
    port: 1234,
    path: \'/\',
    method: \'POST\',
    headers: {
        \'Content-Type\': \'application/x-www-form-urlencoded\',
    }
}

let req = http.request(options, (res) => {
    let arr = [];
    res.on(\'data\',(chunk)=>{
        arr.push(chunk);
    })
    res.on(\'end\',()=>{
        console.log(Buffer.concat(arr).toString());
    })
})


// req.end(`{\"name\":\"zhangsan\"}`)
req.end(`a=1&b=2`)

3.代理客户端跨域

外部服务器代码:

const http = require(\'http\');

const server = http.createServer((req, res) => {
    let arr = [];
    req.on(\'data\',(data)=>{
        arr.push(data);
    })
    req.on(\'end\',()=>{
        console.log(Buffer.concat(arr).toString());
        res.end(\'外部服务i器端数据\');
    })
})

server.listen(1234, () => {
    console.log(\'外部服务端启动了\');
})

代理客户服务器:

const http = require(\'http\');

let options = {
    host: \'localhost\',
    port: 1234,
    path: \'/\',
    method: \'POST\'
}
let server = http.createServer((request, response) => {
    let req = http.request(options, (res) => {
        let arr = [];
        res.on(\'data\', (data) => {
            arr.push(data);
        })
        res.on(\'end\', () => {
            let ret = Buffer.concat(arr).toString();
            response.setHeader(\'Content-Type\', \'text/html;charset=utf-8\');
            console.log(\'ret\', ret)
            response.end(ret)
        })
    })
    req.end(\'hello world\');
})


server.listen(2345, () => {
    console.log(\'本地服务端已经启动\');
})

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号