← Cloudflare Workers / workers / runtime-apis
TCP-сокеты
Среда выполнения Workers предоставляет connect() API для создания исходящих TCP-соединения ↗ из Workers.
Многие протоколы прикладного уровня строятся поверх протокола управления передачей (TCP). Для работы таких протоколов, включая SSH, MQTT, SMTP, FTP, IRC и большинство сетевых протоколов баз данных, в том числе MySQL, PostgreSQL и MongoDB, необходим базовый TCP socket API.
connect()
connect() функция возвращает TCP-сокет с читаемый и для записи поток данных. Это позволяет читать и записывать данные непрерывно, пока соединение остаётся открытым.
connect() предоставляется как Runtime API, и доступ к нему осуществляется путем импорта connect функцию из cloudflare:sockets. Этот процесс похож на импорт встроенных модулей в 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):Socketconnect()принимает либо строку URL, либоSocketAddressчтобы задать имя хоста и номер порта для подключения, а также необязательный объект конфигурации,SocketOptions. Он возвращает экземплярSocket.
SocketAddress
-
hostnameстрока- Имя хоста для подключения. Например:
cloudflare.com.
- Имя хоста для подключения. Например:
-
portчисло- Номер порта для подключения. Пример:
5432.
- Номер порта для подключения. Пример:
SocketOptions
-
secureTransport"off" | "on" | "starttls": по умолчанию используетсяoff- Определяет, использовать ли TLS ↗ при создании TCP сокета.
off: не использовать TLS.on: используйте TLS.starttls: изначально не используйте TLS, но разрешите обновление сокета до TLS с помощью вызоваstartTls().
-
allowHalfOpenboolean: по умолчаниюfalse- Определяет, будет ли автоматически закрываться сторона записи TCP-сокета при достижении конца файла (EOF). Если задано значение
false, записываемая сторона TCP-сокета автоматически закроется при EOF. Если задано значениеtrue, записываемая сторона TCP-сокета останется открытой при EOF. - Этот параметр аналогичен тому, что предлагает Node.js
netмодуль ↗ и обеспечивает совместимость с кодом, который его использует.
- Определяет, будет ли автоматически закрываться сторона записи TCP-сокета при достижении конца файла (EOF). Если задано значение
SocketInfo
-
remoteAddressstring | null- Адрес удалённого узла, к которому подключён сокет. Может быть указан не всегда.
-
localAddressstring | null- Адрес локальной конечной точки сети для этого сокета. Может быть указан не всегда.
Socket
-
readable: ReadableStream- Возвращает читаемую сторону TCP-сокета.
-
writable: WritableStream- Возвращает записываемую сторону TCP-сокета.
-
WritableStreamвозвращаемый, принимает только фрагменты размеромUint8Arrayили его представления.
-
openedPromise<SocketInfo>- Этот promise переходит в состояние resolved, когда соединение сокета установлено, и в состояние rejected, если в сокете возникает ошибка.
-
closedPromise<void>- Этот promise переходит в состояние resolved, когда сокет закрывается, и в состояние rejected, если в сокете возникает ошибка.
-
close()Promise<void>- Закрывает TCP-сокет. Оба потока, для чтения и для записи, закрываются принудительно.
-
startTls(): Socket- Обновляет незащищённый сокет до защищённого, использующего TLS, и возвращает новый Сокет. Обратите внимание, что для вызова
startTls(), вам нужно задатьsecureTransportкstarttlsпри первом вызовеconnect()чтобы создать сокет.
- Обновляет незащищённый сокет до защищённого, использующего TLS, и возвращает новый Сокет. Обратите внимание, что для вызова
Оппортунистический TLS (StartTLS)
Многие системы на основе TCP, включая базы данных и почтовые серверы, требуют, чтобы клиенты использовали оппортунистический TLS (также известный как StartTLS ↗) при подключении. В этой схеме клиент сначала создаёт незащищённый TCP-сокет без TLS, а затем повышает его до защищённого TCP-сокета с TLS. Метод connect() API упрощает эту задачу, предоставляя метод startTls(), который возвращает новый Socket экземпляр, использующий TLS:
import { connect } from "cloudflare:sockets"
const address = {
hostname: "example-postgres-db.com",
port: 5432
};
const socket = connect(address, { secureTransport: "starttls" });
const secureSocket = socket.startTls();startTls()можно вызывать только в том случае, еслиsecureTransportимеет значениеstarttlsпри создании исходного TCP сокета.- Как только
startTls()вызывается, исходный сокет закрывается, и из него больше нельзя ни читать, ни писать в него. В примере выше в любой момент после того, какstartTls()вызывается, используйте только что созданныйsecureSocket. Any existing readers and writers based off the original socket will no longer work. You must create new readers and writers from the newly createdsecureSocket. 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>;Закрытие TCP-соединений
Закрыть TCP соединение можно, вызвав close() для сокета. Это закроет обе стороны сокета: и читаемую, и записываемую.
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Особенности
- Исходящие TCP сокеты к Диапазоны IP-адресов Cloudflare ↗ блокируются.
- TCP-сокеты нельзя создавать в глобальной области видимости и использовать совместно в разных запросах. Всегда создавайте TCP-сокеты внутри обработчика (например,
fetch(),scheduled(),queue()) илиalarm(). - Каждый открытый TCP-сокет учитывается в максимальном количестве открытые соединения которые могут быть открыты одновременно.
- Если TCP-сокет создается внутри Durable Object, он удерживает Durable Object в памяти и приводит к начислению платы за длительность работы в течение до 15 минут на каждое соединение. По истечении 15 минут сокет перестает удерживать Durable Object активным (сам сокет продолжает работать), и стандартные правила вытеснения возобновить.
- По умолчанию Workers не могут создавать исходящие TCP-соединения на порту
25для отправки писем на почтовые серверы SMTP. Cloudflare Email Workers предоставляет API для обработки и пересылки электронной почты. - Поддержка обработки входящих TCP-соединений скоро появится ↗. В настоящее время невозможно установить входящее TCP соединение с вашим Worker, например, с помощью
CONNECTметод HTTP.
Устранение неполадок
Ознакомьтесь с описанием типичных сообщений об ошибках, которые могут появиться при работе с TCP Sockets, узнайте, что они означают и как их устранить.
proxy request failed, cannot connect to the specified address
Ваш сокет подключается к адресу, который запрещён. Примеры запрещённых адресов включают IP-адреса Cloudflare, localhost, и IP-адреса частной сети.
Если вам нужно подключаться к адресам на порту 80 или 443 чтобы выполнять HTTP-запросы, используйте fetch.
TCP Loop detected
Ваш сокет подключается обратно к Worker, который инициировал исходящее соединение. Иными словами, Worker подключается сам к себе. В настоящее время это не поддерживается.
Connections to port 25 are prohibited
Ваш сокет подключается к адресу на порте 25. Обычно этот порт используется почтовыми серверами SMTP. Workers не может создавать исходящие подключения через порт 25. Рассмотрите возможность использовать Cloudflare Email Workers взамен.