套接字P2P和线程

时间:2015-03-18 22:18:01

标签: python multithreading sockets python-3.x python-multithreading

我正在尝试创建一个消息传递应用程序(很可能是以其他方式完成),我正在使用线程,因此我可以随时发送或接收消息

我已经为每个线程使用了类,一个用于接收一个用于发送的消息

发送文件

class snd (threading.Thread):
    def run (self):
        while True:
            msg = input("You: ")
            if msg == endprompt:
                closecon()
                break
            else:
                s.send(bytes(msg, "UTF-8"))
                sen()
                pass

然后由此调用:

def sen ():
    s.start()
s = snd()

接收是类似的,只是你期望的模糊变化

接收

class rcv (threading.Thread):
    def run (self):
        while True:
            reply = s.recv(2048)
            print("User: ", reply.decode("UTF-8"))
            rec()

这又称为:

def rec ():
    r.start()
r = rcv()

问题

“发送”不是snd的属性 与recv相同我假设

这是在对象中触发我的套接字上的发送的问题 所以我尝试了一些事情

我试图解决这个问题

一个是: 正在改变from socket import *import socket as sck

然后我继续尝试将类似的东西添加到类中,例如class snd (threading.Thread, sck.send)

这再次无效并吐出错误: AttributeError:'module'对象没有属性'send'

问题

我该如何解决这个问题?所以我可以在对象内发送消息吗?

有条件的信息:

s = sck.socket(sck.AF_INET, sck.SOCK_STREAM)

~~~~

def closecon ():
    s.close()

~~~~

def con ():
    s.connect((host, port))

1 个答案:

答案 0 :(得分:2)

首先让我指出一个可能使你的情况变得清晰的简单问题。让我们分析一下这些代码片段之间的情况:

s = sck.socket(sck.AF_INET, sck.SOCK_STREAM) # s defined at the global level

def closecon ():
    # python looks for s in the local namespace, doesn't find it
    # and uses the global s above.
    s.close()  # s.close() refers to the socket object's (defined above) .close() method
def con ():
    s.connect((host, port))  # Again s not defined locally, so it uses the global s.

在你的函数中使用全局变量,或任何真正被认为是不好的做法,并最终使你的代码难以阅读,即使对你自己也是如此。您遇到的具体问题归结为此代码段:

def sen ():
    s.start()
s = snd()  # s is now a snd object, no longer a socket

通过重新定义s引用的内容,您的类snd,无论何时运行(),它都使用s的定义而不是原始套接字。

class snd (threading.Thread):
    def run (self):
        while True:
            msg = input("You: ")
            if msg == endprompt:
                closecon()
                break
            else:
                s.send(bytes(msg, "UTF-8")) # snd objects (subclassed Thread) has no send() method
                sen()
                pass

如果您能够关注我,直到现在,您会发现每当run()类的snd中的代码运行时,它就会转到行s.send(bytes(msg, "UTF-8"))并使用当前行s的全局定义,因为它在本地范围内没有s定义。要修复此问题,可以使套接字对象和snd对象的变量名称彼此不同,并更新您想要引用套接字的代码,以及带有新名称的snd对象。我不建议将其作为代码的解决方案,即使它可能有效。

我推荐的内容

Thread子类中,您应该覆盖__init__()的{​​{1}}方法,以包括接受套接字对象。您的snd类的代码(以及类似于您的rec类)看起来更像是:

threading.Thread

当您想要使用线程时,您只需稍微修改一下代码:

class snd (threading.Thread):
    ## Added __init__ override
    def __init__(self, sock):
        threading.Thread.__init__(self)
        self.sock = sock
    def run (self):
        while True:
            msg = input("You: ")
            if msg == endprompt:
                closecon()
                break
            else:
                self.sock.send(bytes(msg, "UTF-8")) # Now we use the socket specifically given to this object
                sen()
                pass # You don't need this

请注意,此时代码仍无效,因为您的con()和closecon()函数需要重写:

my_sock = sck.socket(sck.AF_INET, sck.SOCK_STREAM)
sender = snd(my_sock)
sender.start()

最终,您的代码中还有其他一些问题需要重新设计,但我认为我已经留下了足够的信息来帮助您弄清楚如何做到这一点,或者了解如何解决这个问题。如果你不想阅读下面关于python的任何更基本的解释,我将至少给你留下几点:

  • 网络非常复杂。
  • 套接字真的很低级,使网络变得困难。
  • 线程是一种高级工具,需要合理的程序流理解才能正常运行。
  • 子类和继承是一个中等先进的概念。

如果你对语言基础知识缺乏扎实的基础理解,尝试使用上述任何一点进行编码将是一场复制粘贴和混乱的斗争,这种混乱是由于理解上的小漏洞而产生的。在尝试使用套接字和线程进行联网之前,我最终建议您获取一本python书或者密切阅读在线教程。如果你只是试图让某些东西起作用,并且更专注于其他代码,我建议你研究其他更高级别的网络库来实现python。除此之外,祝你的项目好运。

如果你想让你的代码对你和他人更有意义,那么了解局部变量和全局变量之间的区别,以及python中变量作用域的概念将有助于你理解为什么你的几段代码会失败。随着它变大,它将帮助您更好地组织代码。如果您只想快速停机,请继续阅读。我不是python或编程方面的专家,所以也许这里或那里可能存在错误,但我会尽力解释。

<强>作用域

当您的python代码正在运行并遇到变量名时,它会检查该变量的定义。首先,它检查本地命名空间,然后从那里上升到最高命名空间,即全局命名空间。请注意,它永远不会查找较低名称空间的定义。这意味着您可以在不同的范围级别重用变量名称,而不会发生冲突。如果您不知道命名空间是什么,只需将其视为您在代码中所处的位置。例如,

def closecon(sock):
    sock.close()

def con(sock, host, port):
    sock.connect((host,port))

如果您认为此代码会在屏幕上显示x=1 # Here you are at the global scope/namespace def func(): x=2 # << Here you are in func's namespace print(x) # Back out at the global namespace ,那么您就错了。 2行在func范围内,x=2行在全局范围内。执行print(x)时,它会从全局范围开始查找print(x)的值,并在第一行中将其定义为x。在给定范围内将变量定义为局部变量时,通常会在离开该范围时销毁它们。因此,如果您调用x=1,它会在本地范围内设置func(),然后退出该函数。通过执行x=2或只是完成一个函数,我们将离开其范围,并且销毁范围中定义的变量x(取消引用和垃圾收集)。由于func的内容范围低于全局范围,因此在全局级别执行的代码永远不会看到在其中分配的值。

因此,范围使函数和类非常孤立,但解决方案是将参数传递给这些结构。通过将参数传递给函数,他们将在其本地范围中定义的变量设置为从另一个范围传递给它们的值。要解释一下,使用args执行此代码的操作:

return

你必须使用全局变量:

def func(value):
    print(value)
# By passing 100 to func(), there is essentially 
# an invisible line in func that does 'value=100'.
func(100)

要理解它提供的实用程序,只需让python搜索变量的全局范围,这两个片段都会将def func(): value = x print(value) # By not using arguments, we have to manually do that invisible # assignment of value=100 by setting x=100 and then the function setting value=x x=100 func() 打印到屏幕上

使用函数参数:

2

使用全局:

x = 1
def func_args(value):
    print(value*2)

func_args(x)

但请注意,x=1 def func_globals(): print(x*2) func_globals() 只会打印func_globals(),而x*2会为传递给它的任何值打印func_args(value)。这意味着,为了使用value*2之类的func_globals(),您必须知道func_args()设置为您在调用x时所需的内容,或者手动设置它是您每次调用它时想要使用的值。使用func_globals(),您只需要知道您作为func_args()传递给函数的内容。

这绝不是对变量作用域和命名空间的彻底解释,但希望您对作用域的内容有一些基本的了解。在python上有关于这类事情的互联网上有很好的信息,我建议熟悉这些概念,因为它们是如何在python和其他语言中构建代码的核心。