网络通信

一、网络通信

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)

封包解决粘包:

// serverserver.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('本地服务端曾经启动');})