Python IRC ChatBot 在看似随机的时间后挂在 socket.recv,即使 socket.settimeout 是 8

Python IRC ChatBot hangs on socket.recv after seemingly random time even though socket.settimeout is 8

嘿,所以我决定创建一个 IRC ChatBot,其唯一目的是读取来自 Twitch Chat 的传入消息,如果赠品被关键字识别,它应该通过在聊天中发送 !enter 来输入赠品。

我在这个来源上构建了 Bot:https://github.com/BadNidalee/ChatBot。我只更改了 Run.py 中的内容,所以这是我要 post 的唯一代码。未更改的 ChatBot 确实可以工作,但它没有重新连接能力,并且会定期停止接收数据,因为套接字关闭或其他原因。

我想要改变的只是让 ChatBot 稳定并且可以一直留在 IRC 聊天中而不会断开连接。我试图通过为我的套接字设置 8 秒的超时并捕获可能发生的超时异常并在它们发生后重新连接来实现这一点。

总而言之,它似乎确实有效,即使收到大量消息,我的 Bot 也会执行它应该做的事情,它会识别 Giveaway 何时开始并相应地回答。 IRC 服务器 PING 消息也得到正确处理和回答。如果 Chat 中没有消息超过 8 秒,则会正确抛出异常,并且 Bot 也会正确地重新连接到 IRC。

但这是我的问题:在看似随机的时间之后,套接字将完全停止工作。我觉得奇怪的是它有时会工作 20 分钟,有时会工作一个小时。它不会在特殊事件发生时发生,例如在聊天中发生大量消息或其他事情,它看起来确实是随机的。它不会超时,只是什么都没有发生了。如果此时我用 CTRL-C 取消程序,控制台说最后一次调用是 "readbuffer = s.recv(1024)" 但为什么它在那个时候不抛出超时异常?如果 s.recv 被调用,如果 8 秒后没有收到任何内容,套接字应该超时,但程序只是停止并且没有更多输出,直到您手动中止它。

也许我完全走错了路。我只想要一个稳定的 24/7 全天候聊天机器人,它可以扫描一个简单的关键字并用一个简单的 !enter 回答。 这也是我第一次在 Python 编程,所以如果我违反任何约定或犯了任何严重错误,请告诉我。

getUser方法returns当前扫描的聊天线路的用户名。

getMessage方法returns扫描的聊天线路的消息。

openSocket方法打开Socket并发送JOIN NICK PASS等到IRC

#!/usr/bin/python
import string
import socket
import datetime
import time
from Read import getUser, getMessage
from Socket import openSocket, sendMessage
from Initialize import joinRoom

connected = False
readbuffer = ""


def connect():
    print "Establishing Connection..."
    irc = openSocket()
    joinRoom(irc)
    global connected
    connected = True
    irc.settimeout(8.0)
    print "Connection Established!"
    return irc

while True:
    s = connect()
    s.settimeout(8.0)
    while connected:
            try:
                readbuffer = s.recv(1024)
                temp = string.split(readbuffer, "\n")
                readbuffer = temp.pop()


                for line in temp:
                    if "PING" in line:
                        s.send(line.replace("PING", "PONG"))
                        timern = str(datetime.datetime.now().time())
                        timern = timern[0:8]
                        print timern + " PING received"
                        break

                    user = getUser(line)
                    message = getMessage(line)
                    timern = str(datetime.datetime.now().time())
                    timern = timern[0:8]
                    print timern +" " + user + ": " + message
                    if "*** NEW" in message:
                        sendMessage(s, "!enter")
                        break


            except socket.timeout:
                connected = False
                print "Socket Timed Out, Connection closed!"
                break
            except socket.error:
                connected = False
                print "Socket Error, Connection closed!"
                break

我认为您误解了超时在套接字上的工作原理。

s.settimeout(8.0)

如果无法到达目标主机,只会将 s.connect(...) 设置为超时。
此外,如果 s.setblocking(0) 通常您想要使用什么,但是仅此一项对您也无济于事(可能)。

您要使用的是:

import select
ready = select.select([s], [], [], timeout_in_seconds)
if ready[0]:
    data = s.recv(1024)

select 所做的是检查缓冲区以查看是否有任何传入数据可用,如果有则调用 recv() 这本身就是一个阻塞操作。如果缓冲区中没有任何内容 select 将 return 为空,您应该避免调用 recv().

如果您 运行 *Nix 上的所有内容,您最好还是使用 epoll

from select import epoll, EPOLLIN
poll = epoll()
poll.register(s.fileno(), EPOLLIN)

events = poll.poll(1) # 1 sec timeout
for fileno, event in events:
    if event is EPOLLIN and fileno == s.fileno():
        data = s.recv(1024)

这是如何使用 epoll 的粗略示例。
但是玩起来很有趣,你应该 read more about it