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)
jsvar 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)
jsvar 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个连接,实际是一个线程池
- 需要修改
jsvar agent = new http.Agent({ maxSockets: 10 }); var options = { hostname: '127.0.0.1', port: 1334, path: '/', method: 'GET', agent: agent }
- agent为false,脱离连接池的管理,不受并发的限制。
- http响应
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将证书颁发给服务器端
- 客户端先会去获取服务器端的证书
- 通过CA验证真假(还有服务器名称、IP地址验证)
- 知名的CA机构,它们的证书一般预装在浏览器中,不需要再从服务器获取了
- 根证书:不需要上级参与签名的 2、TLS服务(/tls)
- CA数字证书认证中心
- 客户端:也需要生成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证书使之完成对服务器证书的验证
- curl