Skip to content

Instantly share code, notes, and snippets.

@gregvish
Last active February 9, 2023 12:33
Show Gist options
  • Star 22 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
  • Save gregvish/7665915 to your computer and use it in GitHub Desktop.
Save gregvish/7665915 to your computer and use it in GitHub Desktop.
Python 3.4 asyncio chat server example
from socket import socket, SO_REUSEADDR, SOL_SOCKET
from asyncio import Task, coroutine, get_event_loop
class Peer(object):
def __init__(self, server, sock, name):
self.loop = server.loop
self.name = name
self._sock = sock
self._server = server
Task(self._peer_handler())
def send(self, data):
return self.loop.sock_sendall(self._sock, data.encode('utf8'))
@coroutine
def _peer_handler(self):
try:
yield from self._peer_loop()
except IOError:
pass
finally:
self._server.remove(self)
@coroutine
def _peer_loop(self):
while True:
buf = yield from self.loop.sock_recv(self._sock, 1024)
if buf == b'':
break
self._server.broadcast('%s: %s' % (self.name, buf.decode('utf8')))
class Server(object):
def __init__(self, loop, port):
self.loop = loop
self._serv_sock = socket()
self._serv_sock.setblocking(0)
self._serv_sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
self._serv_sock.bind(('',port))
self._serv_sock.listen(5)
self._peers = []
Task(self._server())
def remove(self, peer):
self._peers.remove(peer)
self.broadcast('Peer %s quit!\n' % (peer.name,))
def broadcast(self, message):
for peer in self._peers:
peer.send(message)
@coroutine
def _server(self):
while True:
peer_sock, peer_name = yield from self.loop.sock_accept(self._serv_sock)
peer_sock.setblocking(0)
peer = Peer(self, peer_sock, peer_name)
self._peers.append(peer)
self.broadcast('Peer %s connected!\n' % (peer.name,))
def main():
loop = get_event_loop()
Server(loop, 1234)
loop.run_forever()
if __name__ == '__main__':
main()
@suchislife801
Copy link

Hi. I've worked on a chat client for your server. It works so far. Just my 2 cents. Trying to figure out how to prompt the client for a username prior to connecting and having the server use this username instead of ip address and port as it is right now.

from socket import *
from threading import Thread

host = 'localhost'
port = 1234
s = socket(AF_INET, SOCK_STREAM)
s.connect((host, port))

def Listener():
try:
while True:
data = s.recv(1024).decode('utf-8')
print('', data)
except ConnectionAbortedError:
pass

t = Thread(target=Listener)
t.start()

try:
while True:
message = input('')
s.send(message.encode('utf-8'))
except EOFError:
pass
finally:
s.close()

@sneils
Copy link

sneils commented Apr 9, 2014

asyncio already brings the whole socket stuff with it, so you can cut that out. Plus using the power of the Protocol class does shorten the code (not perfect, just for demonstration purposes):

import asyncio

clients = []

class SimpleChatClientProtocol(asyncio.Protocol):
    def connection_made(self, transport):
        self.transport = transport
        self.peername = transport.get_extra_info("peername")
        print("connection_made: {}".format(self.peername))
        clients.append(self)

    def data_received(self, data):
        print("data_received: {}".format(data.decode()))
        for client in clients:
            if client is not self:
                client.transport.write("{}: {}".format(self.peername, data.decode()).encode())

    def connection_lost(self, ex):
        print("connection_lost: {}".format(self.peername))
        clients.remove(self)

if __name__ == '__main__':
    print("starting up..")

    loop = asyncio.get_event_loop()
    coro = loop.create_server(SimpleChatClientProtocol, port=1234)
    server = loop.run_until_complete(coro)

    for socket in server.sockets:
        print("serving on {}".format(socket.getsockname()))

    loop.run_forever()

@LukeB42
Copy link

LukeB42 commented Apr 22, 2016

Greg do you mind if this forms the basis of psyrcd-0.21? Project currently lives here: https://github.com/LukeB42/Psyrcd

Cheers

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment