왕초보를 위한 Python 2.7 12. 인터넷 12.4. 채팅(1)

12.4. 채팅(1)

 

(고친 날 : 2001-09-21)

 

오늘은 간단한 채팅 프로그램을 만들어볼까 합니다.

 

우리가 벌써 그런 어려운 것까지 할 수 있을까 걱정하시는 분도 있겠지만, 알고 보면 생각보다 쉽다고 느끼실 거예요.

 

채팅을 하기 위해서는 채팅 서비스를 제공하는 서버가 있어야하죠? 또, 사용자들은 각각 그 서버에 접속해서 다른 사람들과 대화를 나누구요.

 

요즘은 채팅 서비스를 제공하는 웹사이트에 접속해서 웹브라우저 상에서 채팅을 하는 경우가 많지만, 이번에 만들어볼 것은 웹사이트를 거치지 않고 채팅을 위한 전용 프로그램을 써서 채팅 서버에 접속하는 방식입니다.

 

 

 

소켓(socket)

서로 다른 컴퓨터가 통신망을 통해서 데이터를 주고받는 방법 중에 많이 사용되는 것으로 소켓(socket)이라는 것이 있습니다.

가전제품의 전원 플러그를 영어로는 보통 소켓이라고 하거든요. 그림을 한 번 보세요.

 

 

서버와 클라이언트가 소켓으로 접속된 모습이랍니다. ^^;

우리가 만들어볼 채팅 프로그램들도 이렇게 소켓을 사용해서 만들거구요.

어라… 채팅 프로그램들이라? 프로그램이 한 개가 아니라 여러 개냐구요? >.<

네에~ 서버랑 클라이언트 둘 다 만들어야죠~ *^^*

그럼 왠지 좀 더 쉬울 것만 같은 클라이언트 쪽부터 만들어보도록 하죠.

참고로, 서버와 클라이언트 프로그램을 각각 만들기는 하겠지만, 컴퓨터는 한 대만 있으면 된답니다.

 

채팅 클라이언트

채팅 프로그램을 만들겠다고 큰소리를 치긴했지만, 막상 우리가 알고 있는 그런 좋은 채팅 프로그램을 만들려고 하니 엄두가 안 나네요.

기능을 아주아주 간단하게 줄여서, 일단 서버에 접속부터 하고, 내가 한 마디 하면, 상대쪽에서 한 마디하는 정도만 구현해보도록하겠습니다.

사용자 인터페이스도 그냥 텍스트 환경으로 만들어보고, 프로그램이 잘 돌아가면 나중에 GUI 환경으로 업그레이드하도록 하죠.

 

자, 시작합니다.

소켓 프로그래밍을 위한 모듈의 이름은 바로 socket입니다. import 해주세요.

그리고, 아무리 간단하다고는 해도 명색이 채팅 프로그램이니까 사용자에게 대화명 정도는 물어보도록 하지요.

 

 

# chat_client.py

from socket import *

print '#############'
print '사오정 대화방'
print '#############'

name = raw_input('대화명을 입력하세요:')

 

주소와 포트 번호

 

대화명을 얻었으면, 뜸들일 것 없이 바로 채팅 서버에 소켓 접속을 합시다.

소켓 객체를 만들고, 서버의 주소와 포트 번호라는 것을 가지고 접속을 하면 됩니다.

이 때, 서버 측에서는 이미 소켓 접속을 할 수 있는 준비를 갖추고 클라이언트를 기다리고 있는 상태라야 합니다.

 

 

if name:
    # 클라이언트 소켓을 생성한다.
    HOST = '127.0.0.1'
    PORT = 60000
    cs = socket(AF_INET, SOCK_STREAM)
    cs.connect((HOST, PORT))

 

 

 

채팅 서버는 특별히 다른 컴퓨터에 만들 것이 아니라, 여러분의 컴퓨터에서 작동하도록 할 테니까 자기 자신을 가리키는 주소인 '127.0.0.1'을 사용하면 됩니다. 요렇게 해주시면 인터넷으로 연결된 다른 컴퓨터에 접속한 것처럼 동작을 한답니다.

 

이것을 루프백(loopback) 주소라고 하구요,  ‘127.0.0.1’ 대신 ‘localhost’라고 써주셔도 됩니다.

 

 

 

포트 번호라는 것은 한 대의 서버가 HTTP, FTP, SMTP와 같은 여러가지 서비스를 수행할 수 있기 때문에, 클라이언트가 그 중에서 어떤 서비스를 이용할 것인지 고를 수 있도록 정해주는 번호입니다.

 

포트 번호는 0에서 65535까지를 쓸 수 있는데, 그 중 일부는 특정한 서비스가 쓰기로 약속이 되어있지요. 예를 들면 80번 포트는 HTTP, 21번 포트는 FTP, 25번 포트는 SMTP가 사용합니다.

 

이런 식으로 0에서 1023까지는 많이 사용하기 때문에 건드리지 않는 것이 좋구요, 개인적으로 쓰기에는 5만번대 이상이 적당하다고 합니다.

 

참고로 지금 정해준 포트 번호는 서버에서 사용되는 번호로, 클라이언트에서 사용하는 포트 번호와는 아무 상관이 없습니다.

 

따라서 클라이언트의 주소와 포트 번호는 신경쓰실 필요가 없지요.

 

 

 

소켓의 종류

 

 

그 다음에 소켓을 생성하기 위해 socket(AF_INET, SOCK_STREAM)라고 해주었는데, 이건 또 뭘까요?

 

괄호의 앞부분에 있는 것은 서버와 클라이언트의 장소를 가리키는데, AF_UNIX라고 하면 클라이언트와 서버가 유닉스 환경의 동일한 컴퓨터에 존재해야한다는 뜻이고, AF_INET이라고 하면 클라이언트와 서버가 인터넷 어느 곳에든지 존재할 수 있다는 뜻이라고 합니다.

 

우리의 목적을 달성하려면 AF_INET을 써야겠죠?

 

 

 

또, 괄호의 뒷부분은 소켓의 종류를 가리키는데, SOCK_STREAM을 써주면 데이터를 차례차례 꼼꼼하게 보내주는 TCP(Transmission Control Protocol) 소켓을 생성하고, SOCK_DGRAM을 쓰면 데이터를 대충대충 빨리빨리 순서에 상관없이 보내주는 UDP(User Datagram Protocol) 소켓을 생성하게 됩니다.

 

TCP 소켓이 안정적이기 때문에 대부분의 서비스에서 애용하지만, 동영상이나 음악을 실시간으로 전송하는 것과 같은 경우에는 UDP 소켓을 쓴다고 하는군요.

 

채팅을 할 때는 데이터를 잃어버리면 안되니까 TCP 소켓을 사용하는 것이 좋겠죠? 

 

그러니까 SOCK_STREAM을 써주시면 됩니다.

 

 

 

이렇게 해서 만들어진 소켓 객체는 클라이언트(client) 쪽의 소켓(socket)이라는 뜻으로 cs라는 이름을 붙여보았습니다.

 

이것의 connect() 메쏘드를 써서 조금 전에 정해둔 주소와 포트 번호로 연결해주면 되구요.

 

(HOST, PORT)라는 튜플을 인자로 주기 때문에 괄호를 두 개씩 쓴 것이 특이하네요.

 

 

 

오늘따라 알아야할 것이 많지요? 

 

일단 고비는 넘겼으니까 클라이언트를 마저 끝내고나서 한숨돌리도록 하시지요.

 

서버에 소켓 접속까지 했으니 데이터를 주고받는 일만 남았습니다.

 

 

 

    # 데이터를 주고 받는다.
    cs.send(name)
    data = cs.recv(100)
    while 1:
        print data
        cs.send(raw_input() or ' ')
        data = cs.recv(1024)

 

 

이 부분도 if name:에 속하는 부분이니 들여쓰기를 맞춰주세요.

 

척 보면 아시겠지만 send()를 써서 서버에게 데이터를 보내고, 다시 recv()를 써서 서버로부터 데이터를 얻고 있습니다.

첫째, 둘째 줄에서는 아까 입력받은 대화명을 서버에게 보낸 후, 서버로부터 어떤 메시지를 얻어옵니다. 

 

서버에서 ‘OO님께서 접속하셨습니다’와 같은 메시지를 보내주도록 하려구요.

 

recv(100)이라고 숫자를 써준 것은 데이터를 한 번에 최대 100바이트 씩 받겠다는 뜻이지요.

 

8비트(bit)를 바이트(byte)라고 하고, 영어랑 숫자는 한 글자가 1바이트, 한글은 한 자에 2바이트 크기라는 것은 알고 계시죠?

 

 

 

그 다음부터는 읽어온 data를 화면에 출력한 다음, 사용자의 대화를 입력받아 서버로 보내주고, 다시 서버에서 보내오는 데이터를 data에 저장하는 과정을 계속 반복합니다. 

 

raw_input() or ' '이라고 한 것은 대화를 입력받는 부분에서 사용자가 그냥 엔터키만 치는 경우에는 공백 하나를 보내주도록 하기 위한 것입니다. 

 

그냥 raw_input()만 써줬더니 사용자가 대화를 입력하지 않고 엔터키를 쳐서 아무 문자열도 보내지 않으면, 서버의 대답만 하염없이 기다리는 바람에 먹통이 되더라구요.

 

 

 

간단한 클라이언트 프로그램인데도 설명이 길었죠?

 

위의 설명을 다시 한 번 읽어보시고 대충 정리를 해주세요. 기지개도 한 번 켜시구요.

 

 

 

채팅 서버

 

이제는 서버를 만들어보겠습니다. 소스를 한번 훑어보세요.

 

# chat_server.py

from socket import *

print '##################'
print '사오정 대화방 서버'
print '##################'

# 서버 소켓을 생성한다.

HOST = ''
PORT = 60000
ss = socket(AF_INET, SOCK_STREAM)
ss.bind((HOST, PORT))
ss.listen(1)

# 클라이언트를 받아들인다.
con, add = ss.accept()

# 데이터를 주고 받는다.
name = con.recv(10)
data = '***' + name + '님께서 접속하셨습니다. ***'

while data:
    print data
    con.send(data)
    data = name + '] ' + con.recv(1024)

서버도 클라이언트와 거의 비슷하죠? 서버쪽 소켓 객체(ss)를 만든 후에는 클라이언트에서와 같이 connect하는 것이 아니라, bind와 listen, 그리고 accept를 한다는 것이 차이점입니다.

ss.bind((HOST, PORT))라고 한 것은 서버쪽 소켓 ss가 HOST라는 주소와 PORT라는 포트번호로 서비스를 하겠다고 지정해준 것인데, HOST 주소는 빈 문자열(‘’)만 써주시면 되고, 포트 번호는 아까 클라이언트에서 썼던 것과 같은 번호를 써주시면 되지요.

그 다음에는 listen()과 accept()가 있는데, listen()은 클라이언트가 받아들여지지 않은 상태로 접속만 할 수 있는 개수를 정해준 것이고, accept()에서 비로소 클라이언트를 받아들이게 됩니다.

 

accept() 메쏘드를 실행하게 되면, 클라이언트를 받아들이기 위해 새로운 소켓이 하나 더 생성되어서 클라이언트가 접속해올 때까지 기다리게 되지요.

그렇게 기다리다가 클라이언트와 접속이 이루어지고 나면 accept() 메쏘드의 결과 값으로 그 소켓과 클라이언트의 주소 및 포트 번호를 돌려줍니다. 결과값으로 얻은 소켓은 con으로, 주소와 포트 번호는 add로 받아주었지요.

이제부터는 con을 이용해서 클라이언트와 데이터를 주고 받으면 됩니다.

나머지 부분은 아까 클라이언트를 만들었던 것과 같은 방법으로 해주었으니 어렵지 않을 거예요.

이렇게해서 아주 간단한 채팅서버도 만들어보았습니다.

사실 이 서버는 클라이언트를 하나만 받아들일 수 있습니다. 채팅 서버라기보다는 혼잣말 서버라고 하는 것이 낫겠군요. 보통 에코(echo, 메아리) 서버라고들 하지요.

이제 테스트를 해봐야죠?

서버를 먼저 실행시키고 나서, 클라이언트를 실행시키시면 됩니다.

 

마지막 수정일 : 2013년 4월 13일 2:34:49 오후
이 름 :
홈페이지 :
비밀번호 :
검색구분 :
최근변경이력