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