입문할 때 간단한 채팅 서버 다들 한번씩 짜셨죠?


아마도 이후로는 쓸 일 없는 select 이용해서 짜셨죠?


이제는 입문용 채팅 서버도 select를 버릴 때가 왔습니다.


대세는 asyncio 입니다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import socket
import asyncio
 
HOST = ''
PORT = 9999
ADDR = (HOST, PORT)
 
class Server:
    def __init__(self, loop, addr):
        self.loop = loop
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.setblocking(False)
        self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
        self.sock.bind(addr)
        self.sock.listen(5)
        self.clients = []
        asyncio.Task(self.serve())
 
    def broadcast(self, msg):
        [client.send(msg) for client in self.clients]
 
    def remove(self, client):
        self.clients.remove(client)
        msg = '########## GUEST {} OUT ##########\n'.format(client.name)
        self.broadcast(msg)
 
    async def serve(self):
        while True:
            conn, addr = await self.loop.sock_accept(self.sock)
            conn.setblocking(False)
            name = '[' + addr[0+ ']'
            client = Client(self, conn, name)
            self.clients.append(client)
            msg = '########## GUEST {} IN ##########\n'.format(client.name)
            self.broadcast(msg)
 
class Client:
    def __init__(self, server, sock, name):
        self.server = server
        self.loop = server.loop
        self.name = name
        self.sock = sock
        asyncio.Task(self.handler())
 
    def send(self, msg):
        return self.loop.sock_sendall(self.sock, msg.encode())
 
    async def handler(self):
        try:
            await self.converse()
        except IOError:
            pass
        finally:
            self.server.remove(self)
 
    async def converse(self):
        while True:
            buf = await self.loop.sock_recv(self.sock, 1024)
            if buf == b'':
                break
            msg = '{} : {}'.format(self.name, buf.decode())
            self.server.broadcast(msg)
 
def runServer(addr):
    loop = asyncio.get_event_loop()
    Server(loop, addr)
    loop.run_forever()
 
if __name__ == '__main__':
    runServer(ADDR)
cs