Spec-Zone.ru › Nim

std/asyncnet

SourceEdit

Этот модуль реализует высокоуровневый асинхронный API сокетов, основанный на асинхронном диспетчере, определённом в модуле asyncdispatch.

Асинхронное ввод-вывод в Nim

Асинхронный ввод-вывод в Nim состоит из нескольких слоёв (с верхнего до нижнего):

  • asyncnet модуль
  • Async await
  • asyncdispatch модуль (цикл обработки событий)
  • selectors модуль

Каждый слой строится поверх предыдущего. Модуль селекторов является абстракцией для различных системных select() механизмов, таких как epoll или kqueue. Если вы хотите, вы можете использовать его напрямую, и некоторые люди делали это успешно. Но вы должны понимать, что в Windows он поддерживает только select().

Асинхронный диспетчер реализует шаблон проактора, а также имеет реализацию IOCP. Он реализует шаблон проактора для других ОС через модуль селекторов. Здесь также реализованы фьючеры, и, на самом деле, все процедуры возвращают фьючер.

Последний слой — это преобразование async await. Это позволяет писать асинхронный код в синхронном стиле и работает аналогично await в C#. Преобразование работает путём преобразования всех асинхронных процедур в итератор.

Всё это однопоточно, полностью неблокирующее и даёт вам большой контроль. Теоретически вы должны иметь возможность работать с любыми из этих слоёв взаимозаменяемо (пока вас не интересуют платформы Windows).

Для большинства приложений использование asyncnet является лучшим вариантом, так как оно построено поверх всех слоёв, предоставляя некоторые дополнительные функции, такие как буферизация.

SSL

SSL можно включить, скомпилировав с флагом -d:ssl.

Вы должны создать новый контекст SSL с помощью функции newContext, определённой в модуле net. Затем вы можете вызвать wrapSocket для вашего сокета, используя только что созданный контекст SSL, чтобы получить SSL сокет.

Примеры

Сервер чата

Следующий пример демонстрирует простой сервер чата.

import std/[asyncnet, asyncdispatch]

var clients {.threadvar.}: seq[AsyncSocket]

proc processClient(client: AsyncSocket) {.async.} =
  while true:
    let line = await client.recvLine()
    if line.len == 0: break
    for c in clients:
      await c.send(line & "\c\L")

proc serve() {.async.} =
  clients = @[]
  var server = newAsyncSocket()
  server.setSockOpt(OptReuseAddr, true)
  server.bindAddr(Port(12345))
  server.listen()
  
  while true:
    let client = await server.accept()
    clients.add client
    
    asyncCheck processClient(client)

asyncCheck serve()
runForever()

Импорты

since, asyncdispatch, nativesockets, net, os, openssl

Типы

AsyncSocket = ref AsyncSocketDesc
Source Edit

Процедуры

proc accept(socket: AsyncSocket; flags = {SafeDisconn}): owned(
    Future[AsyncSocket]) {....raises: [ValueError, OSError, Exception],
                           tags: [RootEffect], forbids: [].}
Принимает новое подключение. Возвращает будущее, содержащее сокет клиента, соответствующий этому подключению. Если inheritable равно false (по умолчанию), полученный сокет клиента не будет наследуем дочерними процессами. Будущее завершится, когда подключение будет успешно принято. Исходный код Редактировать
proc acceptAddr(socket: AsyncSocket; flags = {SafeDisconn};
                inheritable = defined(nimInheritHandles)): owned(
    Future[tuple[address: string, client: AsyncSocket]]) {.
    ...raises: [ValueError, OSError, Exception], tags: [RootEffect], forbids: [].}

Принимает новое подключение. Возвращает будущее, содержащее сокет клиента, соответствующий этому подключению, и удалённый адрес клиента.

Если inheritable равно false (по умолчанию), полученный сокет клиента не будет наследуем дочерними процессами.

Будущее завершится, когда подключение будет успешно принято.

Исходный код Редактировать
proc bindAddr(socket: AsyncSocket; port = Port(0); address = "") {.
    ...tags: [ReadIOEffect], raises: [ValueError, OSError], forbids: [].}

Привязывает address:port к сокету.

Если address равно "", то будет привязано ADDR_ANY.

Исходный код Редактировать
proc bindUnix(socket: AsyncSocket; path: string) {....raises: [], tags: [],
    forbids: [].}
Привязывает Unix-сокет к path. Работает только на системах Unix-подобного типа: Mac OS X, BSD и Linux Исходный код Редактировать
proc close(socket: AsyncSocket) {....raises: [LibraryError, Exception, SslError],
                                  tags: [RootEffect], forbids: [].}
Закрывает сокет. Исходный код Редактировать
proc connect(socket: AsyncSocket; address: string; port: Port): owned(
    Future[void]) {....stackTrace: false, raises: [Exception], tags: [RootEffect],
                    forbids: [].}

Подключает socket к серверу по адресу address:port.

Возвращает Future, который завершится, когда подключение будет выполнено успешно или произойдёт ошибка.

Исходный код Редактировать
proc connectUnix(socket: AsyncSocket; path: string): owned(Future[void]) {.
    ...raises: [], tags: [], forbids: [].}
Привязывает Unix-сокет к path. Работает только на системах Unix-подобного типа: Mac OS X, BSD и Linux Исходный код Редактировать
proc dial(address: string; port: Port; protocol = IPPROTO_TCP; buffered = true): owned(
    Future[AsyncSocket]) {....stackTrace: false, raises: [Exception, ValueError],
                           tags: [RootEffect], forbids: [].}
Устанавливает соединение с указанной address:port парой через указанный протокол. Процедура перебирает возможные разрешения address, пока не добьётся успеха, что означает плавную работу как с IPv4, так и с IPv6. Возвращает AsyncSocket, готовый к отправке или приёму данных. Исходный код Редактировать
proc getFd(socket: AsyncSocket): SocketHandle {....raises: [], tags: [],
    forbids: [].}
Возвращает дескриптор файла сокета. Исходный код Редактировать
proc getLocalAddr(socket: AsyncSocket): (string, Port) {.
    ...raises: [OSError, Exception], tags: [], forbids: [].}

Получить локальный адрес и номер порта сокета.

Это интерфейс высокого уровня для getsockname.

Исходный код Редактировать
proc getPeerAddr(socket: AsyncSocket): (string, Port) {.
    ...raises: [OSError, Exception], tags: [], forbids: [].}

Получить адрес и номер порта удалённого сокета.

Это интерфейс высокого уровня для getpeername.

Исходный код Редактировать
proc getPeerCertificates(socket: AsyncSocket): seq[Certificate] {.
    ...raises: [Exception], tags: [], forbids: [].}
Возвращает цепочку сертификатов, полученную от удалённого узла, к которому мы подключены через указанный сокет. Рукопожатие должно быть завершено, и цепочка сертификатов должна быть успешно проверена, иначе возвращается пустая последовательность. Цепочка отсортирована от сертификата листа к корневому сертификату. Исходный код Редактировать
proc getSockOpt(socket: AsyncSocket; opt: SOBool; level = SOL_SOCKET): bool {.
    ...tags: [ReadIOEffect], raises: [OSError], forbids: [].}
Извлекает опцию opt в качестве булевого значения. Исходный код Редактировать
proc hasDataBuffered(s: AsyncSocket): bool {....raises: [], tags: [], forbids: [].}
Определяет, содержит ли AsyncSocket буферизованные данные. Исходный код Редактировать
proc isClosed(socket: AsyncSocket): bool {....raises: [], tags: [], forbids: [].}
Определяет, был ли сокет закрыт. Исходный код Редактировать
proc isSsl(socket: AsyncSocket): bool {....raises: [], tags: [], forbids: [].}
Определяет, является ли socket SSL-сокетом. Исходный код Редактировать
proc listen(socket: AsyncSocket; backlog = SOMAXCONN) {....tags: [ReadIOEffect],
    raises: [OSError], forbids: [].}

Помечает socket как принимающий подключения. Backlog задаёт максимальную длину очереди ожидающих подключений.

При ошибке генерирует ошибку OSError.

Исходный код Редактировать
proc newAsyncSocket(domain, sockType, protocol: cint; buffered = true;
                    inheritable = defined(nimInheritHandles)): owned(AsyncSocket) {.
    ...raises: [OSError], tags: [], forbids: [].}

Создаёт новый асинхронный сокет.

Эта процедура также создаст новый дескриптор файла для этого сокета.

Если inheritable равно false (по умолчанию), новый дескриптор файла не будет наследуем дочерними процессами.

Исходный код Редактировать
proc newAsyncSocket(domain: Domain = AF_INET; sockType: SockType = SOCK_STREAM;
                    protocol: Protocol = IPPROTO_TCP; buffered = true;
                    inheritable = defined(nimInheritHandles)): owned(AsyncSocket) {.
    ...raises: [OSError], tags: [], forbids: [].}

Создаёт новый асинхронный сокет.

Эта процедура также создаст новый дескриптор файла для этого сокета.

Если inheritable равно false (по умолчанию), новый дескриптор файла не будет наследуем дочерними процессами.

Исходный код Редактировать
proc newAsyncSocket(fd: AsyncFD; domain: Domain = AF_INET;
                    sockType: SockType = SOCK_STREAM;
                    protocol: Protocol = IPPROTO_TCP; buffered = true;
                    inheritable = defined(nimInheritHandles)): owned(AsyncSocket) {.
    ...raises: [OSError], tags: [], forbids: [].}

Создаёт новый AsyncSocket на основе предоставленных параметров.

Состояние неблокирующего режима предоставленного fd будет неявно включено.

Если inheritable равно false (по умолчанию), предоставленный fd не будет наследуем дочерними процессами.

Примечание: Эта процедура НЕ зарегистрирует fd в глобальном асинхронном диспетчере. Вам нужно сделать это вручную. Если вы использовали newAsyncNativeSocket для создания fd, то он уже зарегистрирован.

Исходный код Редактировать
proc recv(socket: AsyncSocket; size: int; flags = {SafeDisconn}): owned(
    Future[string]) {....stackTrace: false, raises: [Exception, ValueError],
                      tags: [RootEffect], forbids: [].}

Читает до size байт из socket.

Для буферизованных сокетов эта функция попытается прочитать все запрошенные данные. Она будет читать эти данные кусками по BufferSize.

Для небуферизованных сокетов эта функция не пытается прочитать все запрошенные данные. Она вернёт столько данных, сколько предоставит операционная система.

Если сокет отключается во время операции recv, будущее может завершиться с только частью запрошенных данных.

Если сокет отключен и нет данных для чтения, будущее завершится со значением "".

Исходный код Редактировать
proc recvFrom(socket: AsyncSocket; data: FutureVar[string]; size: int;
              address: FutureVar[string]; port: FutureVar[Port];
              flags = {SafeDisconn}): owned(Future[int]) {....stackTrace: false,
    raises: [ValueError, Exception], tags: [RootEffect], forbids: [].}

Принимает пакет данных от socket в data, размер которого должен быть не менее size. Адрес и порт отправителя пакета будут сохранены в address и port соответственно. Возвращаемое будущее завершится, как только будет получен один пакет, и вернёт размер полученного пакета.

Если произойдёт ошибка, будет поднято исключение OSError.

Эта процедура обычно используется с беспроводными сокетами (UDP-сокетами).

Примечания

  • data должен быть инициализирован длиной size.
  • address должен быть инициализирован длиной 46.
Исходный код Изменить
proc recvFrom(socket: AsyncSocket; size: int; flags = {SafeDisconn}): owned(
    Future[tuple[data: string, address: string, port: Port]]) {.
    ...stackTrace: false, raises: [Exception, ValueError], tags: [RootEffect],
    forbids: [].}

Принимает данные пакета от socket, размер которых должен быть не менее size. Возвращаемое будущее завершится, как только будет получен один пакет, и вернёт кортеж с данными полученного пакета и адресом и портом отправителя пакета.

Если произойдёт ошибка, будет поднято исключение OSError.

Эта процедура обычно используется с беспроводными сокетами (UDP-сокетами).

Исходный код Изменить
proc recvInto(socket: AsyncSocket; buf: pointer; size: int;
              flags = {SafeDisconn}): owned(Future[int]) {....stackTrace: false,
    raises: [Exception, ValueError], tags: [RootEffect], forbids: [].}

Читает до size байт из socket в buf.

Для буферизованных сокетов эта функция попытается прочитать все запрошенные данные. Она будет читать эти данные в BufferSize фрагментах.

Для небуферизованных сокетов эта функция не пытается прочитать все запрошенные данные. Она вернёт столько данных, сколько предоставит операционная система.

Если соединение с сокетом прервано во время операции чтения, будущее может завершиться с лишь частью запрошенных данных.

Если соединение с сокетом прервано и доступных данных для чтения нет, будущее завершится со значением 0.

Исходный код Изменить
proc recvLine(socket: AsyncSocket; flags = {SafeDisconn};
              maxLength = MaxLineLength): owned(Future[string]) {.
    ...stackTrace: false, raises: [Exception, ValueError], tags: [RootEffect],
    forbids: [].}

Читает строку данных из socket. Возвращаемое будущее завершится, как только будет прочитана полная строка или произойдёт ошибка.

Если полная строка прочитана, \r\L не добавляется к line, однако, если прочитана только \r\L, то line будет установлено в это значение.

Если соединение с сокетом прервано, line будет установлено в "".

Если соединение с сокетом прервано посреди строки (до прочтения \r\L ), тогда строка будет установлена в "". Частичная строка будет потеряна.

Параметр maxLength определяет максимальное количество символов, которые могут быть прочитаны. Результат усекается после этого.

Предупреждение: Флаг Peek ещё не реализован.
Предупреждение: recvLine для небуферизованных сокетов предполагает, что протокол использует \r\L для разграничения новой строки.
Исходный код Изменить
proc recvLineInto(socket: AsyncSocket; resString: FutureVar[string];
                  flags = {SafeDisconn}; maxLength = MaxLineLength): owned(
    Future[void]) {....stackTrace: false, raises: [ValueError, Exception],
                    tags: [RootEffect], forbids: [].}

Читает строку данных из socket в resString.

Если полная строка прочитана, \r\L не добавляется к line, однако, если прочитана только \r\L, то line будет установлено в это значение.

Если соединение с сокетом прервано, line будет установлено в "".

Если соединение с сокетом прервано посреди строки (до прочтения \r\L ), тогда строка будет установлена в "". Частичная строка будет потеряна.

Параметр maxLength определяет максимальное количество символов, которые могут быть прочитаны. resString будет усечён после этого.

Предупреждение: Флаг Peek ещё не реализован.
Предупреждение: recvLineInto для небуферизованных сокетов предполагает, что протокол использует \r\L для разграничения новой строки.
Исходный код Изменить
proc send(socket: AsyncSocket; buf: pointer; size: int; flags = {SafeDisconn}): owned(
    Future[void]) {....stackTrace: false, raises: [Exception], tags: [RootEffect],
                    forbids: [].}
Отправляет size байт из buf в socket. Возвращаемое будущее завершится, как только все данные будут отправлены. Исходный код Изменить
proc send(socket: AsyncSocket; data: string; flags = {SafeDisconn}): owned(
    Future[void]) {....stackTrace: false, raises: [Exception], tags: [RootEffect],
                    forbids: [].}
Отправляет data в socket. Возвращаемое будущее завершится, как только все данные будут отправлены. Исходный код Изменить
proc sendTo(socket: AsyncSocket; address: string; port: Port; data: string;
            flags = {SafeDisconn}): owned(Future[void]) {....stackTrace: false,
    raises: [Exception], tags: [RootEffect], forbids: [].}

Эта процедура отправляет data на указанный address, который может быть IP-адресом или именем хоста. Если указано имя хоста, эта функция попытается использовать каждый IP-адрес этого хоста. Возвращаемое будущее завершится, как только все данные будут отправлены.

Если произойдёт ошибка, будет поднято исключение OSError.

Эта процедура обычно используется с беспроводными сокетами (UDP-сокетами).

Исходный код Изменить
proc setSockOpt(socket: AsyncSocket; opt: SOBool; value: bool;
                level = SOL_SOCKET) {....tags: [WriteIOEffect], raises: [OSError],
                                      forbids: [].}
Устанавливает опцию opt в булевое значение, заданное value. Исходный код Изменить
proc sslHandle(self: AsyncSocket): SslPtr {....raises: [], tags: [], forbids: [].}
Получить указатель ssl для socket. Полезно для взаимодействия с openssl. Исходный код Изменить
proc wrapConnectedSocket(ctx: SslContext; socket: AsyncSocket;
                         handshake: SslHandshakeType; hostname: string = "") {.
    ...raises: [SslError], tags: [], forbids: [].}

Оборачивает подключенный сокет в контекст SSL. Эта функция фактически преобразует socket в SSL-сокет. hostname должно быть указано, чтобы клиент знал, по какому имени хоста серверу необходимо валидировать сертификат.

Это следует вызывать на подключенном сокете и немедленно выполнит SSL-рукопожатие.

Отказ от ответственности: Этот код не хорошо протестирован, может быть очень небезопасным и подвержен уязвимостям.

Исходный код Изменить
proc wrapSocket(ctx: SslContext; socket: AsyncSocket) {....raises: [SslError],
    tags: [], forbids: [].}

Оборачивает сокет в контекст SSL. Эта функция фактически преобразует socket в SSL-сокет.

Отказ от ответственности: Этот код не хорошо протестирован, может быть очень небезопасным и подвержен уязвимостям.

Исходный код Изменить

© 2006–2024 Andreas Rumpf
Licensed under the MIT License.
https://nim-lang.org/docs/asyncnet.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API