网络通信
一、网络通信
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('本地服务端曾经启动');})