Skip to content

7网络编程

tcp、udp、http、htps、websocket中如何使用 还重点分析了websocket数据传输的过程、ssl/tls和https如何进行数字签名

服务模块

Node提供了net、dgram、http、https这4个模块, 分别用于处理TCP、UDP、HTTP、HTTPS, 适用于服务器端和客户端。

net -> TCP => tls -> TLS/SSL加密的TCP连接上 dgram -> UDP http -> HTTP => https -> HTTPS

OSI模型

1、TCP

  • 传输之前需要3次握手形成会话

  • 创建会话

  • 测试

    • 创建服务端(7.1.1.js)
    js
    var net = require("net");
    var server = net.createServer(function (socket) {
      // Nagle算法:要求缓冲区的数据达到一定数量或者一定时间后才将其发出
      // 去掉Nagle算法
      socket.setNoDelay(true);
      socket.on("data", function (data) {
        console.log(data.toString());
        socket.write("欢迎光临《深入浅出Node.js》3");
      });
      socket.on("end", function () {
        console.log("连接断开");
      });
      socket.write("欢迎光临《深入浅出Node.js》1\n");
    });
    server.listen(8124, function () {
      console.log("server bound");
    });
    
    // 服务器事件
    server.on("connection", function (socket) {
      // 新的连接
      socket.write("《深入浅出Node.js》2\n");
      console.log("connection event");
    });
    • telnet 127.0.0.1 8124
    • nc -U /tmp/echo.sock (前提对domain socket监听 server.listen('/tmp/echo.sock');
    • net模块自行构造客户端进行会话(7.1.2.js)
    js
    var count = 0;
    var net = require("net");
    var client = net.connect({ port: 8124 }, function () {
      console.log("client connected");
      client.write("world! \r\n");
    });
    client.on("data", function (data) {
      count++;
      console.log("测试调用次数", count);
      // 有时候调1次
      // 有时候调2次
      // 更多时候是小数据包进行合并,发送1次
      console.log(data.toString());
      client.end();
    });
    client.on("end", function () {
      console.log("client disconnected");
    });
    
    client.on("connect", () => {
      console.log("client connect event");
    });

2、UDP

  • UDP 不是面向连接的、直接传输
  • DNS服务即是基于它实现的。

3、http

  • http
    • HTTP标准为RFC 2616
    • curl -v http://127.0.0.1:1337
    • 报文开头:经典的TCP的3次握手过程
    • 请求、响应:都包含报文头和报文体,get请求没有报文体
  • http 模块
    • http 封装 net(tcp)
    • HTTP服务以request为单位进行服务。
    • http模块即是将connection到request的过程进行了封装,
    • 对二进制的数据进行解析:请求、响应过程中
  • 客户端
    • http响应
      • ClientRequest在解析响应报文时,
      • 一解析完响应头就触发response事件,
      • 同时传递一个响应对象以供操作ClientResponse
    • http代理
      • 最多可以创建5个连接,实际是一个线程池
      • 需要修改
      js
      var agent = new http.Agent({
        maxSockets: 10
      });
      var options = {
        hostname: '127.0.0.1',
        port: 1334,
        path: '/',
        method: 'GET',
        agent: agent
      }
      • agent为false,脱离连接池的管理,不受并发的限制。

4、websocket

  • 优点:
    • 事件变成
    • 长连接
    • 只建立一个tcp连接
    • 数据可以推动到客户端,双向通信
    • 更轻量级的协议头
  • Comet技术 长轮询(过时)
  • WebSocket与HTTP的区别
    • 相比HTTP,WebSocket更接近于传输层协议,在TCP上定义独立的协议。
    • 让人迷惑的部分在于WebSocket的握手部分:是由HTTP完成的,使人觉得它可能是基于HTTP实现的。

1、握手

  • 请求
    YAML
    GET /chat HTTP/1.1        
    Host: server.example.com 
    # 升级协议为WebSocket 
    Upgrade: websocket        
    Connection: Upgrade        
    # 用于安全校验
    # 随机生成的Base64编码的字符串
    # 服务器端接收到之后将其与字符串258EAFA5-E914-47DA-95CAC5AB0DC85B11相连
    # 然后通过sha1安全散列算法计算出结果后,Base64编码,返回给客户端
    Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==        
    # 子协议和版本号  
    Sec-WebSocket-Protocol: chat, superchat
    Sec-WebSocket-Version: 13
  • 响应
    YAML
    HTTP/1.1101 Switching Protocols
    # 更新应用层协议为WebSocket协议
    Upgrade: websocket        
    Connection: Upgrade    
    # 基于Sec-WebSocket-Key生成的字符串 
    # 客户端将会校验Sec-WebSocket-Accept的值,如果成功,将开始接下来的数据传输   
    Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=        
    Sec-WebSocket-Protocol: chat

2、数据传输

  • send
    • 将这个数据封装为一帧或多帧数据, 然后逐帧发送
  • 安全
    • 客户端需要对发送的数据帧进行掩码处理,服务器一旦收到无掩码帧(比如中间拦截破坏),连接将关闭。
    • 而服务器发送到客户端的数据帧则无须做掩码处理,同样,如果客户端收到带掩码的数据帧,连接也将关闭。
  • 数据帧
    • WebSocket数据帧的定义,每8位为一列,也即1个字节。其中每一位都有它的意义
    • fin:
      • 如果这个数据帧是最后一帧, 这个fin位为1, 其余情况为0。
      • 当一个数据没有被分为多帧时, 它既是第一帧也是最后一帧。
    • rsv1、 rsv2、 rsv3:
      • 各为1位长, 3个标识用于扩展, 当有已协商的扩展时, 这些值可能为1, 其余情况为0。
    • opcode:
      • 长为4位的操作码, 可以用来表示0到15的值, 用于解释当前数据帧。
      • 0表示附加数据帧, 1表示文本数据帧, 2表示二进制数据帧, 8表示发送一个连接关闭的数据帧, 9表示ping数据帧,
      • 10表示pong数据帧, 其余值暂时没有定义。
      • ping数据帧和pong数据帧用于心跳检测, 当一端发送ping数据帧时, 另一端必须发送pong数据帧作为响应, 告知对方这一端仍然处于响应状态。
    • masked:
      • 表示是否进行掩码处理, 长度为1。 客户端发送给服务器端时为1, 服务器端发送给客户端时为0。
    • payload length:
      • 一个7、 7+16或7+64位长的数据位, 标识数据的长度, 如果值在0~125之间, 那么该值就是数据的真实长度;
      • 如果值是126, 则后面16位的值是数据的真实长度;
      • 如果值是127, 则后面64位的值是数据的真实长度。
    • masking key:
      • 当masked为1时存在, 是一个32位长的数据位, 用于解密数据。
    • payload data:
      • 我们的目标数据, 位数为8的倍数。
  • 服务端处理
    • 服务端接受,解析,通过掩码解密,触发onmessage
    • 服务端响应,无需掩码,其他相同

5、服务安全

  • SSL作为一种安全协议(Secure Sockets Layer,安全套接层)- 传输层提供对网络连接加密
  • 随后IETF将其标准化,称为TLS(Transport Layer Security,安全传输层协议)
  • crypto、 加密解密
  • tls、 针对net tec
  • https、针对http 1、TLS/SSL
    • TLS/SSL是一个公钥/私钥的结构,它是一个非对称的结构
    • 1、客户端和服务器分别生成公钥、私钥;
      shell
      # 生成服务器端私钥
      $ openssl genrsa -out server.key 1024        
      # 生成客户端私钥        
      $ openssl genrsa -out client.key 1024
      # 生成公钥
      $ openssl rsa -in server.key -pubout -out server.pem       
      $ openssl rsa -in client.key -pubout -out client.pem
    • 2、客户端和服务器端互换公钥,使用对方的公钥进行加密;
    • 3、中间人攻击,
      • 中间人对客户端扮演服务器端的角色,
      • 对服务器端扮演客户端的角色,
      • 为了解决这个问题, TLS/SSL引入了数字证书来进行认证、
      • 数字证书包括:
        • 1、服务器的名称和主机名、
        • 2、服务器的公钥、
        • 3、签名颁发机构的名称、
        • 4、来自签名颁发机构的签名;
      • 通过证书中的签名,确认收到的公钥是来自目标服务器的;
    • 4、数字证书
      • CA数字证书认证中心
        • 为站点颁发证书, 且这个证书中具有CA通过自己的公钥和私钥实现的签名
        • 为了得到签名证书,
        • 服务器需要通过自己的私钥生成CSR(Certificate Signing Request,证书签名请求)文件
        • CA机构将通过这个文件颁发属于该服务器端的签名证书,
        • 通过CA机构就能验证证书是否合法。
        • 自签名证书:自己扮演CA机构
        • CA:
          • 生成私钥、
          • 生成CSR文件、
          • 通过私钥自签名生成证书(扮演CA角色需要的文件)
          bash
          $ 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文件、
          • 得到签名证书
          bash
          $ 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将证书颁发给服务器端
        • 客户端先会去获取服务器端的证书
        • 通过CA验证真假(还有服务器名称、IP地址验证)
        • 知名的CA机构,它们的证书一般预装在浏览器中,不需要再从服务器获取了
        • 根证书:不需要上级参与签名的 2、TLS服务(/tls)
    • 客户端:也需要生成CA证书,双向确认身份
      js
      // 创建私钥
      $ openssl genrsa -out client.key 1024
      // 生成CSR
      $ 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

3、https服务(/https)

  • 直接用上文生成的私钥和证书。
  • curl
    • curl https://localhost:8000/ 抛错,无法验证服务器端证书是否正确,
    • curl -k https://localhost:8000/ 忽略掉证书的验证
    • curl --cacert keys/ca.crt https://localhost:8000/ 告知CA证书使之完成对服务器证书的验证