美文网首页
node 网络(二)

node 网络(二)

作者: wmtcore | 来源:发表于2016-08-12 16:32 被阅读0次

    构建websocket服务

    websocket的优势:

    • 客户端与服务器只需要一个tcp连接
    • 服务器可以推送到客户端
    • 轻量化的协议头,提高传输效率

    node使用websocket的优势:

    • WebSocket客户端基于事件的编程模式和node的自定义事件类似
    • websocket需要客户端与服务器之间的长连接,node事件驱动的方式擅长与量大的客户端保持高并发连接

    WebSocket握手

    客户端发起升级协议请求:

    GET / chat HTTP / 1.1
    Host: server.example.com
    Upgrade: websocket //升级协议为websocket
    Connection: Upgrade
    Sec - WebSocket - Key: dGhlIHNhbXBsZSBub25jZQ == 
    Sec - WebSocket - Protocol: chat, superchat //子协议
    Sec - WebSocket - Version: 13 //版本号
    
    

    Sec-WebSocket-Key用于安全校验,值是随机生成的Base64编码的字符串。服务端将其与字符串258EAFA5-E914-47DA-95CA-C5AB0DC85B11拼接,然后再用sha1计算再Base64编码

    var crypto = require('crypto');
    var val = crypto.createHash('sha1').update(key).digest('base64');
    
    //服务端响应b报文
    HTTP / 1.1 101 Switching Protocols
    Upgrade: websocket
    Connection: Upgrade 
    Sec - WebSocket - Accept: s3pPLMBiTxaQ9kYGzzhZRbK + xOo =
    Sec - WebSocket - Protocol: chat
    
    

    客户端校验Sec-WebSocket-Accept,正确的话就开始数据传输

    WebSocket数据传输

    在握手后就开始websocket数据帧协议, 握手完成客户端onopen()被触发

    socket.onopen = function() { 
      // TODO: opened()
    };
    
    

    服务端没有onopen()方法,想完成tcp套接字事件到websocket事件的封装,需要在收发数据时处理,Websocket的数据帧是在底层data事件上封装的

    //接收
    WebSocket.prototype.setSocket = function(socket) {
       this.socket = socket;
       this.socket.on('data', this.receiver);
    };
    
    //发送
    WebSocket.prototype.send = function(data) {
       this._send(data);
    };
    
    

    当一端调用send()发送时,另一端会触发onmessage,协议可能将数据封装为多帧发送。客户端需要对发送的数据帧做掩码处理,服务端收到无掩码帧会断开连接,而服务端发送时不需要

    websocket数据帧定义

    Smaller iconSmaller icon
    • fin 如果这数据帧是最后一帧时为1(如果数据就一帧,它也是1),其余为0
    • rsv1、rsv2、rsv3:1位长 用于标识拓展,当有拓展时为1
    • opcode: 4位(0~15) 0:附加数据帧 ,1:文本数据帧 ,2:二进制数据帧,8:发送一个连接关闭帧,9:ping数据帧 ,10:pong数据帧 ping,pong用于心跳检测,一端发ping、一端发pong
    • masked 是否进行掩码处理 客户端发送时是1 服务端是0
    • payload 标识数据长度
    • masking key 当masked为1时存在 长度32位 用于解密
    • payload data 目标数据 位数为8的倍数

    网络服务和安全

    • ssl(Secure Sockets Layer,安全套接层),应用在传输层
    • TLS(Transport Layer Security,安全传输层协议),由IETF标准化

    node提供crypto,tls,https。crypto用于加解密,tls与net功能类似,区别是它建立在TLS/SSL加密的tcp.https和http接口也一致,也是区别在建立于安全的连接

    TLS/SSL

    • 非对称加密,公钥用于加密传输数据,私钥解密
    Smaller iconSmaller icon

    node的tls/ssl是用openssl实现的,公、私钥生成参照:

    // 生成服务器端私 
    $ openssl genrsa -out server.key 1024 // 生成客户端私 
    $ openssl genrsa -out client.key 1024
    
    //利用上面的1024位长的RSA私钥生成公钥
    $ openssl rsa -in server.key -pubout -out server.pem
    $ openssl rsa -in client.key -pubout -out client.pem
    
    
    数字证书
    • 由CA颁发,并提供验证
    • 防止中间人攻击

    中间人攻击:在服务端和客户端交换密钥时,伪装成其中一方发送公钥,如对客户端就伪装成服务端。所以需要对公钥认证,确认来自目标服务器

    服务端通过私钥生成CSR(Certificate Signing Request,证书签名请求),ca通过它颁发属于该服务器的签名证书

    自签名证书流程:

    \\ca生成私钥,csr文件,和自签名的证书
    $ openssl genrsa -out ca.key 1024
    $ openssl req -new -key ca.key -out ca.csr
    $ openssl x509 -req -in ca.csr -signkey ca.key -out ca.crt
    \\服务器生成csr,向ca申请签名,获取证书
    $ openssl req -new -key server.key -out server.csr
    $ openssl x509 -req -CA ca.crt -CAkey ca.key -CAcreateserial -in server.csr -out server.crt
    
    

    客户端发起安全连接会获取服务端证书,然后用ca的证书验证服务器证书,包括真伪、服务器名称、ip等。对于知名ca,它的证书一般预装在浏览器,自签的ca需要客户端安装才能验证

    创建tcl服务

    • 通过node的tls创建安全的tcp服务
    //服务端
    var tls = require('tls');
    var fs = require('fs');
    var options = {
        key: fs.readFileSync('./keys/server.key'),
        cert: fs.readFileSync('./keys/server.crt'),
        requestCert: true,
        ca: [fs.readFileSync('./keys/ca.crt')]
    };
    var server = tls.createServer(options, function(stream) {
        console.log('server connected', stream.authorized ? 'authorized' : 'unauthorized');
        stream.write("welcome!\n");
        stream.setEncoding('utf8');
        stream.pipe(stream);
    });
    server.listen(8000, function() {
        console.log('server bound');
    });
    
    //测试: $ openssl s_client -connect 127.0.0.1:8000
    //客户端
    $ openssl genrsa - out client.key 1024
    $ openssl req - new - key client.key - out client.csr
    $ openssl x509 - req - CA ca.crt - CAkey ca.key - CAcreateserial - in client.csr - out client.crt
    var fs = require('fs');
    var tls = require('tls');
    var options = {
        key: fs.readFileSync('./keys/client.key'),
        cert: fs.readFileSync('./keys/client.crt'),
        ca: [fs.readFileSync('./keys/ca.crt')]
    };
    var stream = tls.connect(8000, options, function() {
        console.log('client connected', stream.authorized ? 'authorized' : 'unauthorized');
        process.stdin.pipe(stream);
    });
    stream.setEncoding('utf8');
    stream.on('data', function(data) {
        console.log(data);
    });
    stream.on('end', function() {
        server.close();
    });
    
    //和tcp相比只是多了证书配置
    

    https服务

    • 使用node的https,比http多了一个配置
    var https = require('https');
    var fs = require('fs');
    var options = {
        key: fs.readFileSync('./keys/server.key'),
        cert: fs.readFileSync('./keys/server.crt')
    };
    https.createServer(options, function(req, res) {
        res.writeHead(200);
        res.end("hello world\n");
    }).listen(8000);
    
    //验证 $ curl https://localhost:8000/ -k, 忽略证书验证 -carcert ca证书地址
    //客户端
    
    var https = require('https');
    var fs = require('fs');
    var options = {
        hostname: 'localhost',
        port: 8000,
        path: '/',
        method: 'GET',
        key: fs.readFileSync('./keys/client.key'),
        cert: fs.readFileSync('./keys/client.crt'),
        ca: [fs.readFileSync('./keys/ca.crt')]
    };
    options.agent = new https.Agent(options);//https代理另设
    var req = https.request(options, function(res) {
        res.setEncoding('utf-8');
        res.on('data', function(d) {
            console.log(d);
        });
    });
    req.end();
    req.on('error', function(e) {
        console.log(e);
    });
    
    

    相关文章

      网友评论

          本文标题:node 网络(二)

          本文链接:https://www.haomeiwen.com/subject/bfkesttx.html