Модераторы: Daevaorn
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Мультиклиентский чат. Укажите мне путь, 2 действия одновременно 
:(
    Опции темы
deHimer
Дата 28.3.2011, 21:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 20
Регистрация: 24.9.2009

Репутация: нет
Всего: нет



Здравия Всем! Выставляю на обсуждение вопрос, над которым бьюсь несколько дней.
Есть сервер и множество клиентов. Вся работа выполняется на сокетах.
Как сделать так чтобы пользователь мог в одно и то же время и получать сообщения из общего чата  и писать их?

Я думал просто пока пользователь не начнет ввод - ждать сообщений и выводить их.

Мои варианты: 
2 потока, один принимает данные  и выводит их на экран.
или
1 цикл с проверкой на пользовательский ввод с клавиатуры. Если ввода не происходит - слушать сокет.

Если первый вариант - то надо на сервер отправить информацию о номере порта UDP сервера, ожидающего сообщения.
Во втором мне не понятно как сделать ожидание события "нажатие клавиши".

Какие идеи?

Код с попыткой реализации первого варианта:
Сервер:
Код

import socket
import threading
import re

host = 'localhost'
common_chat_port = 14
individual_port = 13

#имя клиента -> (статус, (хост, порт)).
#статус - в общем чате(1)/личная переписка(2)
clients = {}

class Common_messages_send(threading.Thread):
    """
    Рассылка сообщений клиентам из состоящим в общем чате.
    """
    def __init__(self, sock, data, sendler_addres):
        threading.Thread.__init__(self)
        self.sock = sock
        self.data = data
        self.sendler_addres = sendler_addres
        
    def run(self):        
        for client in clients.values():
            
            #отправка только если статус в общем чате и получатель не является оптправителем
            if (client[0] == 1) and (client[1]!=self.sendler_addres):

                self.sock.sendto(self.data, client[1])
        print ' cm_done'        

class Common_chat(threading.Thread):
    """
    Процесс, получающий и перенаправляющий клиентам сообщения для общего чата.
    """
    def __init__(self, sock):
        threading.Thread.__init__(self)
        self.sock = sock
        
    def run(self):

        while True:

            data, sendler_addres = self.sock.recvfrom(1024)
            Common_messages_send(self.sock, data, sendler_addres).start()
            print ' cc_done' 
            
class Clients(threading.Thread):
    """
    Процесс, поддерживающий связь с конкретным пользователем.
    Выполняет команды от пользователя и организует личную переписку.
    """
    def __init__(self, sock, host_port):    
        threading.Thread.__init__(self)
        self.sock = sock
        self.host_port = host_port
        
    def run(self):
        
        have_connection = True
        
        #получение, проверка и сохранение информации о клиенте
        try:
            user_name = self.sock.recv(20)        
        except:
            have_connection = False
        
        while have_connection:
            
            if clients and (user_name in clients.keys()):
                self.sock.send('Name chosen busy. Choose another name')
            else:
                if re.search('\W', user_name):
                    self.sock.send("Login invalid")
                else:    
                    self.sock.send("Ok")
                    #сохраняем данные пользователя
                    clients[user_name]=(1, self.host_port) # 1 - 
                    print ('User %s has connected' % user_name)
                    break
            
            user_name = self.sock.recv(20)                                
        
        
        #информация о собеседнике (в режиме работы 2 - индивидуальный чат)
        #[name]->(host, port)
        companion = {}
        
        #цикл работы с клиентом
        have_connection = True
        while have_connection:
            
            try:
                buffer = self.sock.recv(1024)
            except:
                have_connection = False
                break
                
            if buffer == '!c':
                connected_clients_list=''
                for client in clients.keys():
                    connected_clients_list += client + ' '
                  
                self.sock.send(connected_clients_list)
            elif buffer == '!a':
                client = clients[user_name]
                client[0] = 1
                clients[user_name] = client
            elif buffer =='!e':
                have_connection = False
                print ('User %s is disconnected' % user_name)
                        
        self.sock.close()                

#создание и настрока сокета для общего чата
common_chat_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
common_chat_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
common_chat_socket.bind((host, common_chat_port))  

#создание и настрока сокета для соединения с клиентом
individual_chat_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
individual_chat_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
individual_chat_socket.bind((host, individual_port))
individual_chat_socket.listen(5)

#запуск потока реализующего общий чат
Common_chat(common_chat_socket).start()

while True:

    #создание потока связи с клиентом
    sock, client_host_port = individual_chat_socket.accept()
    Clients(sock, client_host_port).start()



Клиент:
Код

import socket
import threading


#параметры соединений
host = 'localhost'
common_chat_port = 14
individual_port = 13


common_chat_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect((host, individual_port))

#режим чата. 1 - общий. 2 - индивидуальный
chat_mode = 1   

class Common_chat_client(threading.Thread):
    def __init__(self, sock):
        self.sock = sock
        threading.Thread.__init__(self)
    
    def run(self):
        
        global chat_mode
        self.sock.sendto('new user connected', ('localhost', 14))
        while True:
            if chat_mode==1:
                buffer, addres=self.sock.recvfrom(1024)
                print 'All>'+ buffer       

class Work(threading.Thread):
    def __init__(self, client_sock, common_sock):
        self.client_sock = client_sock
        self.common_sock = common_sock
        threading.Thread.__init__(self)
    def run(self):
        
        global chat_mode
        
        #выбор имени для клиента
        while True:
            user_name = raw_input('Select the desired name: ')
            self.client_sock.send(user_name)
            server_answer = self.client_sock.recv(100)
            
            if server_answer=='Ok':
                print 'Name valid. Connection is established.'
                break
            else:
                print server_answer

        print "!h - help\n!a - common chat\n!c - client list\n!l - chat with login\n!e - exit"

            
        #основной цикл работы
        while True:
            buffer = raw_input('>')
            if buffer=='!c':
                #получение списка контактов
                command = '!c'
                self.client_sock.send(command)
                connected_clients_list = self.client_sock.recv(64000)
                print connected_clients_list
            elif buffer=='!a':
                chat_mode = 1
                self.client_socket.send(buffer)
            elif buffer=='!h':
                print "!h - help\n!a - common chat\n!c - client list\n!l - chat with login\n!e - exit"
            elif buffer=='!e':
                self.client_socket.send('!e')
                break
            elif buffer=='!l':
                chat_mode = 2
                pass
            else:
                #получение и отправка сообщений
                if chat_mode==1:
                    self.common_sock.sendto(buffer, (host, common_chat_port))
                else:
                    pass

        client_socket.close()
        
Work(client_socket ,common_chat_socket).start()
Common_chat_client(common_chat_socket).start()              

PM MAIL WWW   Вверх
deHimer
Дата 31.3.2011, 06:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 20
Регистрация: 24.9.2009

Репутация: нет
Всего: нет



4ый вопрос.. и 4ый ответ самому же себе..
потом выложу код решения
PM MAIL WWW   Вверх
bilbobagginz
Дата 31.3.2011, 07:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Naughtius Maximus
****


Профиль
Группа: Экс. модератор
Сообщений: 8813
Регистрация: 2.3.2004
Где: Israel

Репутация: 6
Всего: 317



можно посмотреть как такое делается в существующих протоколах. примеры реализации протоколов (в хронологическом порядке изобретения):
  • TALK
  • IRC
  • XMPP



--------------------
Я ещё не демон. Я только учусь.
PM WWW   Вверх
deHimer
Дата 31.3.2011, 21:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 20
Регистрация: 24.9.2009

Репутация: нет
Всего: нет



Более менее дышащий код.

Код Клиента:
Код

#Сети ЭВМ. лр №3
#мультиклиентский чат. клиентская часть. Автор: deHimer

import socket
from threading import Thread

#глобалные переменные клиента 
host = 'localhost'
tcp_socket_port = 13                    

opp_udp_host_port = ()  # host, port оппонента в режиме приватноо чата

#режим чата. 1 - общий. 2 - индивидуальный
chat_mode = 1

 

# UDP сервер принимающий входящие сообщения и ответственный за получение
# приглашений от другого пользователя чата на личную переписку

class UDP_server(Thread):
    def __init__(self, udp_sock):
        self.udp_server_socket = udp_sock
        Thread.__init__(self)
        
    def run(self):
    
        global opp_udp_host_port
        global chat_mode
        
        # бесконечный цикл ожидающий новых сообщений
        while True: 
            #получаем сообщение/команду
            data, address = self.udp_server_socket.recvfrom(100)
                        
            if data != '!privateChat':
                #вывод сообщения из общего чата
                print data
            else:
                #обработка запроса на установление приватного чата
                
                #узнаем как зовут оппонента
                data, address = self.udp_server_socket.recvfrom(100)
                print data
                
                #отвечаем, хочется ли
                data = raw_input('now enter \'yes\' or \'no\' ')
                self.udp_server_socket.sendto(data, address)
                
                # если мы согласны, то мы должны перейти в режим личного чата
                # и получить адрес UDP порта оппонента
                if data == 'yes':
                    
                    #получить имя хоста и номер порта оппонента
                    opp_host, address = self.udp_server_socket.recvfrom(25)                    
                    self.udp_server_socket.sendto('udp host accept', address)
                    
                    opp_port, address = self.udp_server_socket.recvfrom(5)
                    self.udp_server_socket.sendto('udp port accept', address)
                    
                    opp_udp_host_port = (opp_host, int(opp_port))
                    chat_mode = 2
                    
                else:
                    pass


#настрока сокета и запуск UDP сервера
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udp_socket.bind((host, 0))      # если указан 0 - выберется совбодный порт

#узнаем, какой порт получил наш UDP сервер
udp_socket_host_port = udp_socket.getsockname()
udp_socket_port = str(udp_socket_host_port[1])

print 'you on port'+udp_socket_port

#запуск сервера UDP
UDP_server(udp_socket).start()


                    
#настрока TCP клиента, выполняющего связь с сервером                    

# создание TCP клиента
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_socket.connect((host, tcp_socket_port))

#отправка серверу адреса UDP сервера
tcp_socket.send(host)
tcp_socket.recv(100)

tcp_socket.send(udp_socket_port)
tcp_socket.recv(100)



#выбор клиентом имени и согласование его с сервером
while True:
    user_name = raw_input('Select the desired name: ')
    tcp_socket.send(user_name)
    server_answer = tcp_socket.recv(100)
    
    if server_answer=='Name valid':
        print 'Name valid. Connection is established.'
        break
    else:
        print server_answer
    
        
#создание UDP сокета для приватного общения с сервером оппонента
udp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        
print "!help  !common_chat  !clients_list  !private_chat  !exit\n"

#основной цикл общения с TCP сервером
while True:
    user_input = raw_input('')
    if user_input == '!clients_list':
        #получение списка контактов
        tcp_socket.send(user_input)
        print tcp_socket.recv(64500)        
        
    elif user_input=='!common_chat':    
        
        if chat_mode == 2:
            udp_client_socket.sendto(user_name + 'out from private chat', opp_udp_host_port)
        
        #переход в режим общего чата        
        chat_mode = 1
        tcp_socket.send(user_input)
        
        print 'you in common chat mode'
        
    elif user_input=='!help':
        print "!help  !common_chat  !clients_list  !private_chat  !exit\n"
        
    elif user_input=='!exit':
        tcp_socket.send('!exit')
        break
        
    elif user_input=='!private_chat':
        #запрос на создание приватного чата
        tcp_socket.send(user_input)
        
        #отправка имени оппонента
        opp_name = raw_input('Enter opponent name: ')               
        tcp_socket.send(opp_name)
        
        #получение результата запроса
        answer = tcp_socket.recv(100)
        
        if answer=='yes':
            #получаем адресс UDP сервера оппонента
            opponent_host = tcp_socket.recv(100)
            tcp_socket.send('opponent udp host accept')
            
            opponent_port = tcp_socket.recv(100)            
            opponent_port = int(opponent_port)
            tcp_socket.send('opponent udp port accept')
            
            opp_udp_host_port = (opponent_host, opponent_port)
            
            chat_mode = 2
            
            print 'you in private chat mode'
            
        else:
            print answer
    else:
        
        #получение и отправка сообщений
        if chat_mode==1:
            tcp_socket.send(user_name+' com> '+user_input)            
        else:
            #тут должна быть отправка данных на UDP сервер оппонента
            udp_client_socket.sendto(user_name+' priv> '+user_input, opp_udp_host_port)

tcp_socket.close()



Код Сервера:
Код

#Сети ЭВМ. лр №3
#мультиклиентский чат. серверная часть. Автор: deHimer

import socket
from threading import Thread
import re

#глобальные переменные
host = 'localhost'
tcp_serv_port = 13

clients = {}    #[name] -> (chat_mode, (udp_host, udp_port))


#поток обрабатывающий команды/сообщения пользователя
class Connect(Thread):
    def __init__(self, client_socket, client_host_port):
        self.client_tcp_socket = client_socket
        self.client_tcp_addres = client_host_port
        Thread.__init__(self)
        
    def run(self):
    
        #создание UDP клиента
        self.udp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        
        #получение адреса UDP сервера клиента
        self.client_udp_host = self.client_tcp_socket.recv(100)
        self.client_tcp_socket.send('udp host received')
        
        self.client_udp_port = int(self.client_tcp_socket.recv(100))
        self.client_tcp_socket.send('udp port received')       
        
        #получение имени клиента
        have_connection = True
        
        try:
            self.user_name = self.client_tcp_socket.recv(20)        
        except:
            have_connection = False
        
        while have_connection:  
            
            #проверка имени на уникальность и правильность
            if clients and (self.user_name in clients.keys()):
                self.client_tcp_socket.send('Name chosen busy. Choose another name')
                
            else:
                if re.search('\W', self.user_name):
                    self.client_tcp_socket.send("Name invalid")
                else:    
                    self.client_tcp_socket.send("Name valid")
                    #сохраняем данные пользователя
                    clients[self.user_name]=(1, (self.client_udp_host, self.client_udp_port))
                    print ('User %s has connected' % self.user_name)
                    break
                    
            self.user_name = self.client_tcp_socket.recv(20)   
            
        
        #цикл связи с клиентом
        have_connection = True
        while have_connection:
            
            try:
                buffer = self.client_tcp_socket.recv(1024)
            except:
                have_connection = False
                break
                
            if buffer == '!clients_list':
                #составление списка клиентов
                connected_clients_list=''
                for client in clients.keys():
                    connected_clients_list += client + ' '
                  
                self.client_tcp_socket.send(connected_clients_list)
            elif buffer == '!common_chat':
                #переключение в общий чат
                
                client = clients[self.user_name]
                client = (1, client[1])
                clients[self.user_name] = client
                
            elif buffer =='!exit':
            
                have_connection = False
                print ('User %s is disconnected' % self.user_name)
                del clients[self.user_name]                
                break
                
            elif buffer == '!private_chat':
                #создание приватного чата
                
                #получение имени оппонента                
                try:
                    opponent_name = self.client_tcp_socket.recv(100)
                except:
                    have_connection = False
                    break
                                                
                #проверка на существование выбранного оппонента
                if opponent_name in clients.keys():
                    #запрос согласия оппонента на установление контакта
                    opp_mode_host_port = clients[opponent_name]
                    self.udp_client_socket.sendto('!privateChat', opp_mode_host_port[1])
                    self.udp_client_socket.sendto('Allow chat with ' + self.user_name + '. (before answering, press Enter)', opp_mode_host_port[1])

                    opp_answer = self.udp_client_socket.recv(100)

                    
                    if opp_answer == 'yes':
                        
                        #отправка оппоненту адреса UDP сервера клиента
                        print opp_mode_host_port[1]
                        self.udp_client_socket.sendto(str(self.client_udp_host), 0, opp_mode_host_port[1])
                        data, address =  self.udp_client_socket.recvfrom(15)
                        print data
                        
                        self.udp_client_socket.sendto(str(self.client_udp_port), 0, opp_mode_host_port[1])
                        data, address = self.udp_client_socket.recvfrom(15)
                        print data                        
                        
                        #изменение состояний клиента и оппонента
                        
                        #переключение оппонента в режим прив. чата
                        opp_date = clients[opponent_name]
                        new_opp_mode = (2, opp_date[1])
                        clients[opponent_name] = new_opp_mode                        
                        
                        #переключение пользователя
                        client = clients[self.user_name]
                        client = (2, client[1])
                        clients[self.user_name] = client
                                          
                        
                        #пишем клиенту что все гуд
                        self.client_tcp_socket.send('yes')                        
                        
                        #отправляем клиенту адрес UDP сервера оппонента
                        opp_host_port = opp_date[1]                        
                        self.client_tcp_socket.send(str(opp_host_port[0]))
                        self.client_tcp_socket.recv(100)
                        self.client_tcp_socket.send(str(opp_host_port[1]))
                        self.client_tcp_socket.recv(100)
                        
                        
                    else:
                        #иначе пишем что с ним не хотят иметь дел
                        self.client_tcp_socket.send('no')
                else:
                    #если такого нет
                    self.client_tcp_socket.send('Opponent with the same name is missing.')                    
                    
                
            else:
                client_mode_address = clients[self.user_name]
                client_chat_mode = client_mode_address[0]
                if client_chat_mode == 1:
                    for client in clients.values():
                        if (client[0] == 1) and (client[1] != (self.client_udp_host,  self.client_udp_port)):
                            self.udp_client_socket.sendto(buffer, client[1])
                else:
                    self.udp_client_socket.sendto(buffer, companion[1])
                
                        
        self.udp_client_socket.close()
        self.client_tcp_socket.close()
        
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
tcp_server_socket.bind((host, tcp_serv_port))
tcp_server_socket.listen(5)

#ожидание и подключние новых клиентов
while True:
    client_socket, client_host_port = tcp_server_socket.accept()
    Connect(client_socket, client_host_port).start()


Добавлено через 3 минуты и 39 секунд
С самого начала хотел на Tkinter'e сделать интерфейс, но на то что выше ушло времени больше чем ожидалось.
Если есть желающие привинтить интерфейс и улучшить код - только за.
Буду приятно удивлен увидеть тут развитие темы smile 
PM MAIL WWW   Вверх
afiskon
Дата 31.3.2011, 21:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 294
Регистрация: 31.3.2011
Где: Россия, Москва

Репутация: нет
Всего: 4



Есть событие - "клиент прислал данные". При получении сообщений они помещаются в очередь. Специально выделенный поток обрабатывает очередь, рассылая сообщения из нее клиентам. По-моему, все достаточно просто.
PM MAIL WWW   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Python: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.1345 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.