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

问题描述:

我正在研究 UDP 聊天,它应该可以只使用一个套接字监听并能够随时发送消息.例如,我将完成聊天程序,我将第一次打开它,然后第二次,我必须能够从两个程序通过 UDP 进行通信,只是每个程序只有一个打开的套接字.

I am working on UDP chat which should be listening and being able to send message any time using only one socket. Example, I will have the chat program done, I will open it first time, then second time and I must be able to communicate over UDP from both programs, simply each program has only one opened socket.

我的两个线程是监听的,是守护线程,因为我想让它不停地监听新消息,另一个是发送消息,就像普通线程一样.

My two threads are for listening, which is deamon thread, because I want it to listen to new messages nonstop, and my other is sending the messages, which is just like a normal thread.

首先,我的问题是我的线程看起来像是互相阻塞,因为如果我运行程序,我只能从我启动的第一个线程获得输出.

First of all, my problem is that it looks like my threads are blocking each other, because if I run the program, I only get output from the first thread I start.

第二个问题是我不确定我的发送函数或整个类是否写得正确,或者是否有遗漏或不正确的东西.

Second problem is that I am not sure if my sending function or the entire class is written properly, or if there is something missing or incorrect.

提前致谢.顺便说一句,我是 python 新手,我正在使用 python 3,只是为了清楚起见.

Thanks in advance. Btw, I am new into python and I am using python 3, just to make it clear.

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()    

祝贺您对 Python 的介绍!看起来您使用的是 Python 3,在以后的问题中,如果您明确说明您使用的是哪个版本会很有帮助,因为某些代码(包括此代码!)中存在轻微但破坏程序的不兼容性.

Congrats on your introduction to Python! It looks like you're using Python 3, and in future questions it's helpful if you are explicit about which version you're using because there are minor but program-breaking incompatibilities in some code (including this code!).

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

I found a few errors in your program:

  • 最重要的问题 - 正如 Trevor Barnwell 所说,您没有正确调用 threading.Thread.target= 参数需要是一个可调用对象(即函数),但在这种情况下,它应该只是对函数的引用.如果您在函数中添加括号,self.create_socket(host, port) 就像上面那样,它实际上会立即运行该函数.正如 Trevor 所解释的,您的 Sending.send() 方法被提前调用,但另外在 Receiving 中也存在类似的错误.因为 Receiving.create_socket() 创建了一个无限循环,它永远不会返回程序执行.虽然控制台输出对用户来说看起来是正确的,但实际的程序执行从未在单独的线程中运行侦听器.

  • The most major issue - as Trevor Barnwell says, you're not calling threading.Thread quite correctly. The target= argument needs to be a callable object (i.e. function), but in this case it should just be a reference to the function. If you add brackets to the function, self.create_socket(host, port) as you have above, it actually runs the function immediately. As Trevor explained, your Sending.send() method was called early, but additionally there was a similar bug in Receiving. Because Receiving.create_socket() creates an infinite loop, it never returns program execution. While the console output looks correct to the user, the actual program execution has never made it to running the listener in a separate thread.

bytearray.extend() 需要一个可迭代的整数,你现在传递的是一个字节对象元组.

bytearray.extend() takes an iterable of ints, what you're passing right now is a tuple of byte objects.

Sending.send() 中,你调用了 self.sock,但你从不给 self.sock 赋值,所以它失败了.

In Sending.send() you call self.sock, but you never assign self.sock a value, so it fails.

Sending.run() 只运行一次 Sending.send().为用户完成输入后,它立即退出,因为程序已经完成.

Sending.run() only runs Sending.send() one time. After completing input for the user, it immediately exits, because the program has finished.

如果您正在寻找适合有经验的程序员的深入的、基于项目的 Python 介绍(包括与此问题非常相似的关于基本套接字的练习,以及另一个关于线程的练习),我强烈建议您查看 WesleyChun 的核心 Python 应用程序编程".最新版本(第 3 版)包含大量 Python 2 代码,但读者只需稍作修改即可轻松移植到 Python 3.

If you're looking for an in-depth, project based introduction to Python appropriate for an experienced programmer (including an exercise very similar to this question on basic sockets, and another on threading), I highly recommend you check out Wesley Chun's "Core Python Applications Programming". The most recent edition (3rd) has a lot of Python 2 code, but it's easily portable to Python 3 with some minor work on the reader's part.

我尝试尽可能少地修改您的代码以使其正常工作,如下所示:

I tried to modify your code as little as possible to get it working, here it is:

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()