[Python3] 소켓 기반 채팅 앱

Alexandria·2024년 3월 3일

Python3 Advanced

목록 보기
19/27
post-thumbnail

1. Server

ECHO 서버와 큰 차이점은 클라이언트를 지속적으로 받기 위하여 accept가 반복문 안으로 들어가게 됩니다.

이후 클라이언트의 데이터를 송신 및 수신을 위한 스레드를 생성하여 비동기적으로 클라이언트가 채팅을 할 수 있게 합니다.

스레드에서는 데이터를 송신한 클라이언트를 제외한 모든 클라이언트에게 데이터를 전송합니다.

import socket
import threading

HOST    = ""
PORT    = 9999
CLIENTS = []

def work(_client_socket:socket.socket, _remote_addr:tuple):
    user    = f"{_remote_addr[0]}:{_remote_addr[1]}"
    print(f"[{user}] Connected")

    while True:
        try:
            data    = _client_socket.recv(1024)
        except ConnectionError:
            print(f"[{user}] Disconnected")
            CLIENTS.remove(_client_socket)
            break

        if not data:
            break

        data    = f"[{user}] {data.decode()}"
        print(data)
        # 모든 소켓을 순환
        for client in CLIENTS:
            # 데이터를 송신한 클라이언트가 아니라면 데이터를 송신
            if client != _client_socket:
                client.sendall(data.encode())

server_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind((HOST, PORT))
server_socket.listen()

while True:
    try:
        client_socket, remote_addr = server_socket.accept()
    except KeyboardInterrupt:
        break

    # 클라이언트에 할당된 소켓을 리스트에 추가
    CLIENTS.append(client_socket)
    # 클라이언트로부터 데이터 송신 및 수신을 수행할 스레드 생성
    client_thread = threading.Thread(target=work, args=(client_socket, remote_addr))
    client_thread.daemon = True
    client_thread.start()

print("Server stop")
for client in CLIENTS:
    client.close()
server_socket.close()

2. Client

지정된 서버로 접속 후 데이터를 전송합니다.

exit을 입력하기 전까지는 계속 데이터를 송신할 수 있습니다.

데이터 수신은 비동기적으로 이루어지기 때문에 송신과 분리된 스레드를 이용합니다.

import socket
import threading

def work(socket:socket.socket):
    while True:
        try:
            data = socket.recv(1024)
        except ConnectionAbortedError:
            break
        except ConnectionResetError:
            break

        if not data:
            break
        print(data.decode())

HOST            = "127.0.0.1"
PORT            = 9999
client_socket   = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect((HOST, PORT))

thread          = threading.Thread(target=work, args=(client_socket,))
thread.start()

while True:
    data        = input("message > ")
    if data == "exit":
        break
    
    try:
        client_socket.sendall(data.encode())
    except ConnectionResetError:
        print("Server die")
        break

client_socket.close()
thread.join()
profile
IT 도서관

0개의 댓글