使用线程在UDP聊天中使用一个套接字

时间:2016-10-28 12:45:44

标签: python multithreading sockets udp

我正在进行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()    

2 个答案:

答案 0 :(得分:1)

祝贺你对Python的介绍!看起来您正在使用Python 3,并且在将来的问题中,如果您明确指出您正在使用哪个版本,因为某些代码中存在轻微但破解程序的不兼容性(包括此代码!)。

我在你的程序中发现了一些错误:

  • 最重要的问题 - 正如Trevor Barnwell所说,你没有正确地调用threading.Threadtarget=参数需要是一个可调用对象(即函数),但在这种情况下,它应该只是对函数的引用。如果您在函数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