我正在进行UDP聊天,它应该是监听,并且只能使用一个套接字随时发送消息。例如,我将完成聊天程序,我将第一次打开它,然后第二次,我必须能够通过UDP从两个程序进行通信,只需每个程序只有一个打开的套接字。
我的两个线程是用于监听,这是deamon线程,因为我希望它不间断地收听新消息,而我的另一个是发送消息,就像普通线程一样。
首先,我的问题是看起来我的线程相互阻塞,因为如果我运行程序,我只从第一个线程开始输出。
第二个问题是我不确定我的发送功能或整个班级是否写得正确,或者是否有遗漏或不正确。
提前致谢。顺便说一句,我是python的新手,我使用的是python 3,只是为了说清楚。
import socket
import threading
import logging
import time
from sys import byteorder
class Sending():
def __init__(self, name, tHost, tPort):
self.name = name
self.host = tHost
self.port = tPort
def set_name(self, name):
self.name = name
def send(self, name, tHost, tPort, msgType, dgramSize):
logging.debug('Starting send run')
message = input('Enter message: ')
data = bytearray()
data.extend( (name.encode('utf-8'), message.encode('utf-8'), msgType.to_bytes(1, byteorder = 'little')) )
#data.extend(message.encode(encoding='utf_8'))
self.sock.sendto(bytearray(data), (tHost, tPort))
def run(self):
th2 = threading.Thread(name = 'send', target=self.send('username', 'localhost', 8001, 1, 1400))
th2.start()
class Receiving():
def __init__(self, host, port):
self.host = host
self.port = port
def create_socket(self, host, port):
logging.debug('Starting socket')
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((host, port))
#print ('socket ready')
time.sleep(5)
while True:
data, addr = sock.recvfrom(1500)
print('Prijata:' + data + addr)
def run(self):
th1 = threading.Thread(name = 'rec', target=self.create_socket('localhost', 8000))
th1.setDaemon(True)
th1.start()
if __name__ == '__main__':
#print ('running')
rec = Receiving('localhost', 8000)
send = Sending('username', 'localhost', 8001)
send.run()
rec.run()
答案 0 :(得分:1)
祝贺你对Python的介绍!看起来您正在使用Python 3,并且在将来的问题中,如果您明确指出您正在使用哪个版本,因为某些代码中存在轻微但破解程序的不兼容性(包括此代码!)。
我在你的程序中发现了一些错误:
最重要的问题 - 正如Trevor Barnwell所说,你没有正确地调用threading.Thread
。 target=
参数需要是一个可调用对象(即函数),但在这种情况下,它应该只是对函数的引用。如果您在函数self.create_socket(host, port)
中添加括号,就像上面一样,它实际上会立即运行该函数。正如Trevor所解释的那样,您的Sending.send()
方法被提前调用,但另外在Receiving
中也存在类似的错误。因为Receiving.create_socket()创建了一个无限循环,所以它永远不会返回程序执行。虽然控制台输出看起来对用户来说是正确的,但实际的程序执行从未使它在单独的线程中运行监听器。
bytearray.extend()
采用可迭代的整数,你现在传递的是一个字节对象元组。
在Sending.send()
中,您拨打了self.sock
,但是您从未为self.sock
分配值,因此会失败。
Sending.run()
仅运行一次Sending.send()
。完成用户输入后,会立即退出,因为程序已完成。
如果您正在寻找适合经验丰富的程序员的深入,基于项目的Python介绍(包括与基本套接字上的此问题非常类似的练习,以及另一个关于线程的练习),我强烈建议您检查Wesley Chun"核心Python应用程序编程"。最新版本(第3版)有很多Python 2代码,但它很容易移植到Python 3上,并且在读者的一部分上做了一些小的工作。
我尝试尽可能少地修改您的代码以使其正常工作,这里是:
import socket
import threading
import logging
import time
class Sending():
def __init__(self, name, tHost, tPort, target):
self.name = name
self.host = tHost
self.port = tPort
self.target_port = target
self.sock = self.create_socket()
def create_socket(self):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((self.host, self.port))
return sock
def set_name(self, name):
self.name = name
def send_loop(self):
while True:
logging.debug('Starting send run')
message = input('Enter message: ')
data = bytearray()
data.extend(message.encode('utf-8'))
self.sock.sendto(bytearray(data), (self.host, self.target_port))
def run(self):
th2 = threading.Thread(name='send', target=self.send_loop)
th2.start()
class Receiving():
def __init__(self, host, port):
self.host = host
self.port = port
def create_socket(self):
logging.debug('Starting socket')
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((self.host, self.port))
print ('socket ready')
time.sleep(5)
while True:
data, addr = sock.recvfrom(1500)
print('\nPrijata:' + data.decode('utf-8') + str(addr))
def run(self):
th1 = threading.Thread(name='rec', target=self.create_socket)
print("Made it here")
th1.daemon = True
th1.start()
return
if __name__ == '__main__':
print('running')
rec = Receiving('localhost', 8000)
send = Sending('username', 'localhost', 8001, 8000)
rec.run()
send.run()
答案 1 :(得分:0)
线程没有相互阻塞。在创建线程之前调用send
。
th2 = threading.Thread(name = 'send', target=self.send('username', 'localhost', 8001, 1, 1400))
这一行调用发送时间:
self.send('username', 'localhost', 8001, 1, 1400)
我认为你打算这样做:
th2 = threading.Thread(
target=self.send
args=('username', 'localhost', 8001, 1, 1400))
这样一个线程就会启动下一行的调用发送。
另外两件事:
raw_input
而不是input