Многопоточность для подключения сокета в python

#python #multithreading #sockets #websocket #twitch

Вопрос:

Я пытаюсь найти действительно беспокойные чаты twitch для ключевых слов, но иногда сокет останавливается на долю секунды, но за эту долю секунды может пройти 5 сообщений. Я думал о реализации некоторой многопоточности, но в приведенном ниже коде не повезло. Похоже, что все они не могут поймать ключевое слово или все преуспевают. Любая помощь будет признательна. Код ниже:

 import os import time from dotenv import load_dotenv import socket import logging from emoji import demojize  import threading  # loading environment variables load_dotenv()  # variables for socket server = "irc.chat.twitch.tv" port = 6667 nickname = "frankied003" token = os.getenv("TWITCH_TOKEN") channel = "#xqcow"  # creating the socket and connecting sock = socket.socket() sock.connect((server, port)) sock.send(f"PASS {token}n".encode("utf-8")) sock.send(f"NICK {nickname}n".encode("utf-8")) sock.send(f"JOIN {channel}n".encode("utf-8"))  while True:  consoleInput = input(  "Enter correct answer to the question (use a ',' for multiple answers):"  )   # if console input is stop, the code will stop ofcourse lol  if consoleInput == "stop":  break   # make array of all the correct answers  correctAnswers = consoleInput.split(",")  correctAnswers = [answer.strip().lower() for answer in correctAnswers]   def threadingFunction():   correctAnswerFound = False   # while the correct answer is not found, the chats will keep on printing  while correctAnswerFound is not True:   while True:  try:  resp = sock.recv(2048).decode(  "utf-8"  ) # sometimes this fails, hence retry until it succeeds  except:  continue  break   if resp.startswith("PING"):  sock.send("PONGn".encode("utf-8"))   elif len(resp) gt; 0:  username = resp.split(":")[1].split("!")[0]  message = resp.split(":")[2]  strippedMessage = " ".join(message.split())   # once the answer is found, the chats will stop, correct answer is highlighted in green, and onto next question  if str(strippedMessage).lower() in correctAnswers:  print(bcolors.OKGREEN   username   " - "   message   bcolors.ENDC)  correctAnswerFound = True  else:  if username == nickname:  print(bcolors.OKCYAN   username   " - "   message   bcolors.ENDC)  # else:  # print(username   " - "   message)    t1 = threading.Thread(target=threadingFunction)  t2 = threading.Thread(target=threadingFunction)  t3 = threading.Thread(target=threadingFunction)   t1.start()  time.sleep(.3)  t2.start()  time.sleep(.3)  t3.start()  time.sleep(.3)   t1.join()  t2.join()  t3.join()  

Ответ №1:

Во-первых, нет особого смысла позволять 3 потокам параллельно читать в одном и том же сокете, это приводит только к путанице и условиям гонки.

Основная проблема, однако, заключается в том, что вы предполагаете, что один recv человек всегда будет читать одно сообщение. Но TCP работает не так. TCP не имеет понятия о сообщении, а представляет собой только поток байтов. Сообщение — это концепция прикладного уровня. Один recv может содержать одно сообщение, несколько сообщений, части сообщений …

Таким образом, вы должны фактически проанализировать полученные данные в соответствии с семантикой, определенной протоколом приложения, т. е.

  1. инициализировать некоторый буфер
  2. получите некоторые данные из сокета и добавьте их в буфер — не декодируйте данные
  3. извлеките все полные сообщения из буфера, декодируйте и обработайте каждое сообщение отдельно
  4. оставьте оставшиеся неполные сообщения в буфере
  5. продолжайте с #2

Кроме того, не отбрасывайте слепо ошибки во recv(..).decode(..) время . Учитывая, что вы используете блокирующий сокет recv , обычно произойдет сбой только в том случае, если возникнет фатальная проблема с подключением, и в этом случае повторная попытка не поможет. Проблема, скорее всего, связана с тем, что вы вызываете decode неполные сообщения, которые также могут означать недопустимую кодировку utf-8. Но поскольку вы просто игнорируете проблему, вы, по сути, теряете сообщения.

Комментарии:

1. Как вы узнаете, завершено сообщение или нет. У Twitch нет прикладного протокола или отличной документации по этому вопросу.

2. ААААА, я понял, ты был прав. Я принимал первое сообщение в каждом ответе, но ответов было несколько.