跳转到内容
搜索文档

TCP sockets

最后更新 查看 MarkdownAgent 设置

Workers 运行时提供 connect() API,用于从 Workers 创建出站 TCP 连接

许多应用层协议构建在传输控制协议(TCP)之上。这些应用层协议(包括 SSH、MQTT、SMTP、FTP、IRC,以及大多数数据库线协议,如 MySQL、PostgreSQL、MongoDB)都需要底层 TCP 套接字 API 才能工作。

connect()

connect() 函数返回一个 TCP 套接字,包含可读可写数据流。只要连接保持打开,您就可以持续读写数据。

connect() 作为运行时 API 提供,通过从 cloudflare:sockets 导入 connect 函数来访问。此过程类似于在 Node.js 中导入内置模块。请参阅以下代码块,了解创建 TCP 套接字、向其写入数据并将套接字的可读端作为响应返回的示例:

import { connect } from 'cloudflare:sockets';

export default {
  async fetch(req): Promise<Response> {
    const gopherAddr = { hostname: "gopher.floodgap.com", port: 70 };
    const url = new URL(req.url);

    try {
      const socket = connect(gopherAddr);

      const writer = socket.writable.getWriter()
      const encoder = new TextEncoder();
      const encoded = encoder.encode(url.pathname + "\r\n");
      await writer.write(encoded);
      await writer.close();

      return new Response(socket.readable, { headers: { "Content-Type": "text/plain" } });
    } catch (error) {
      return new Response("Socket connection failed: " + error, { status: 500 });
    }
  }
} satisfies ExportedHandler;
  • connect(address: SocketAddress | string, options?: optional SocketOptions) : Socket
    • connect() 接受 URL 字符串或 SocketAddress 来定义要连接的主机名和端口号,以及可选的配置对象 SocketOptions。它返回 Socket 实例。

SocketAddress

  • hostname string

    • 要连接的主机名。示例:cloudflare.com
  • port number

    • 要连接的端口号。示例:5432

SocketOptions

  • secureTransport "off" | "on" | "starttls" — Defaults to off

    • 指定创建 TCP 套接字时是否使用 TLS
    • off — 不使用 TLS。
    • on — 使用 TLS。
    • starttls — 初始不使用 TLS,但允许通过调用 startTls() 将套接字升级为使用 TLS。
  • allowHalfOpen boolean — Defaults to false

    • 定义 TCP 套接字的可写端是否在文件结束(EOF)时自动关闭。设置为 false 时,TCP 套接字的可写端会在 EOF 时自动关闭。设置为 true 时,TCP 套接字的可写端在 EOF 时保持打开。
    • 此选项类似于 Node.js net 模块 提供的选项,可与使用该模块的代码互操作。

SocketInfo

  • remoteAddress string | null

    • 套接字所连接的对等方地址。可能并非始终设置。
  • localAddress string | null

    • 此套接字的本地网络端点地址。可能并非始终设置。

Socket

  • readable : ReadableStream

    • 返回 TCP 套接字的可读端。
  • writable : WritableStream

    • 返回 TCP 套接字的可写端。
    • 返回的 WritableStream 仅接受 Uint8Array 或其视图的块。
  • opened Promise<SocketInfo>

    • 建立套接字连接时此 Promise 会解析;套接字遇到错误时会被拒绝。
  • closed Promise<void>

    • 套接字关闭时此 Promise 会解析;套接字遇到错误时会被拒绝。
  • close() Promise<void>

    • 关闭 TCP 套接字。可读和可写流都会被强制关闭。
  • startTls() : Socket

    • 将不安全的套接字升级为使用 TLS 的安全套接字,返回新的 Socket。请注意,要调用 startTls(),在最初调用 connect() 创建套接字时必须将 secureTransport 设置为 starttls

Opportunistic TLS (StartTLS)

许多基于 TCP 的系统(包括数据库和电子邮件服务器)要求客户端在连接时使用 opportunistic TLS(也称为 StartTLS)。在此模式下,客户端首先创建不安全的 TCP 套接字(不使用 TLS),然后将其升级为使用 TLS 的安全 TCP 套接字。connect() API 通过提供 startTls() 方法简化了此过程,该方法返回使用 TLS 的新 Socket 实例:

import { connect } from "cloudflare:sockets"

const address = {
  hostname: "example-postgres-db.com",
  port: 5432
};
const socket = connect(address, { secureTransport: "starttls" });
const secureSocket = socket.startTls();
  • 仅当创建初始 TCP 套接字时将 secureTransport 设置为 starttls,才能调用 startTls()
  • 调用 startTls() 后,初始套接字会关闭,无法再从中读取或写入。在上面的示例中,startTls() 调用后的任何时刻,您都应使用新创建的 secureSocket。基于原始套接字的任何现有 reader 和 writer 将不再工作。您必须从新创建的 secureSocket 创建新的 reader 和 writer。
  • 对现有套接字只能调用一次 startTls()

处理错误

创建新 TCP 套接字、从套接字读取或向套接字写入时,若要处理错误,请将这些调用包装在 try...catch 语句块中。以下示例打开与 Google.com 的连接,发起 HTTP 请求并返回响应。如果失败并抛出异常,将返回 500 响应:

import { connect } from 'cloudflare:sockets';
const connectionUrl = { hostname: "google.com", port: 80 };
export interface Env { }
export default {
  async fetch(req, env, ctx): Promise<Response> {
    try {
      const socket = connect(connectionUrl);
      const writer = socket.writable.getWriter();
      const encoder = new TextEncoder();
      const encoded = encoder.encode("GET / HTTP/1.0\r\n\r\n");
      await writer.write(encoded);
      await writer.close();

      return new Response(socket.readable, { headers: { "Content-Type": "text/plain" } });
    } catch (error) {
      return new Response(`Socket connection failed: ${error}`, { status: 500 });
    }
  }
} satisfies ExportedHandler<Env>;

Close TCP connections

您可以通过调用套接字上的 close() 来关闭 TCP 连接。这将关闭套接字的可读端和可写端。

import { connect } from "cloudflare:sockets"

const socket = connect({ hostname: "my-url.com", port: 70 });
const reader = socket.readable.getReader();
socket.close();

// After close() is called, you can no longer read from the readable side of the socket
const reader = socket.readable.getReader(); // This fails

Considerations

  • Cloudflare IP 范围 的出站 TCP 套接字被阻止。
  • TCP 套接字不能在全局作用域中创建并在请求之间共享。您应始终在处理器内创建 TCP 套接字(例如 fetch()scheduled()queue())或 alarm()
  • 每个打开的 TCP 套接字都计入可同时打开的最大连接数
  • 在 Durable Object 内创建时,打开的 TCP 套接字会使 Durable Object 保持在内存中,每个连接最多产生 15 分钟的 duration 费用。15 分钟后,套接字不再保持 Durable Object 存活(套接字本身继续运行),标准驱逐规则 恢复适用。
  • 默认情况下,Workers 无法在端口 25 上创建出站 TCP 连接以向 SMTP 邮件服务器发送电子邮件。Cloudflare Email Workers 提供用于处理和转发电子邮件的 API。
  • 对入站 TCP 连接的支持即将推出。目前,无法向您的 Worker 发起入站 TCP 连接,例如使用 CONNECT HTTP 方法。

Troubleshooting

查看使用 TCP Sockets 时可能遇到的常见错误消息说明、含义及解决方法。

proxy request failed, cannot connect to the specified address(代理请求失败,无法连接到指定地址)

您的套接字正在连接到不允许的地址。不允许的地址示例包括 Cloudflare IP、localhost 和私有网络 IP。

如果您需要在端口 80443 上连接到地址以发起 HTTP 请求,请使用 fetch

TCP Loop detected(检测到 TCP 环路)

您的套接字正在连接回发起出站连接的 Worker。换句话说,Worker 正在连接回自身。目前不支持此操作。

Connections to port 25 are prohibited(禁止连接到端口 25)

您的套接字正在连接到端口 25 上的地址。这通常是 SMTP 邮件服务器使用的端口。Workers 无法在端口 25 上创建出站连接。请考虑改用 Cloudflare Email Workers

这篇文档对您有帮助吗?