UDP чат, где сервер тоже может общаться - PullRequest
1 голос
/ 29 апреля 2019

Я нашел на GitHub проект с открытым исходным кодом , который очень прост: один и тот же скрипт 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>')

Как видно из рисунка, он работает очень хорошо:

enter image description here

Но моя цель - сделать серверприсоединиться к чату тоже.

Возможно ли для комнаты чата UDP, чтобы сервер мог также выступать в роли клиента, пока он работает в качестве сервера?

1 Ответ

0 голосов
/ 29 апреля 2019

Чтобы сервер также запускал клиента, вы можете вставить эту строку непосредственно перед вызовом RunServer() в части кода __main__:

    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% ядра ЦП в течение всего времени его работы. Это ужасно неэффективно. Причина этой проблемы здесь, внутри RunServer():

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

Обратите внимание, что в классе Python Queue вызов get() блокируется до тех пор, пока не будет получено больше данных, не позволяя процессору вращаться. Но в приведенном выше коде get() никогда не вызывается, если только / 10 * * не является пустым, поэтому поток никогда не блокируется.

Исправить несложно, просто полностью удалите строку while not recvPackets.empty(), чтобы get() вызывался, даже когда очередь пуста. get() не вернется, пока не будет данных для возврата.

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