Как сделать простой чат из командной строки на Python? - PullRequest
11 голосов
/ 20 июня 2009

Я изучаю сетевое программирование и хотел бы написать простой чат для командной строки на Python.

Мне интересно, как сделать постоянный прием и ввод доступным для отправки в любое время.

Как видите, этот клиент может выполнять только одну работу за раз:

from socket import *

HOST = 'localhost'
PORT = 21567
BUFSIZE = 1024
ADDR = (HOST, PORT)

tcpCliSock = socket(AF_INET, SOCK_STREAM)
tcpCliSock.connect(ADDR)

while 1:
    data = raw_input('> ')
    if not data: break
    tcpCliSock.send(data)
    data = tcpCliSock.recv(BUFSIZE)
    if not data: break
    print data

tcpCliSock.close()

Так что, если другой клиент отправит сообщение, этот клиент получит его только после отправки сообщения. Спорим, ты меня понимаешь. Я гуглил по этому вопросу и обнаружил много интересных вещей, таких как асинхронный ввод-вывод, многопоточность, неблокирующая синхронизация, параллельное программирование и так далее. Я также установил скрученный пакет. Короче говоря, я изучал все эти вещи, но пока не нашел того, что искал. (Конечно, я буду продолжать пытаться и пытаться, пока не доберусь до сути.)

Итак, мой вопрос: как это сделать? =)

Ответы [ 6 ]

8 голосов
/ 21 июня 2009

Ваш вопрос был не очень связным. Однако ваша программа вовсе не должна быть асинхронной, чтобы достичь того, о чем вы просите.

Это рабочий скрипт чата, который вы изначально хотели с минимальными изменениями. Он использует 1 поток для получения и 1 для отправки, оба с использованием блокирующих сокетов. Это гораздо проще, чем использовать асинхронные методы.

from socket import *
from threading import Thread
import sys

HOST = 'localhost'
PORT = 21567
BUFSIZE = 1024
ADDR = (HOST, PORT)

tcpCliSock = socket(AF_INET, SOCK_STREAM)
tcpCliSock.connect(ADDR)

def recv():
    while True:
        data = tcpCliSock.recv(BUFSIZE)
        if not data: sys.exit(0)
        print data

Thread(target=recv).start()
while True:
    data = raw_input('> ')
    if not data: break
    tcpCliSock.send(data)

tcpCliSock.close()
5 голосов
/ 20 июня 2009

Если вы хотите закодировать его с нуля select - это путь (и вы можете прочитать в Поиске книг Google большую часть главы Python в двух словах, которая охватывает такие вопросы); если вы хотите использовать больше абстракций, можно использовать asyncore, но Twisted гораздо богаче и мощнее.

2 голосов
/ 20 июня 2009

Ну, хорошо, вот что я имею в данный момент.

Сервер работает так:

import asyncore
import socket

clients = {}

class MainServerSocket(asyncore.dispatcher):
    def __init__(self, port):
        asyncore.dispatcher.__init__(self)
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.bind(('',port))
        self.listen(5)
    def handle_accept(self):
        newSocket, address = self.accept( )
        clients[address] = newSocket
        print "Connected from", address
        SecondaryServerSocket(newSocket)

class SecondaryServerSocket(asyncore.dispatcher_with_send):
    def handle_read(self):
        receivedData = self.recv(8192)
        if receivedData:
            every = clients.values()
            for one in every:
                one.send(receivedData+'\n')
        else: self.close( )
    def handle_close(self):
        print "Disconnected from", self.getpeername( )
        one = self.getpeername( )
        del clients[one]

MainServerSocket(21567)
asyncore.loop( )

И клиент работает так:

from Tkinter import *
from socket import *
import thread

HOST = 'localhost'
PORT = 21567
BUFSIZE = 1024
ADDR = (HOST, PORT)

tcpCliSock = socket(AF_INET, SOCK_STREAM)
tcpCliSock.connect(ADDR)

class Application(Frame):
    def __init__(self, master):
        Frame.__init__(self, master)
        self.grid()
        self.create_widgets()
        self.socket()

    def callback(self, event):
        message = self.entry_field.get()
        tcpCliSock.send(message)

    def create_widgets(self):
        self.messaging_field = Text(self, width = 110, height = 20, wrap = WORD)
        self.messaging_field.grid(row = 0, column = 0, columnspan = 2, sticky = W)

        self.entry_field = Entry(self, width = 92)
        self.entry_field.grid(row = 1, column = 0, sticky = W)
        self.entry_field.bind('<Return>', self.callback)

    def add(self, data):
        self.messaging_field.insert(END, data)

    def socket(self):
        def loop0():
            while 1:
                data = tcpCliSock.recv(BUFSIZE)
                if data: self.add(data)

        thread.start_new_thread(loop0, ())



root = Tk()
root.title("Chat client")
root.geometry("550x260")

app = Application(root)

root.mainloop()

Теперь пришло время сделать код лучше и добавить некоторые функции.

Спасибо за вашу помощь, ребята!

2 голосов
/ 20 июня 2009

Программы чата делают две вещи одновременно.

  1. Просмотр клавиатуры локального пользователя и отправка удаленному пользователю (через какой-либо разъем)

  2. Просмотр удаленного сокета и отображение того, что они печатают на локальной консоли.

У вас есть несколько способов сделать это.

  1. Программа, которая открывает сокет и клавиатуру и использует модуль select , чтобы увидеть, какой из них готов к вводу.

  2. Программа, которая создает два потока. Один поток читает удаленный сокет и печатает. Другой поток читает клавиатуру и отправляет на удаленный разъем.

  3. Программа, которая разветвляется на два подпроцесса. Один подпроцесс читает удаленный сокет и печатает. Другой подпроцесс читает клавиатуру и отправляет в удаленный сокет.

1 голос
/ 20 июня 2009

Я написал один в асинхронном вводе / выводе ... намного проще обернуть голову, чем в полной поточной модели.

если вы сможете взять в свои руки исходный код "talk", вы сможете много узнать об этом. посмотрите демоверсию http://dsl.org/cookbook/cookbook_40.html#SEC559 или попробуйте сами, если вы работаете в Linux-системе ...

отправляет символы в режиме реального времени.

также, ytalk является интерактивным и многопользовательским .... вроде как hudddlechat или campfire.

1 голос
/ 20 июня 2009
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...