ホームページ  >  記事  >  ウェブフロントエンド  >  Node における TCP と UDP の簡単な分析

Node における TCP と UDP の簡単な分析

青灯夜游
青灯夜游転載
2023-04-20 18:15:261747ブラウズ

Node における TCP と UDP の簡単な分析

Node はネットワーク指向のプラットフォームであり、イベント駆動型、ノンブロッキング、シングルスレッドであり、優れたスケーラビリティを備えているため、非常に軽量です分散ネットワークでさまざまな役割を果たすのに適しています。

Node は、netdgramhttphttp2 https を提供します。 およびその他のモジュールは、それぞれ TCPUDPHTTPHTTPS を処理するために使用され、サーバーとクライアントの両方に適しています。

TCP サービスの構築

TCP サービスはネットワーク アプリケーションで非常に一般的であり、現在のアプリケーションのほとんどは TCP# に基づいています。 # # 構築された、その正式名は Transmission Control Protocol であり、OSI モデルのトランスポート層プロトコルに属します。多くのアプリケーション層プロトコルは TCP に基づいて構築されており、一般的な HTTP SMTPIMAP およびその他のプロトコル。 TCP 関連のナレッジ ポイントについてはここでは説明しません。興味がある場合は、私の コンピューター ネットワーク コラムを参照して学習してください。

TCP サーバーの作成

TCP の動作原理を基本的に理解した後、## の作成を開始できます。 #TCP サーバー側はネットワーク リクエストを受け入れます。net モジュールは、stream ## に基づいて TCP# を作成するための非同期ネットワーク API を提供します。または IPC サーバーとクライアント。 [関連チュートリアルの推奨事項: nodejs ビデオ チュートリアル プログラミング教育 ]以下の例をご覧ください。server.js## に記述されています。 # file 次のコードは次のとおりです:

import net from "net";

const server = net.createServer((socket) => {
  socket.on("data", (data) => {
    console.log("监听到客户端的数据:", data.toString());
  });
  // 监听客户端断开连接事件
  socket.on("end", () => {
    console.log("客户端断开连接");
  });
  // 发送数据给客户端
  socket.end("over over over\n");
});

// 启动服务
server.listen(3000, () => {
  console.log("服务创建成功");
});

net.createServer(listener) を通じて

TCP

サーバーを作成できます。この関数のパラメータはリンクですイベント 接続のリスナー。 ターミナルでファイルを実行すると、サービスは正常に作成され、出力がターミナルに表示されます。

nodemon .\server.js

先ほど、net.createServer を通じてサーバーを作成し、次に

net.connect

を使用してセッション用のクライアントを作成しました。具体的なコードは次のとおりです。 : <pre class="brush:js;toolbar:false;">import net from &quot;net&quot;; const client = net.connect({ port: 3000 }, () =&gt; { client.write(&quot;今晚出去吃饭,收到请 over\n&quot;); }); // 接收服务端的数据 client.on(&quot;data&quot;, (data) =&gt; { console.log(&quot;接收服务端的数据: &quot;, data.toString()); // 断开连接 client.end(); }); // 断开连接 client.on(&quot;end&quot;, () =&gt; { console.log(&quot;断开连接&quot;); });</pre> 以下に示すように、この時点で 2 つのファイルを実行します。

次に、このような例、具体的なコードを図に示します。以下:

Node における TCP と UDP の簡単な分析

特定の実行結果については、次のアニメーションを参照してください:

Node における TCP と UDP の簡単な分析

私が使用するクライアントでは

client.write()Node における TCP と UDP の簡単な分析 は複数回データを送信していますが、

setTimeout

以外のデータだけが正常であり、setTimeout で送信される連続データは 1 回に 1 つではないようです返される代わりに、それらはランダムにマージされて返され、スティッキー パケットがここに表示されます。 TCP ネットワーク内の小さなデータ パケットには、特定の最適化戦略があります。

Negle

アルゴリズム、最適化なしで一度に 1 バイトのコンテンツのみが送信される場合、ネットワークは、有効なデータが非常に少ないデータグラムでいっぱいになるため、ネットワーク リソースが大幅に無駄になります。この状況に対して、このアルゴリズムでは、バッファ内のデータが送信前に一定の量または一定の時間に達する必要があります。小さなデータ パケットはバッファによって送信され、ネットワークを最適化するためにアルゴリズムが結合されます。この最適化によりネットワークを効率的に利用できるようになりますが、データの送信に遅延が発生する場合があります。 Node では、

TCP

がデフォルトで Negle アルゴリズムを有効にするため、socket.setNoDelay(true)## を呼び出すことができます。 #Negle アルゴリズムを削除して、write() がすぐにネットワークにデータを送信できるようにします。 #Close

Nagle

このアルゴリズムは、サーバー側でマージを完了するため、常に有効であるとは限りません。Node における TCP と UDP の簡単な分析TCP

受信したデータは、最初に独自のバッファーに格納され、その後、アプリケーションにそれを受信するように通知されます。アプリケーション層は、ネットワークまたはその他の問題によりブロックされます。その理由は、

TCP バッファーからデータを時間内に取得できない場合、複数のデータ ブロックが TCP バッファーに格納され、スティッキーが発生するためです。パケット。 TCP 原則

Node では、createServer()

を呼び出すことは、

new Server() を呼び出すことと同じです。 、具体的な結果を以下の図に示します: <p>这主要的原因它在 <code>Node 源码中有如下定义,所以调用 createServer() 函数实际上调用的是 new Server(),具体代码如下图所示:

function createServer(options, connectionListener) {
  return new Server(options, connectionListener);
}

该构造函数的定义主要有如下所示:

function Server(options, connectionListener) {
  EventEmitter.call(this);
  // 注册连接到来时执行的回调
  if (typeof options === "function") {
    connectionListener = options;
    options = {};
    this.on("connection", connectionListener);
  } else if (options == null || typeof options === "object") {
    options = { ...options };

    if (typeof connectionListener === "function") {
      this.on("connection", connectionListener);
    }
  }
  // 服务器建立的连接数
  this._connections = 0;

  this[async_id_symbol] = -1;
  this._handle = null;
  this._usingWorkers = false;
  this._workers = [];
  this._unref = false;
  // 服务器下的所有连接是否允许半连接
  this.allowHalfOpen = options.allowHalfOpen || false;
  // 有连接时是否注册读事件
  this.pauseOnConnect = !!options.pauseOnConnect;
  this.noDelay = Boolean(options.noDelay);
  // 是否支持keepAlive
  this.keepAlive = Boolean(options.keepAlive);
  this.keepAliveInitialDelay = ~~(options.keepAliveInitialDelay / 1000);
}
ObjectSetPrototypeOf(Server.prototype, EventEmitter.prototype);
ObjectSetPrototypeOf(Server, EventEmitter);

listen

它返回的是一个普通的 JavaScript 对象,接着调用 listen 函数监听端口,listen 方法支持多种使用方式主要有以下这几种方法:

  • 传入的是一个已经创建的 TCP 服务器,而不是需要创建的一个服务器;
  • 传进来是一个对象,并且带了 fd 字段;
  • 创建了一个 TCP 服务器,并启动该服务器,如果传入了 host 会对其进行域名解析;

该方法的的主要逻辑有如下代码所示:

Server.prototype.listen = function (...args) {
  /*
         处理入参,根据文档我们知道listen可以接收好几个参数,
          假设我们这里是只传了端口号9297  
        */
  var normalized = normalizeArgs(args);
  //  normalized = [{port: 9297}, null];
  var options = normalized[0];
  var cb = normalized[1];
  // 第一次listen的时候会创建,如果非空说明已经listen过
  if (this._handle) {
    throw new errors.Error("ERR_SERVER_ALREADY_LISTEN");
  }
  // listen成功后执行的回调
  var hasCallback = cb !== null;
  if (hasCallback) {
    // listen成功的回调
    this.once("listening", cb);
  }

  options = options._handle || options.handle || options;
  // 第一种情况,传进来的是一个TCP服务器,而不是需要创建一个服务器
  if (options instanceof TCP) {
    this._handle = options;
    this[async_id_symbol] = this._handle.getAsyncId();
    listenIncluster(this, null, -1, -1, backlogFromArgs);
    return this;
  }
  // 第二种,传进来一个对象,并且带了fd
  if (typeof options.fd === "number" && options.fd >= 0) {
    listenIncluster(this, null, null, null, backlogFromArgs, options.fd);
    return this;
  }
  // 创建一个tcp服务器
  var backlog;
  if (typeof options.port === "number" || typeof options.port === "string") {
    backlog = options.backlog || backlogFromArgs;
    // 第三种 启动一个TCP服务器,传了host则先进行DNS解析
    if (options.host) {
      lookupAndListen(
        this,
        options.port | 0,
        options.host,
        backlog,
        options.exclusive
      );
    } else {
      listenIncluster(
        this,
        null,
        options.port | 0,
        4,
        backlog,
        undefined,
        options.exclusive
      );
    }
    return this;
  }
};

listenInCluster

在每种方式的最后丢回调用 listenIncluster 方法,该方法主要做的事情是区分 master 进程 和 worker 进程,采用不同的处理策略:

  • mastr 进程: 直接调用 server._listen 启动监听;
  • worker 进程: 使用 cluster._getServer 处理传入的 server 对象,修改 server._handle 再调用了 server._listen 启动监听;

构建 UDP 服务

UDP 又称用户数据包协议,与 TCP 一样同属于网络层传输层。UDPTCP 最大的不同是 UDP 不是面向链接的。

创建 <span style="font-size: 18px;">UDP</span> 服务

创建 UDP 套接字十分简单,UDP 套接字一旦创建,既可以作为客户端发送数据,也可以作为服务端接收数据,下面的代码创建了一个 UDP 套接字,具体代码如下所示:

import dgram from "node:dgram";

const server = dgram.createSocket("udp4");

server.on("error", (err) => {
  console.error(`server error:\n${err.stack}`);
  server.close();
});

server.on("message", (msg, rinfo) => {
  console.log(`server got: ${msg} from ${rinfo.address}:${rinfo.port}`);
});

server.on("listening", () => {
  const address = server.address();
  console.log(`server listening ${address.address}:${address.port}`);
});

server.bind(3000);

该套接字将接收所有网课上 3000 端口上的消息,在绑定完成后,将触发 listening 事件,会终端执行,会输出 server listening 0.0.0.0:3000 字段。

接下来我们创建一个客户端和服务端进行对话,具体代码如下所示:

import dgram from "node:dgram";
import { Buffer } from "node:buffer";

const message = Buffer.from("你个叼毛");
const client = dgram.createSocket("udp4");

client.send(message, 0, message.length, 3000, "localhost", () => {
  client.close();
});

终端的最终输出结果如下图所示

Node における TCP と UDP の簡単な分析

UDP 广播

dgram 模块中,可以使用 socket 端口对象的 setBroadcast 方法来进行数据的广播:

socket.setBroadcast(flag);
  • flag: 当 flagtrue 时,UDP 服务器或者客户端可以利用其所用的 socket 端口对象的 send 方法中的地址修改为广播地址。

服务端的代码定义在 server.js 文件,具体代码如下所示:

import dgram from "dgram";

const server = dgram.createSocket("udp4");

server.on("message", function (msg, rinfo) {
  console.log(
    "server got: " + msg + " from " + rinfo.address + ":" + rinfo.port
  );
});

server.on("listening", function () {
  var address = server.address();
  console.log("server listening " + address.address + ":" + address.port);
});

server.bind(3000);

客户端的代码定义在 server.js 文件,具体代码如下定义:

import dgram from "dgram";
import { Buffer } from "buffer";

const socket = dgram.createSocket("udp4");
const params = process.argv.splice(2);

socket.bind(function () {
  socket.setBroadcast(true);
});

const message = Buffer.from(...params);

socket.send(message, 0, message.length, 3000, "255.255.255.255", () => {
  socket.close();
});

具体运行效果如下图所示:

Node における TCP と UDP の簡単な分析

更多node相关知识,请访问:nodejs 教程

以上がNode における TCP と UDP の簡単な分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はjuejin.cnで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。