-3

Python ソケットを使用して、複数のクライアントが接続してテキスト入力をエコーできるサーバーを作成したいと考えています。数行のコードでセットアップできる基本的なサーバーはありますか? 私はすでにクライアントを接続する準備ができています.基本的なpythonソケットサーバーがこれらのクライアント接続でどのように見えるかを知る必要があります.

4

2 に答える 2

6

2 人以上のユーザーが必要な場合は、代わりに xmmp/jabber サーバーをインストールする方がよいかどうかを確認することをお勧めします。

パイソンソケット

Pythons ソケットのドキュメントには、簡単なチャット機能を示す簡単な例もいくつかあります。http://docs.python.org/2/library/socket.html#exampleを参照してください。

これは、トリックを実行する必要がある小さなスニペットです。良い出力は得られませんが、うまくいくはずです。2 つのスレッドを使用して、2 つの異なるスクリプトの起動を回避します。

# Echo server program
import socket
import thread

TARGET = None
DEFAULT_PORT = 45000

def reciever():
    """ Recive messages... """

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
    s.bind(('', DEFAULT_PORT)) # Listens on all interfaces... 
    s.listen(True) # Listen on the newly created socket... 
    conn, addr = s.accept()
    while True:
        data = conn.recv(1024)
        print "\nMessage> %s\n" % data


def sender():
    """ The 'client' which sends the messages """

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((TARGET, DEFAULT_PORT)) # Connect... 

    while True: 
        msg = raw_input("\nMe> ")
        s.sendall(msg)
    s.close()

while not TARGET:
    TARGET = raw_input("Please specify the other client to connect to: ")

thread.start_new_thread(reciever, ())
thread.start_new_thread(sender, ())

while True:
   pass

XMLRPC

2 人以上のユーザーが必要な場合は、Python の XMLRPC 機能を調べることもできます。例えば...

サーバ:

このミニサーバーにより、ユーザーはユーザーにメッセージを送信できます。次に、サーバーはそれらを小さな json ファイルに永続化します。その後、クライアントは特定のユーザーに新しいメッセージを要求できます。

import json
import os.path

from SimpleXMLRPCServer import SimpleXMLRPCServer

""" 
Saves messages as structure: 

{
    'client1':['message1', 'message2'], 
    'client2':['message1', 'message2']
}
"""

STORAGE_FILE = 'messages.json'

def save(messages):
    msg_file = open(STORAGE_FILE, 'w+')
    json.dump(messages, msg_file)

def get():
    if os.path.exists(STORAGE_FILE):
        msg_file = open(STORAGE_FILE, 'r')
        return json.load(msg_file)
    else: 
        return {}

def deliver(client, message):
    """ Deliver the message to the server and persist it in a JSON file..."""

    print "Delivering message to %s" % client

    messages = get()
    if client not in messages:
        messages[client] = [message]
    else: 
        messages[client].append(message)
    save(messages)

    return True

def get_messages(client):
    """ Get the undelivered messags for the given client. Remove them from
    messages queue. """
    messages = get()
    if client in messages:
        user_messages = messages[client]
        messages[client] = []
        save(messages)
        return user_messages
    else: 
        return []

server = SimpleXMLRPCServer(("localhost", 8000))
print "Listening on port 8000..."
server.register_function(deliver, 'deliver')
server.register_function(get_messages, 'get_messages')
server.serve_forever()

"クライアント"

メッセージを送信し、ユーザーのメッセージを取得する使用例。

import xmlrpclib

# Connect to the 'Server'...
proxy = xmlrpclib.ServerProxy("http://localhost:8000/")

# Send a message...
proxy.deliver('username', 'The message to send')

# Recieve all messages for user..
for msg in proxy.get_messages('username'):
    print "Message> %s" % msg

注意:これらは簡単な例です。送信者/受信者の検証が行われないため、どちらも実際には安全ではありません. また、トランスポート セキュリティがないため、メッセージが失われる可能性があります。

多くのユーザーにとっては、単純な JSON ファイルの代わりにDBMSシステムを使用する方がよいでしょう。

Nick ODell が言ったように、単純なシェル スクリプトが必要な場合は、netcat を使用することもお勧めします。

于 2012-12-08T09:39:16.000 に答える
0

あなたが求めていたものはまったくありませんが、次のプログラムとファイルは、GUI クライアントといくつかのサーバーを示しています。ここに示すコードを実行するには、Python 3.x が必要です。


MultichatClient.py

#! /usr/bin/env python3
from safetkinter import *
from tkinter.constants import *
import socket
import sys

class MultichatClient(Frame):

    after_handle = None

    def __init__(self, master, remote_host):
        super().__init__(master)
        self.message_area = ScrolledText(self, width=81, height=21,
                                         wrap=WORD, state=DISABLED)
        self.message_area.grid(sticky=NSEW, columnspan=2)
        self.send_area = Entry(self)
        self.send_area.bind('<Return>', self.keyPressed)
        self.send_area.grid(sticky=EW)
        b = Button(self, text='Send', command=self.mouseClicked)
        b.grid(row=1, column=1)
        self.send_area.focus_set()
        try:
            self.remote = socket.create_connection((remote_host, 8989))
        except socket.gaierror:
            print('Could not find host {}.'.format(remote_host))
        except socket.error:
            print('Could not connect to host {}.'.format(remote_host))
        else:
            self.remote.setblocking(False)
            self.after_handle = self.after_idle(self.dataready)
        self.grid_rowconfigure(0, weight=1)
        self.grid_columnconfigure(0, weight=1)

    @classmethod
    def main(cls, args):
        root = Tk()
        root.title('MultichatClient version 1.0')
        m = cls(root, args[0])
        m.grid(sticky=NSEW)
        root.grid_rowconfigure(0, weight=1)
        root.grid_columnconfigure(0, weight=1)
        root.mainloop()
        return 1

    def dataready(self):
        try:
            s = self.remote.recv(1 << 12).decode()
        except socket.error:
            pass
        else:
            self.message_area['state'] = NORMAL
            self.message_area.insert(END, s)
            self.message_area['state'] = DISABLED
            self.message_area.see(END)
        self.after_handle = self.after(100, self.dataready)

    def destroy(self):
        if self.after_handle:
            self.after_cancel(self.after_handle)
        super().destroy()

    def mouseClicked(self, e=None):
        self.remote.sendall(self.send_area.get().encode() + b'\r\n')
        self.send_area.delete(0, END)

    keyPressed = mouseClicked

if __name__ == '__main__':
    sys.exit(MultichatClient.main(sys.argv[1:]))

Simple_Client.pyw

#! /usr/bin/env python3

"""Provide a GUI for easy interactions with Multichat servers.

This program is an example of a first attempt at implementing a client
for interacting with a Multichat server through purely graphical means."""

__author__ = 'Stephen "Zero" Chappell <Noctis.Skytower@gmail.com>'
__date__ = '11 October 2012'
__version__ = 1, 0, 0

################################################################################

from tkinter.messagebox import *
from tkinter.constants import *
from safetkinter import *
import logging
import traceback
import _thread
import socket
import os
import traceback
import sys
import threadbox

################################################################################

class SimpleClient(Frame):

    "SimpleClient(master, **kw) -> SimpleClient instance"

    @classmethod
    def main(cls):
        "Create a GUI root and demonstrate the SimpleClient widget."
        root = Tk()
        root.title('Chat Client')
        root.minsize(675, 450)
        root.grid_rowconfigure(0, weight=1)
        root.grid_columnconfigure(0, weight=1)
        root.bind_all('<Control-Key-a>', cls.handle_control_a)
        frame = cls(root)
        frame.grid(sticky=NSEW)
        root.mainloop()

    @staticmethod
    def handle_control_a(event):
        "Process Ctrl-A commands by widget type."
        widget = event.widget
        if isinstance(widget, Text):
            widget.tag_add(SEL, 1.0, END + '-1c')
            return 'break'
        if isinstance(widget, Entry):
            widget.selection_range(0, END)
            return 'break'

    def __init__(self, master, **kw):
        "Initialize the SimpleClient instance with the widgets it contains."
        super().__init__(master, **kw)
        self.grid_rowconfigure(0, weight=1)
        self.grid_columnconfigure(0, weight=1)
        # Build Widgets
        self.output_area = ScrolledText(self, width=25, height=4, wrap=WORD)
        self.input_area = Entry(self)
        self.corner = Sizegrip(self)
        # Place Widgets
        self.output_area.grid(row=0, column=0, columnspan=2, sticky=NSEW)
        self.input_area.grid(row=1, column=0, sticky=EW)
        self.corner.grid(row=1, column=1, sticky=SE)
        # Setup Widgets
        self.output_area['state'] = DISABLED
        self.input_area.bind('<Return>', self.send)
        self.after_idle(self.connect)

    def connect(self):
        "Try connecting to a server to begin chatting."
        self.connection = Connector(self, 'Chat Client').connection
        if self.connection is None:
            self._root().destroy()
        else:
            self.connection.setblocking(False)
            self.after_idle(self.update)

    def send(self, event):
        "Send a message across the connection from the given widget."
        self.connection.sendall(event.widget.get().encode() + b'\r\n')
        event.widget.delete(0, END)

    def update(self):
        "Update the output area with any incoming messages."
        self.output_area['state'] = NORMAL
        try:
            self.output_area.insert(END, self.connection.recv(1 << 12).decode())
        except socket.error:
            pass
        else:
            self.output_area.see(END)
        finally:
            self.output_area['state'] = DISABLED
        self.after(100, self.update)

################################################################################

def start_thread(function, *args, **kwargs):
    "Start a new thread of execution while logging any errors."
    _thread.start_new_thread(log_errors, (function, args, kwargs))

def log_errors(function, args=(), kwargs={}):
    "Execute a function with its arguments and log any exceptions."
    try:
        function(*args, **kwargs)
    except SystemExit:
        pass
    except:
        basename = os.path.basename(sys.argv[0])
        filename = os.path.splitext(basename)[0] + '.log'
        logging.basicConfig(filename=filename)
        logging.error(traceback.format_exc())

################################################################################

class Dialog(Toplevel): # Copies tkinter.simpledialog.Dialog

    "Dialog(parent, title=None) -> Dialog instance"

    def __init__(self, parent, title=None):
        "Initialize a Dialog window that takes focus away from the parent."
        super().__init__(parent)
        self.withdraw()
        if parent.winfo_viewable():
            self.transient(parent)
        if title:
            self.title(title)
        self.parent = parent
        self.result = None
        body = Frame(self)
        self.initial_focus = self.body(body)
        body.grid(sticky=NSEW, padx=5, pady=5)
        self.buttonbox()
        if not self.initial_focus:
            self.initial_focus = self
        self.protocol('WM_DELETE_WINDOW', self.cancel)
        if self.parent is not None:
            self.geometry('+{}+{}'.format(parent.winfo_rootx() + 50,
                                          parent.winfo_rooty() + 50))
        self.deiconify()
        self.initial_focus.focus_set()
        try:
            self.wait_visibility()
        except tkinter.TclError:
            pass
        else:
            self.grab_set()
            self.wait_window(self)

    def destroy(self):
        "Destruct the Dialog window."
        self.initial_focus = None
        super().destroy()

    def body(self, master):
        "Create the body of this Dialog window."
        pass

    def buttonbox(self):
        "Create the standard buttons and Dialog bindings."
        box = Frame(self)
        w = Button(box, text='OK', width=10, command=self.ok, default=ACTIVE)
        w.grid(row=0, column=0, padx=5, pady=5)
        w = Button(box, text='Cancel', width=10, command=self.cancel)
        w.grid(row=0, column=1, padx=5, pady=5)
        self.bind('<Return>', self.ok)
        self.bind('<Escape>', self.cancel)
        box.grid()

    def ok(self, event=None):
        "Validate and apply the changes made by this Dialog."
        if not self.validate():
            self.initial_focus.focus_set()
            return
        self.withdraw()
        self.update_idletasks()
        try:
            self.apply()
        finally:
            self.cancel()

    def cancel(self, event=None):
        "Close the Dialong window and return to its parent."
        if self.parent is not None:
            self.parent.focus_set()
        self.destroy()

    def validate(self):
        "Verify that the Dialog is in a valid state."
        return True

    def apply(self):
        "Make any changes the Dialog wishes to accomplish."
        pass

################################################################################

class Connector(Dialog):

    "Connector(parent, title=None) -> Connector instance"

    def body(self, master):
        "Customize the Dialog window with some custom widgets."
        self.connection = None
        self.resizable(False, False)
        # Build Widgets
        self.prompt = Label(master, text='Enter server IP address:')
        self.address = Entry(master)
        # Place Widgets
        self.prompt.grid(sticky=W, padx=30, pady=2)
        self.address.grid(sticky=W, padx=30)

    def buttonbox(self):
        "Redefine the buttons at the bottom of the window."
        w = Button(self, text='Connect', width=10, command=self.ok,
                   default=ACTIVE)
        w.grid(sticky=E, padx=5, pady=5)
        self.bind('<Return>', self.ok)
        self.bind('<Escape>', self.cancel)

    def validate(self):
        "Ask a Consumator to make a connection with the given address."
        c = Consumator(self, 'Chat Client', (self.address.get(), 8989))
        if c.connection is None:
            Message(self, icon=WARNING, type=OK, title='Warning',
                    message='Could not connect to address!').show()
            return False
        self.connection = c.connection
        return True

################################################################################

class Consumator(Dialog):

    "Consumator(parent, title, address) -> Consumator instance"

    def __init__(self, parent, title, address):
        "Initialize the Consumator with the server's address."
        self.server_address = address
        super().__init__(parent, title)

    def body(self, master):
        "Create the widgets for this Dialog and start the connection process."
        self.connection = None
        self.resizable(False, False)
        # Build Widgets
        self.message = Label(master, text='Trying to connect to address ...')
        self.progress = Progressbar(master, orient=HORIZONTAL)
        # Place Widgets
        self.message.grid(sticky=W, padx=10, pady=2)
        self.progress.grid(sticky=EW, padx=10, pady=2)
        # Setup Widgets
        self.progress.configure(mode='indeterminate', maximum=30)
        self.progress.start()
        result = []
        start_thread(self.connect, result)
        self.after_idle(self.poll, result)

    def buttonbox(self):
        "Cancel the creation of the buttons at the bottom of this Dialog."
        pass

    @threadbox.MetaBox.thread
    def connect(self, result):
        "Try connecting to the server address that was given."
        try:
            result.append(socket.create_connection(self.server_address, 10))
        except socket.timeout:
            result.append(None)

    def poll(self, result):
        "Find out if the any connection information is available yet."
        if result:
            self.connection = result[0]
            self.cancel()
        else:
            self.after(100, self.poll, result)

################################################################################

if __name__ == '__main__':
    log_errors(SimpleClient.main)

アフィニティ.py

"""Allow a simple way to ensure execution is confined to one thread.

This module defines the Affinity data type that runs code on a single thread.
An instance of the class will execute functions only on the thread that made
the object in the first place. The class is useful in a GUI's main loop."""

__author__ = 'Stephen "Zero" Chappell <Noctis.Skytower@gmail.com>'
__date__ = '4 June 2012'
__version__ = 1, 0, 0

################################################################################

import sys
import _thread
import queue

################################################################################

def slots(names=''):
    "Sets the __slots__ variable in the calling context with private names."
    sys._getframe(1).f_locals['__slots__'] = \
        tuple('__' + name for name in names.replace(',', ' ').split())

################################################################################

class Affinity:

    "Affinity() -> Affinity instance"

    slots('thread, action')

    def __init__(self):
        "Initializes instance with thread identity and job queue."
        self.__thread = _thread.get_ident()
        self.__action = queue.Queue()

    def __call__(self, func, *args, **kwargs):
        "Executes function on creating thread and returns result."
        if _thread.get_ident() == self.__thread:
            while not self.__action.empty():
                self.__action.get_nowait()()
            return func(*args, **kwargs)
        delegate = _Delegate(func, args, kwargs)
        self.__action.put_nowait(delegate)
        return delegate.value

################################################################################

class _Delegate:

    "_Delegate(func, args, kwargs) -> _Delegate instance"

    slots('func, args, kwargs, mutex, value, error')

    def __init__(self, func, args, kwargs):
        "Initializes instance from arguments and prepares to run."
        self.__func = func
        self.__args = args
        self.__kwargs = kwargs
        self.__mutex = _thread.allocate_lock()
        self.__mutex.acquire()

    def __call__(self):
        "Executes code with arguments and allows value retrieval."
        try:
            self.__value = self.__func(*self.__args, **self.__kwargs)
            self.__error = False
        except:
            self.__value = sys.exc_info()[1]
            self.__error = True
        self.__mutex.release()

    @property
    def value(self):
        "Waits for value availability and raises or returns data."
        self.__mutex.acquire()
        if self.__error:
            raise self.__value
        return self.__value

スレッドボックス.py

"""Provide a way to run instance methods on a single thread.

This module allows hierarchical classes to be cloned so that their instances
run on one thread. Method calls are automatically routed through a special
execution engine. This is helpful when building thread-safe GUI code."""

__author__ = 'Stephen "Zero" Chappell <Noctis.Skytower@gmail.com>'
__date__ = '9 October 2012'
__version__ = 1, 0, 1

################################################################################

import functools
import affinity

################################################################################

class _object: __slots__ = '_MetaBox__exec', '__dict__'

################################################################################

class MetaBox(type):

    "MetaBox(name, bases, classdict, old=None) -> MetaBox instance"

    __REGISTRY = {object: _object}
    __SENTINEL = object()

    @classmethod
    def clone(cls, old, update=()):
        "Creates a class preferring thread affinity after update."
        classdict = dict(old.__dict__)
        classdict.update(update)
        return cls(old.__name__, old.__bases__, classdict, old)

    @classmethod
    def thread(cls, func):
        "Marks a function to be completely threaded when running."
        func.__thread = cls.__SENTINEL
        return func

    def __new__(cls, name, bases, classdict, old=None):
        "Allocates space for a new class after altering its data."
        assert '__new__' not in classdict, '__new__ must not be defined!'
        assert '__slots__' not in classdict, '__slots__ must not be defined!'
        assert '__module__' in classdict, '__module__ must be defined!'
        valid = []
        for base in bases:
            if base in cls.__REGISTRY:
                valid.append(cls.__REGISTRY[base])
            elif base in cls.__REGISTRY.values():
                valid.append(base)
            else:
                valid.append(cls.clone(base))
        for key, value in classdict.items():
            if callable(value) and (not hasattr(value, '_MetaBox__thread') or
                                    value.__thread is not cls.__SENTINEL):
                classdict[key] = cls.__wrap(value)
        classdict.update({'__new__': cls.__new, '__slots__': (), '__module__':
                          '{}.{}'.format(__name__, classdict['__module__'])})
        cls.__REGISTRY[object() if old is None else old] = new = \
            super().__new__(cls, name, tuple(valid), classdict)
        return new

    def __init__(self, name, bases, classdict, old=None):
        "Initializes class instance while ignoring the old class."
        return super().__init__(name, bases, classdict)

    @staticmethod
    def __wrap(func):
        "Wraps a method so execution runs via an affinity engine."
        @functools.wraps(func)
        def box(self, *args, **kwargs):
            return self.__exec(func, self, *args, **kwargs)
        return box

    @classmethod
    def __new(meta, cls, *args, **kwargs):
        "Allocates space for instance and finds __exec attribute."
        self = object.__new__(cls)
        if 'master' in kwargs:
            self.__exec = kwargs['master'].__exec
        else:
            valid = tuple(meta.__REGISTRY.values())
            for value in args:
                if isinstance(value, valid):
                    self.__exec = value.__exec
                    break
            else:
                self.__exec = affinity.Affinity()
        return self

safetkinter.py

"""Register tkinter classes with threadbox for immediate usage.

This module clones several classes from the tkinter library for use with
threads. Instances from these new classes should run on whatever thread
the root was created on. Child classes inherit the parent's safety."""

__author__ = 'Stephen "Zero" Chappell <Noctis.Skytower@gmail.com>'
__date__ = '4 June 2012'
__version__ = 1, 0, 0

################################################################################

import time
import tkinter.filedialog
import tkinter.font
import tkinter.messagebox
import tkinter.scrolledtext
import tkinter.ttk
import threadbox

################################################################################

tkinter.NoDefaultRoot()

@threadbox.MetaBox.thread
def mainloop(self):
    "Creates a synthetic main loop so that threads can still run."
    while True:
        try:
            self.update()
        except tkinter.TclError:
            break
        else:
            time.sleep(tkinter._tkinter.getbusywaitinterval() / 1000)

threadbox.MetaBox.clone(tkinter.Misc, {'mainloop': mainloop})

################################################################################

OldButton = threadbox.MetaBox.clone(tkinter.Button)
Canvas = threadbox.MetaBox.clone(tkinter.Canvas)
OldFrame = threadbox.MetaBox.clone(tkinter.Frame)
Menu = threadbox.MetaBox.clone(tkinter.Menu)
PhotoImage = threadbox.MetaBox.clone(tkinter.PhotoImage)
Spinbox = threadbox.MetaBox.clone(tkinter.Spinbox)
StringVar = threadbox.MetaBox.clone(tkinter.StringVar)
Text = threadbox.MetaBox.clone(tkinter.Text)
Tk = threadbox.MetaBox.clone(tkinter.Tk)
Toplevel = threadbox.MetaBox.clone(tkinter.Toplevel)

################################################################################

Button = threadbox.MetaBox.clone(tkinter.ttk.Button)
Checkbutton = threadbox.MetaBox.clone(tkinter.ttk.Checkbutton)
Entry = threadbox.MetaBox.clone(tkinter.ttk.Entry)
Frame = threadbox.MetaBox.clone(tkinter.ttk.Frame)
Label = threadbox.MetaBox.clone(tkinter.ttk.Label)
Labelframe = threadbox.MetaBox.clone(tkinter.ttk.Labelframe)
Progressbar = threadbox.MetaBox.clone(tkinter.ttk.Progressbar)
Radiobutton = threadbox.MetaBox.clone(tkinter.ttk.Radiobutton)
Scale = threadbox.MetaBox.clone(tkinter.ttk.Scale)
Scrollbar = threadbox.MetaBox.clone(tkinter.ttk.Scrollbar)
Sizegrip = threadbox.MetaBox.clone(tkinter.ttk.Sizegrip)
Treeview = threadbox.MetaBox.clone(tkinter.ttk.Treeview)

################################################################################

Directory = threadbox.MetaBox.clone(tkinter.filedialog.Directory)
Font = threadbox.MetaBox.clone(tkinter.font.Font)
Message = threadbox.MetaBox.clone(tkinter.messagebox.Message)
ScrolledText = threadbox.MetaBox.clone(tkinter.scrolledtext.ScrolledText)

Simple_Server.py

#! /usr/bin/env python3
import socket, select

def main():
    a = [socket.socket(socket.AF_INET, socket.SOCK_STREAM)]     # socket array
    a[0].bind(('', 8989))
    a[0].listen(5)
    while True:
        for b in select.select(a, [], [])[0]:                   # ready socket
            if b is a[0]:
                a.append(b.accept()[0])
            else:
                try:
                    c = b.recv(1 << 12)                         # sent message
                except socket.error:
                    b.shutdown(socket.SHUT_RDWR)
                    b.close()
                    a.remove(b)
                else:
                    for d in (d for d in a[1:] if d is not b):  # message sink
                        d.sendall(c)

if __name__ == '__main__':
    main()
于 2012-12-09T01:18:20.063 に答える