【问题标题】:UDP Chat where Server can chat too服务器也可以聊天的UDP聊天
【发布时间】:2019-04-28 21:01:09
【问题描述】:

我在 GitHub 上找到了一个非常简单的开源 project:可以将相同的 Python 脚本用作服务器或客户端。

import socket
import threading
import queue
import sys
import random
import os


#Client Code
def ReceiveData(sock):
    while True:
        try:
            data,addr = sock.recvfrom(1024)
            print(data.decode('utf-8'))
        except:
            pass

def RunClient(serverIP):
    host = socket.gethostbyname(socket.gethostname())
    port = random.randint(6000,10000)
    print('Client IP->'+str(host)+' Port->'+str(port))
    server = (str(serverIP),5000)
    s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    s.bind((host,port))

    name = input('Please write your name here: ')
    if name == '':
        name = 'Guest'+str(random.randint(1000,9999))
        print('Your name is:'+name)
    s.sendto(name.encode('utf-8'),server)
    threading.Thread(target=ReceiveData,args=(s,)).start()
    while True:
        data = input()
        if data == 'qqq':
            break
        elif data=='':
            continue
        data = '['+name+']' + '->'+ data
        s.sendto(data.encode('utf-8'),server)
    s.sendto(data.encode('utf-8'),server)
    s.close()
    os._exit(1)
#Client Code Ends Here


#Server Code
def RecvData(sock,recvPackets):
    while True:
        data,addr = sock.recvfrom(1024)
        recvPackets.put((data,addr))

def RunServer():
    host = socket.gethostbyname(socket.gethostname())
    port = 5000
    print('Server hosting on IP-> '+str(host))
    s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    s.bind((host,port))
    clients = set()
    recvPackets = queue.Queue()

    print('Server Running...')

    threading.Thread(target=RecvData,args=(s,recvPackets)).start()

    while True:
        while not recvPackets.empty():
            data,addr = recvPackets.get()
            if addr not in clients:
                clients.add(addr)
                continue
            clients.add(addr)
            data = data.decode('utf-8')
            if data.endswith('qqq'):
                clients.remove(addr)
                continue
            print(str(addr)+data)
            for c in clients:
                if c!=addr:
                    s.sendto(data.encode('utf-8'),c)

    s.close()
#Serevr Code Ends Here

if __name__ == '__main__':
    if len(sys.argv)==1:
        RunServer()
    elif len(sys.argv)==2:
        RunClient(sys.argv[1])
    else:
        print('Run Serevr:-> python Chat.py')
        print('Run Client:-> python Chat.py <ServerIP>')

从图片中可以看出,效果非常好:

但我的目标是让服务器也加入聊天。

UDP 聊天室是否可以让服务器在作为服务器工作时也充当客户端?

【问题讨论】:

  • 绝对有可能——一种简单的方法是让服务器产生一个线程,然后从线程内调用 RunClient('127.0.0.1')。
  • 是否有理由不只运行程序的两个副本(以及在其他机器上运行更多副本)?
  • @JeremyFriesner,我试过了,终端是空的。也许我没有捕捉到服务器本身的数据。如果您知道该怎么做,欢迎提出答案
  • @DavisHerring,这是一个挑战

标签: python sockets udp chat chatroom


【解决方案1】:

要让服务器也运行客户端,您可以在代码的__main__ 部分调用RunServer() 之前插入此行:

    threading.Thread(target=RunClient,args=('127.0.0.1',)).start()

这将启动一个线程,该线程与服务器的事件循环并行运行 RunClient('127.0.0.1')

但是,在代码正常工作之前,您还需要修复一些其他问题。特别是:

RunServer() 的顶部,您将host 设置为服务器计算机上其中一个网络接口卡的IP 地址。这意味着服务器只能接受该网络接口上的传入 TCP 连接,这意味着(除非该网络接口恰好是内部/环回接口),服务器不会接受来自其的到 127.0.0.1 的连接自己的客户端线程。解决这个问题的简单方法是将host 设置为空字符串,这样服务器将接受所有本地网络接口上的传入 TCP 连接,问题就会消失。

def RunServer():
    host = '' # was:  socket.gethostbyname(socket.gethostname())
    [...]

第二个问题是您的服务器的事件循环正忙于等待,导致服务器进程在整个运行期间不必要地使用了 100% 的 CPU 内核。这是非常低效的。这个问题的原因就在这里,在RunServer()

while True:
    while not recvPackets.empty():
        data,addr = recvPackets.get()
        [...]

请注意,在 Python Queue 类中,对 get() 的调用会阻塞,直到有更多数据要接收,从而阻止 CPU 旋转。但是在上面的代码中,get() 永远不会被调用,除非/直到recvPackets 非空,所以线程永远不会阻塞。

修复很简单,只需将while not recvPackets.empty() 行完全删除,这样即使队列为空,get() 也会被调用。 get() 在有数据返回之前不会返回。

while True:
   data,addr = recvPackets.get()
   [...]

【讨论】:

  • 谢谢@Jeremy,但终端返回给我一个 'OSError: [WinError 10049] The requested address is not valid in its context' snag.gy/h0jiNv.jpg 。你在你的电脑上试过了吗?
  • 我在我的 Mac 上运行它。
  • Python 版本?
  • Python 版本 3.7.0
猜你喜欢
  • 1970-01-01
  • 2015-09-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-29
  • 1970-01-01
相关资源
最近更新 更多