Spec-Zone.ru › Nim 1

asyncnet

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

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

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

  • asyncnet модуль
  • Асинхронное ожидание
  • asyncdispatch модуль (цикл обработки событий)
  • selectors модуль

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

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

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

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

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

SSL ===

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

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

Примеры

Сервер чата

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

import 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
Исходный код Изменить

Процедуры

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: [].}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Источник Редактировать
proc dial(address: string; port: Port; protocol = IPPROTO_TCP; buffered = true): owned(
    Future[AsyncSocket]) {...}{.raises: [Exception, ValueError], tags: [RootEffect].}
Устанавливает соединение с указанной address:port парой через указанный протокол. Процедура перебирает возможные разрешения address до тех пор, пока не будет успеха, что означает, что она без проблем работает как с IPv4, так и с IPv6. Возвращает AsyncSocket, готовый к отправке или приёму данных. Источник Редактировать
proc connect(socket: AsyncSocket; address: string; port: Port): owned(
    Future[void]) {...}{.raises: [Exception], tags: [RootEffect].}

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

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

Источник Редактировать
proc recvInto(socket: AsyncSocket; buf: pointer; size: int;
              flags = {SafeDisconn}): owned(Future[int]) {...}{.
    raises: [Exception, ValueError], tags: [RootEffect].}

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

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

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

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

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

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

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

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

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

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

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

Источник Редактировать
proc send(socket: AsyncSocket; buf: pointer; size: int; flags = {SafeDisconn}): owned(
    Future[void]) {...}{.raises: [Exception], tags: [RootEffect].}
Отправляет size байт из buf в socket. Возвращаемое будущее завершится, как только все данные будут отправлены. Источник Редактировать
proc send(socket: AsyncSocket; data: string; flags = {SafeDisconn}): owned(
    Future[void]) {...}{.raises: [Exception], tags: [RootEffect].}
Отправляет data в socket. Возвращаемое будущее завершится, как только все данные будут отправлены. Источник Редактировать
proc acceptAddr(socket: AsyncSocket; flags = {SafeDisconn};
                inheritable = defined(nimInheritHandles)): owned(
    Future[tuple[address: string, client: AsyncSocket]]) {...}{.
    raises: [Exception, ValueError, OSError], tags: [RootEffect].}

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

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

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

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

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

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

Если сокет отключён, line будет установлено на "".

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

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

Предупреждение: Флаг Peek ещё не реализован.

Предупреждение: recvLineInto для небуферизованных сокетов предполагает, что протокол использует \r\L для разграничения новой строки.

Источник Редактировать
proc recvLine(socket: AsyncSocket; flags = {SafeDisconn};
              maxLength = MaxLineLength): owned(Future[string]) {...}{.
    raises: [Exception, ValueError], tags: [RootEffect].}

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

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

Если сокет отключён, line будет установлено на "".

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

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

Предупреждение: Флаг Peek ещё не реализован.

Предупреждение: recvLine для небуферизованных сокетов предполагает, что протокол использует \r\L для разграничения новой строки.

Источник Редактировать
proc listen(socket: AsyncSocket; backlog = SOMAXCONN) {...}{.tags: [ReadIOEffect],
    raises: [OSError].}

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

Вызывает ошибку OSError при ошибке.

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

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

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

Источник Редактировать
proc connectUnix(socket: AsyncSocket; path: string): owned(Future[void]) {...}{.
    raises: [], tags: [].}
Привязывает сокет Unix к path. Это работает только на системах Unix-подобного типа: Mac OS X, BSD и Linux Источник Редактировать
proc bindUnix(socket: AsyncSocket; path: string) {...}{.tags: [ReadIOEffect],
    raises: [].}
Привязывает сокет Unix к path. Это работает только на системах Unix-подобного типа: Mac OS X, BSD и Linux Источник Редактировать
proc close(socket: AsyncSocket) {...}{.raises: [Exception, LibraryError, SslError],
                                  tags: [RootEffect].}
Закрывает сокет. Источник Редактировать
proc wrapSocket(ctx: SslContext; socket: AsyncSocket) {...}{.raises: [SslError],
    tags: [].}

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

Предупреждение: Этот код не подвергался тщательной проверке, может быть небезопасным и подвержен уязвимостям.

Исходный код Редактировать
proc wrapConnectedSocket(ctx: SslContext; socket: AsyncSocket;
                         handshake: SslHandshakeType; hostname: string = "") {...}{.
    raises: [SslError], tags: [].}

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

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

Предупреждение: Этот код не подвергался тщательной проверке, может быть небезопасным и подвержен уязвимостям.

Исходный код Редактировать
proc getPeerCertificates(socket: AsyncSocket): seq[Certificate] {...}{.
    raises: [Exception], tags: [].}
Возвращает цепочку сертификатов, полученную от удалённого узла, к которому мы подключены через данный сокет. Рукопожатие должно быть завершено, и цепочка сертификатов должна быть успешно проверена, в противном случае возвращается пустая последовательность. Цепочка отсортирована от лиственного сертификата к корневому. Исходный код Редактировать
proc getSockOpt(socket: AsyncSocket; opt: SOBool; level = SOL_SOCKET): bool {...}{.
    tags: [ReadIOEffect], raises: [OSError].}
Извлекает параметр opt в виде логического значения. Исходный код Редактировать
proc setSockOpt(socket: AsyncSocket; opt: SOBool; value: bool;
                level = SOL_SOCKET) {...}{.tags: [WriteIOEffect], raises: [OSError].}
Устанавливает параметр opt в логическое значение, указанное value. Исходный код Редактировать
proc isSsl(socket: AsyncSocket): bool {...}{.raises: [], tags: [].}
Определяет, является ли socket сокетом SSL. Исходный код Редактировать
proc getFd(socket: AsyncSocket): SocketHandle {...}{.raises: [], tags: [].}
Возвращает дескриптор файла сокета. Исходный код Редактировать
proc isClosed(socket: AsyncSocket): bool {...}{.raises: [], tags: [].}
Определяет, был ли сокет закрыт. Исходный код Редактировать
proc sendTo(socket: AsyncSocket; address: string; port: Port; data: string;
            flags = {SafeDisconn}): owned(Future[void]) {...}{.raises: [Exception],
    tags: [RootEffect].}

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

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

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

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

Принимает данные датаграммы от 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]]) {...}{.
    raises: [Exception, ValueError], tags: [RootEffect].}

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

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

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

Исходный код Редактировать

Экспорт

SOBool

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

Spec-Zone.ru

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