Last active
February 9, 2023 12:33
-
-
Save gregvish/7665915 to your computer and use it in GitHub Desktop.
Python 3.4 asyncio chat server example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
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()
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
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()