单工通信

一方只是发送,一方只是接收

  1. const net = require('net');
  2. const socket = new net.Socket({});
  3. socket.connect({
  4. host: '127.0.0.1',
  5. port: 4000
  6. })
  7. socket.write('good morning geekbang');
  1. const net = require('net');
  2. const server = net.createServer((socket)=> {
  3. socket.on('data', function(buffer) {
  4. console.log(buffer, buffer.toString())
  5. })
  6. });
  7. server.listen(4000);

半双工通信

既可以发送也可以接收,但是同一时刻通信中只有一种行为

  1. const net = require('net');
  2. // 创建socket
  3. const socket = new net.Socket({});
  4. // 连接服务器
  5. socket.connect({
  6. host: '127.0.0.1',
  7. port: 4000
  8. });
  9. const lessonids = [
  10. "136797",
  11. "136798",
  12. "136799",
  13. "136800"
  14. ]
  15. let id = Math.floor(Math.random() * lessonids.length);
  16. // 往服务器传数据
  17. socket.write(encode(id));
  18. socket.on('data', (buffer) => {
  19. console.log(buffer.toString())
  20. // 接收到数据之后,按照半双工通信的逻辑,马上开始下一次请求
  21. id = Math.floor(Math.random() * lessonids.length);
  22. socket.write(encode(id));
  23. })
  24. // 把编码请求包的逻辑封装为一个函数
  25. function encode(index) {
  26. buffer = Buffer.alloc(4);
  27. buffer.writeInt32BE(
  28. lessonids[index]
  29. );
  30. return buffer;
  31. }
  1. const net = require('net');
  2. // 创建tcp服务器
  3. const server = net.createServer((socket) => {
  4. socket.on('data', function(buffer) {
  5. // 从传来的buffer里读出一个int32
  6. const lessonid = buffer.readInt32BE();
  7. // 50毫秒后回写数据
  8. setTimeout(()=> {
  9. socket.write(
  10. Buffer.from(data[lessonid])
  11. );
  12. }, 50)
  13. })
  14. });
  15. // 监听端口启动服务
  16. server.listen(4000);
  17. const data = {
  18. 136797: "01 | 课程介绍",
  19. 136798: "02 | 内容综述",
  20. 136799: "03 | Node.js是什么?",
  21. 136800: "04 | Node.js可以用来做什么?",
  22. }

全双工通信

同时发送和接收

  1. const net = require('net');
  2. const socket = new net.Socket({});
  3. socket.connect({
  4. host: '127.0.0.1',
  5. port: 4000
  6. });
  7. let id = Math.floor(Math.random() * LESSON_IDS.length);
  8. let oldBuffer = null;
  9. socket.on('data', (buffer) => {
  10. // 把上一次data事件使用残余的buffer接上来
  11. if (oldBuffer) {
  12. buffer = Buffer.concat([oldBuffer, buffer]);
  13. }
  14. let completeLength = 0;
  15. // 只要还存在可以解成完整包的包长
  16. while (completeLength = checkComplete(buffer)) {
  17. const package = buffer.slice(0, completeLength);
  18. buffer = buffer.slice(completeLength);
  19. // 把这个包解成数据和seq
  20. const result = decode(package);
  21. console.log(`包${result.seq},返回值是${result.data}`);
  22. }
  23. // 把残余的buffer记下来
  24. oldBuffer = buffer;
  25. })
  26. let seq = 0;
  27. /**
  28. * 二进制包编码函数
  29. * 在一段rpc调用里,客户端需要经常编码rpc调用时,业务数据的请求包
  30. */
  31. function encode(data) {
  32. // 正常情况下,这里应该是使用 protobuf 来encode一段代表业务数据的数据包
  33. // 为了不要混淆重点,这个例子比较简单,就直接把课程id转buffer发送
  34. const body = Buffer.alloc(4);
  35. body.writeInt32BE(LESSON_IDS[data.id]);
  36. // 一般来说,一个rpc调用的数据包会分为定长的包头和不定长的包体两部分
  37. // 包头的作用就是用来记载包的序号和包的长度,以实现全双工通信
  38. const header = Buffer.alloc(6);
  39. header.writeInt16BE(seq)
  40. header.writeInt32BE(body.length, 2);
  41. // 包头和包体拼起来发送
  42. const buffer = Buffer.concat([header, body])
  43. console.log(`包${seq}传输的课程id${LESSON_IDS[data.id]}`);
  44. seq++;
  45. return buffer;
  46. }
  47. /**
  48. * 二进制包解码函数
  49. * 在一段rpc调用里,客户端需要经常解码rpc调用时,业务数据的返回包
  50. */
  51. function decode(buffer) {
  52. const header = buffer.slice(0, 6);
  53. const seq = header.readInt16BE();
  54. const body = buffer.slice(6)
  55. return {
  56. seq,
  57. data: body.toString()
  58. }
  59. }
  60. /**
  61. * 检查一段buffer是不是一个完整的数据包。
  62. * 具体逻辑是:判断header的bodyLength字段,看看这段buffer是不是长于header和body的总长
  63. * 如果是,则返回这个包长,意味着这个请求包是完整的。
  64. * 如果不是,则返回0,意味着包还没接收完
  65. * @param {} buffer
  66. */
  67. function checkComplete(buffer) {
  68. if (buffer.length < 6) {
  69. return 0;
  70. }
  71. const bodyLength = buffer.readInt32BE(2);
  72. return 6 + bodyLength
  73. }
  74. for (let k = 0; k < 100; k++) {
  75. id = Math.floor(Math.random() * LESSON_IDS.length);
  76. socket.write(encode({ id }));
  77. }
  78. const LESSON_IDS = [
  79. "136797",
  80. "136798",
  81. "136799",
  82. "136800",
  83. "136801",
  84. "136803",
  85. "136804",
  86. "136806",
  87. "136807",
  88. "136808",
  89. "136809",
  90. "141994",
  91. "143517",
  92. "143557",
  93. "143564",
  94. "143644",
  95. "146470",
  96. "146569",
  97. "146582"
  98. ]
  1. const net = require('net');
  2. const server = net.createServer((socket) => {
  3. let oldBuffer = null;
  4. socket.on('data', function (buffer) {
  5. // 把上一次data事件使用残余的buffer接上来
  6. if (oldBuffer) {
  7. buffer = Buffer.concat([oldBuffer, buffer]);
  8. }
  9. let packageLength = 0;
  10. // 只要还存在可以解成完整包的包长
  11. while (packageLength = checkComplete(buffer)) {
  12. const package = buffer.slice(0, packageLength);
  13. buffer = buffer.slice(packageLength);
  14. // 把这个包解成数据和seq
  15. const result = decode(package);
  16. // 计算得到要返回的结果,并write返回
  17. socket.write(
  18. encode(LESSON_DATA[result.data], result.seq)
  19. );
  20. }
  21. // 把残余的buffer记下来
  22. oldBuffer = buffer;
  23. })
  24. });
  25. server.listen(4000);
  26. /**
  27. * 二进制包编码函数
  28. * 在一段rpc调用里,服务端需要经常编码rpc调用时,业务数据的返回包
  29. */
  30. function encode(data, seq) {
  31. // 正常情况下,这里应该是使用 protobuf 来encode一段代表业务数据的数据包
  32. // 为了不要混淆重点,这个例子比较简单,就直接把课程标题转buffer返回
  33. const body = Buffer.from(data)
  34. // 一般来说,一个rpc调用的数据包会分为定长的包头和不定长的包体两部分
  35. // 包头的作用就是用来记载包的序号和包的长度,以实现全双工通信
  36. const header = Buffer.alloc(6);
  37. header.writeInt16BE(seq)
  38. header.writeInt32BE(body.length, 2);
  39. const buffer = Buffer.concat([header, body])
  40. return buffer;
  41. }
  42. /**
  43. * 二进制包解码函数
  44. * 在一段rpc调用里,服务端需要经常解码rpc调用时,业务数据的请求包
  45. */
  46. function decode(buffer) {
  47. const header = buffer.slice(0, 6);
  48. const seq = header.readInt16BE();
  49. // 正常情况下,这里应该是使用 protobuf 来decode一段代表业务数据的数据包
  50. // 为了不要混淆重点,这个例子比较简单,就直接读一个Int32即可
  51. const body = buffer.slice(6).readInt32BE()
  52. // 这里把seq和数据返回出去
  53. return {
  54. seq,
  55. data: body
  56. }
  57. }
  58. /**
  59. * 检查一段buffer是不是一个完整的数据包。
  60. * 具体逻辑是:判断header的bodyLength字段,看看这段buffer是不是长于header和body的总长
  61. * 如果是,则返回这个包长,意味着这个请求包是完整的。
  62. * 如果不是,则返回0,意味着包还没接收完
  63. * @param {} buffer
  64. */
  65. function checkComplete(buffer) {
  66. if (buffer.length < 6) {
  67. return 0;
  68. }
  69. const bodyLength = buffer.readInt32BE(2);
  70. return 6 + bodyLength
  71. }
  72. // 假数据
  73. const LESSON_DATA = {
  74. 136797: "01 | 课程介绍",
  75. 136798: "02 | 内容综述",
  76. 136799: "03 | Node.js是什么?",
  77. 136800: "04 | Node.js可以用来做什么?",
  78. 136801: "05 | 课程实战项目介绍",
  79. 136803: "06 | 什么是技术预研?",
  80. 136804: "07 | Node.js开发环境安装",
  81. 136806: "08 | 第一个Node.js程序:石头剪刀布游戏",
  82. 136807: "09 | 模块:CommonJS规范",
  83. 136808: "10 | 模块:使用模块规范改造石头剪刀布游戏",
  84. 136809: "11 | 模块:npm",
  85. 141994: "12 | 模块:Node.js内置模块",
  86. 143517: "13 | 异步:非阻塞I/O",
  87. 143557: "14 | 异步:异步编程之callback",
  88. 143564: "15 | 异步:事件循环",
  89. 143644: "16 | 异步:异步编程之Promise",
  90. 146470: "17 | 异步:异步编程之async/await",
  91. 146569: "18 | HTTP:什么是HTTP服务器?",
  92. 146582: "19 | HTTP:简单实现一个HTTP服务器"
  93. }