Как я могу отправлять сообщения на несколько каналов с использованием Python 3 Select ()? - PullRequest
0 голосов
/ 11 мая 2019

Я пытаюсь создать бота Twitch в Python 3, который будет одновременно отслеживать и отправлять сообщения на несколько каналов.Я сделал это с потоками, но это, очевидно, требует от процессора, и я прочитал, что использование Select () является более эффективным.Приведенный ниже код позволяет мне читать чат по нескольким каналам, но я не знаю, как определить, являются ли те соединения, которые были возвращены как доступные для записи, именно теми, к которым я хочу написать.

Могу ли я передать список объектов, у которых есть соединение с сокетом, а также идентификатор, чтобы я знал, какие из них вернулись как доступные для записи?

Я прочитал несколько сообщений stackoverflowсвязанных с использованием select (), а также с другими источниками в Интернете, но, как программист-любитель, я не могу разобраться с этим.

#!/usr/bin/env

import socket
import select

HOST = "irc.chat.twitch.tv"
PORT = 6667
NICK = "channelname"
PASS = 'oauth:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
CHANNEL = "channelname"


def create_sockets(usr_list):
    final_socket_list = []
    channels_first_element = 0
    channels_last_element = len(usr_list)
    for index in range(channels_first_element, channels_last_element):
        channel = usr_list[index]
        s = socket.socket()
        s.connect((HOST, PORT))
        s.setblocking(False)
        s.send(bytes("PASS " + PASS + "\r\n", "UTF-8"))
        s.send(bytes("NICK " + NICK + "\r\n", "UTF-8"))
        s.send(bytes("JOIN #" + channel + " \r\n", "UTF-8"))
        s.send(bytes('CAP REQ :twitch.tv/membership\r\n'.encode('utf-8')))
        s.send(bytes('CAP REQ :twitch.tv/commands\r\n'.encode('utf-8')))
        s.send(bytes('CAP REQ :twitch.tv/tags\r\n'.encode('utf-8')))
        final_socket_list.append(s)
    return final_socket_list


def main():
    alive = True
    user_list = ['channelone', 'channeltwo', 'channelthree']
    user_sockets = create_sockets(user_list)

    while alive:
        readable, writable, errorreads = select.select(user_sockets, user_sockets, [])
        if len(readable) != 0:
            for element in readable:
                print(str(element.recv(1024), "utf-8"))

if __name__ == "__main__":
    main()

1 Ответ

0 голосов
/ 11 мая 2019

Первое, на что следует обратить внимание, это то, что аргументы select() должны сообщать select(), когда возвращаться, т. Е. Включив сокет в аргумент first / read_fds, вы говорите select(), что нужно возвращать каккак только у этого сокета есть входящие данные, которые готовы к чтению, и, включив сокет в аргумент second / write_fds, вы сообщаете select() возврат, как только этот сокет имеет буферное пространство, готовое для чтения.write-to.

В связи с этим важно (если вы хотите использовать процессор), чтобы включить сокет в аргумент second / write_fds select(), только если у вас есть данные, которые вы хотите отправитьк этому сокету, как только будет доступно пространство в буфере исходящих данных этого сокета.Если вы просто всегда передаете все свои сокеты в аргумент write_fds (как вы в настоящее время делаете в опубликованном коде), то select() почти всегда будет возвращаться немедленно (поскольку сокеты обычно почти всегда имеют буферное пространство, доступное для записи в), и вы в конечном итоге развернете процессор почти на 100%, что очень неэффективно.

Обратите внимание, что для сервера с низкой нагрузкой, который использует блокирующие сокеты TCP, обычно достаточно просто всегда передавать[] в качестве второго аргумента select(), при условии, что вы никогда не будете фактически заполнять буфер исходящих данных сокета (и если вы это сделаете, то следующий вызов send() в этом сокете будет просто блокироваться, пока не произойдетбуферное пространство доступно, и это нормально).Если вы хотите использовать неблокирующие сокеты, вы можете сделать упрощающее предположение о том, что ни один исходящий буфер данных сокета никогда не будет заполнен (в этом случае передача [] в аргумент write_fds будет нормальным), или быть равным 100%.Для надежной работы вам потребуется включить собственную очередь FIFO исходящих данных для каждого сокета и включить каждый сокет только в аргумент write_fds - если его очередь FIFO не пуста и когда сокет указывает, что он готов для записи,send() столько байтов, сколько вы можете из заголовка очереди FIFO сокета.

Что касается того, в какие сокеты вы хотите писать, то это будет полностью зависеть от того, что пытается сделать ваше приложение.и, вероятно, не будет зависеть от того, какие сокеты выбрали для записи.Большинство программ включают словарь (или какой-либо другой подобный механизм поиска) для быстрого определения того, какой объект данных соответствует данному сокету, поэтому, когда сокет select()'s в качестве готового для чтения, вы можете выяснить, какойобъект данных, данные, поступающие из этого сокета, должны рассматриваться как «поступающие из».В ответ вы можете написать любые сокеты, в которые нужно записать.

...