关于node.js:node网络通信

3次阅读

共计 7056 个字符,预计需要花费 18 分钟才能阅读完成。

网络通信

一、网络通信

1. 网络通信基本原理

通信必要条件:

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

图示:

2. 网络通信形式

交换机通信
路由器通信

图示:(利用 Mac 地址,定位主机)

毛病:

交换机的接口数量有下限
局域网存在大量主机会造成播送风暴(任意一条音讯都须要其它机器接管,再确认受否无效)

路由器应用 (连贯局域网) 图示:

3. 网络层次模型

OSI 七层模型:

数据的封装与解封装(TCP/IP 五层协定模式):

参考:https://blog.csdn.net/weixin_…

4.tcp 协定

tcp 报文构造图解:

常见管制字段:

三次握手图解:

四次挥手图解:

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

一个服务端会服务于多个客户端,服务端接管到音讯后,不肯定能即便将后果及时传回

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'));
    })
});

后果展现:

‘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)
    }

})

正确成果:

2. 数据的封包与拆包

约定包的构造:

数据传输过程:

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

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('本地服务端曾经启动');
})
正文完
 0