python 기반 채팅프로그램 - 1

김두현·2023년 7월 3일
2

python_chat

목록 보기
2/4

이번 post에는 소켓이 뭔지 간략하게 설명하고 파이썬에서의 소켓 모듈을 확인한 후 마지막으로 thread 기법을 적용하지 않은 서버와 클라이언트 1:1 채팅 프로그램을 간략하게 구현한 내용을 작성해보겠습니당ㅎㅎ…

Socket

내가 대학교에서 네트워크 전공 강의를 들을 때 배운 소켓의 정의는 아래이다.

서로 떨어진 두 대의 컴퓨터 사이에서 TCP/IP 네트워크를 통해 상호 통신이 가능하도록 운영체제에서 해당 자원을 할당하고, 처리해 주는 방식

나는 위 소켓 정의에서 중요한 키워드는 TCP/IP 와 상호 통신 이라고 생각한다. 그럼 각각의 키워드가 소켓의 정의에서 무엇을 의미하는지 간단하게 살펴보자.

TCP/IP

TCP/IP는 인터넷 프로토콜 스위트(Internet Protocol Suite)로 OSI 7 Layer 중에서 Layer3, 4를 다루는 프로토콜로 패킷 통신 방식의 인터넷 프로토콜인 IP(Internet Protocol)와 전송 조절 프로토콜인 TCP(Transmisson Control Protocol)를 합쳐서 부르는 용어다.

즉 네트워크 상 안전하고 효율적인 데이터 전송을 위한 필수 요건들을 정의하고 있다. 아래는 TCP/IP의 몇 가지 특징을 설명한 것이다.

  • IP는 패킷 전달 여부를 보장하지 않고, 패킷을 보낸 순서와 받는 순서가 다를 수 있다.
  • TCP는 IP 위에서 동작하는 protocol로, 데이터의 전달을 보증하고 보낸 순서대로 받게 해준다.

→ 송신자가 수신자에게 IP 주소를 사용해 데이터를 전달하고 그 데이터가 제대로 전송됐는지?, 너무 빠르거나 느리지는 않는지? 등 데이터 전송을 보증해주는 것을 의미

상호 통신

소켓은 HTTP 통신과 달리 두 프로그램이 서로 실시간으로 데이터를 송수신 가능하게 생성되는 통신 창구라고 생각할 수 있다. 클라이언트에서 서버로만 데이터를 요청하는 것이 아닌 서버도 언제든지 클라이언트 측으로 데이터를 요청 가능한 양방향 통신이라고 할 수 있다.

하지만, 소켓 통신은 HTTP 통신과 달리 서버와 클라이언트가 실시간으로 계속 연결이 되고 있어야 하므로 실시간 채팅, 스트리밍 서비스에 이용되며 비교적 많은 네트워크 비용, 리소스가 발생된다.

맨 위의 그림을 보면 응용 프로그램 Layer와 트랜스포트 Layer 사이에 소켓 층이 존재하는데 이는 응용 프로그램에서 데이터를 송수신 하기 위해서는 트랜스포트 Layer에서 소켓을 거쳐야 가능하다고 볼 수 있다. 소켓은 IP와 Port 번호로 정의되며 같은 IP를 사용하는 응용 프로그램이라도 port 번호로 구분된 통신 창구를 가질 수 있다.

→ 소켓은 OS에서 제공하는 응용 Layer와 트랜스포트 Layer 사이의 인터페이스 역할

Socket 모듈

위에서 설명한 것처럼 소켓은 연결을 요청하는 클라이언트와 연결을 수락하는 서버로 나뉘어지는데 서버와 클라이언트는 서로 조금씩 다르게 구현 과정이 필요하다.

아까 위에서 소켓을 OS에서 제공하는 인터페이스라고 설명한 듯이 소켓 통신을 구현하는 것은 언어에 크게 상관없이 구현 방식은 비슷하다고 볼 수 있다. 아래의 그림을 보면 이해하기 쉽다.

💡 위는 TCP socket을 사용할 때 과정인데 UDP는 이번 채팅 프로그램 구현에 있어 고려 사항이 아니므로 패스~~

Client

서버에 데이터를 주고 받기 위해 소켓 연결 요청을 하는 측으로 host의 IP와 port 번호로 서버에 연결을 요청한다.

  1. socket(): 서버와 연결할 소켓 객체를 생성한다.
  2. connet(): IP 주소와 port 번호로 서버와 연결을 요청해 수락을 기다린다.
    (사전에 host의 IP address와 port number를 알고 있어야 함.)
  3. recv(), send(): 서버와 소켓 연결이 완료되면 데이터를 주고 받을 수 있다.
  4. close(): 서버와 통신이 끝나면 os 측으로 소켓 자원을 반환한다.

Server

클라이언트 측으로부터 소켓 연결 요청을 대기하는 측으로 연결 요청 시 수락하여 요청된 각 클라이언트 별로 소켓을 생성해 통신을 가능하게 한다.

채팅 프로그램에서 server의 역할은 binding 할 서버의 ip와 port를 지정해 클라이언트가 서버에 접속할 수 있도록 하는 것이 중요하다.

  1. socket(): 서버 소켓 객체를 생성한다.
  2. bind(): host IP 주소와 port 번호로 서버 소켓을 binding 한다.
  3. listen() 클라이언트의 소켓 연결 요청을 기다려 요청이 들어오면 연결이 완료되기까지 대기 상태로 전환한다.
  4. accept() 연결을 요청한 클라이언트를 수락해 각 클라이언트 별로 소켓을 생성한다.
  5. … 클라이언트 측과 유사
💡 1번에서 생성된 서버 소켓은 새로운 client의 요청을 기다리고 수락해 새로운 소켓을 생성하기 위해 사용된다.

Code

아래의 글부터는 파이썬으로 구현한 아주 간단한 서버와 소켓 1:1 방식의 채팅 프로그램을 구현한 코드이다.

단순히 while 문을 통해 클라이언트 프로그램에서 메세지를 계속 보낼 수 있고 서버는 그 메세지 내용 그대로 다시 클라이언트로 전송해 채팅이 정상적으로 이뤄지는지 확인할 수 있도록 구현했다.

Client

import socket

server_host = 'localhost'
server_port = 55555

client_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

#서버와 연결
client_sock.connect((server_host, server_port))
print(f'Server: {server_host}, {server_port}와 정상적으로 연결')

while True:
    message = input(">>> ")
    #send vs sendaall: sendall이 버퍼에 있는 데이터를 다 보냈음을 보장함!!
    client_sock.sendall(message.encode('utf-8'))
    message = client_sock.recv(1024)
    print(f"server: {message.decode()}")
client_sock.close()

Server

import socket

host = 'localhost'
port = 55555

parent_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP)
parent_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

parent_sock.bind((host, port))
parent_sock.listen(5)
print(f'Server IP: {host}, Port Num: {port}로 서버 실행 중...')

child_sock, child_addr = parent_sock.accept()
print(f'{child_addr}에서 접속')

while True:
    message = child_sock.recv(1024)
    print(f'{child_addr}: {message.decode()}')
    child_sock.sendall(message)

child_sock.close()
parent_sock.close()

두 코드다 위에서 한번 각각의 소켓 함수들이 무엇을 의미하는지 설명했으니 파이썬 언어를 조금 안다면 크게 이해하는데 어려움이 없을 거라고 생각한다. 그래서 언급하지 않은 몇몇 코드들을 설명하면 아래와 같다.

socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP)
  • AF_INET
    소켓 객체를 생성할 때 프로토콜 패밀리를 설정하는 옵션으로 AF_INET은 IPv4 프로토콜을 사용한다는 의미이다.
    (현재 세계적으로도 아직 대부분은 IPv4를 사용)
  • SOCK_STREAM
    소켓의 type을 결정하는 옵션으로 연결 지향성 소켓이냐 비 연결 지향성 소켓을 생성을 결정하는 것으로 우리 코드에서는 stream → 연결 지향성 소켓을 생성한다.
    (연결 지향성 소켓과 TCP 소켓은 거의 같음…)
  • IPPROTO_TCP
    마지막 옵션은 어떤 IP 프로토콜을 사용할 것인지 결정하는 옵션으로 위에서 말한 듯이 연결 지향성 소켓은 TCP를 의미하므로 이 옵션이 없어도 정상적으로 같은 tcp 소켓을 생성한다.
    (그럼에도 이 옵션이 존재하는 의미는 TCP, UDP 말고 다양한 IP 프로토콜이 존재하고 원시 소켓을 생성할 때 사용)

실행 결과

마지막으로 프로그램의 실행 결과를 나타낸 그림으로 이번 post를 마무리하겠당~~

  1. 서버 실행

  1. 클라이언트 실행 후 메시지 입력

  1. 클라이언트와 연결된 서버

  1. 서버로 메세지를 받은 클라이언트

  1. 무한히 계속 클라이언트에서 메시지 입력 → 서버로 같은 메시지 되돌아옴 반복!

Reference

파이썬 소켓 프로그래밍 - 1. 간단한 채팅 프로그램 구현(1)

socket — 저수준 네트워킹 인터페이스

TCP/IP 쉽게 이해하기

[기본] 소켓(SOCKET)통신 이란?

profile
끄적끄적

0개의 댓글

관련 채용 정보