Python/2.7 for fun.

gui 멀티쓰레드 채팅 서버, 클라이언트

qkqhxla1 2014. 12. 22. 19:52

방학때 몇일 날잡아서 멀티쓰레드 채팅 서버를 만들어보려고 했는데 의외로 여태까지 한 수련이


헛되진 않았는지 하루동안 열심히 투자하니까 틀은 완성됬다. 이번 코딩을 하면서


오히려 서버 클라이언트 사이의 작동보다는 유니코드 인코딩 관련 조사를 가장 많이 한것 같다.


근데 소스코드를 다시 살펴보면 정말 초심자가 짠 어색하고 더러운 코드다....


방법이 있긴 한거같은데 몰라서 야매로,더럽게 구현한것도 많다.


실행결과.




첫 실행 결과. 왼쪽 서버, 오른쪽 클라이언트이다. 서버는 gui화 안했고 클라이언트만 gui.


접속하면 접속한 유저의 아이피주소와 내가 유저몇인지 뜸..




왼쪽이 서버이고 클라이언트를 하나 더 실행시켰을때 화면. 원래 있던 유저 1에도 유저 2가 접속했다고


뜨는것을 확인할수 있다.





메시지를 쓰고 보내기버튼을 누르면 서버, 내 자신, 다른 클라이언트의 화면에까지 채팅내용이 뜬다.




유저 2도 마찬가지...




유저 2가 나가기버튼을 눌러 나간경우 나갔다고 뜬다. 


개선점 : 예외처리를 더 정교하게 처리, 마우스클릭말고 엔터 눌렀을때도 채팅메시지 보내기, 서버측도 gui로 구현, 클라이언트의 창을 더 효과적으로 나누기,클라이언트 입장 시 호칭 정하기 등등등





서버.


# -*- encoding: cp949 -*-
import socket,threading,Tkinter

sem = threading._Semaphore()
count = 0 #클라이언트 순서번호 매기기
th = []; conns = [] #쓰레드 집어넣을 리스트,
def to_client(conn,addr,count):
    cnt = count
    global conns
    for i in range(len(conns)):
        conns[i].sendall('%s에서, 유저 %d님이 접속하였습니다.' %(addr[0],cnt))
    print '%s에서, 유저 %d님이 접속하였습니다.' %(addr[0],cnt)
    conn.sendall('서버에 접속하셨습니다.\n당신은 유저 %d입니다' %cnt)
    try:
        while 1:
            read = conn.recv(1000)
            if read=='-1':
                conn.sendall('-1')
                exit(0)
            read = '유저 %d : %s' %(cnt,read)
            print read
            for i in range(len(conns)):
                conns[i].sendall(read)
    except:
        print '유저 %d 님이 나가셨습니다.' %cnt
        conns.remove(conn)
        for i in range(len(conns)):
            conns[i].sendall('유저 %d 님이 나가셨습니다.' %cnt)
        exit(0)

s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind(('127.0.0.1',50000))
s.listen(1)

while 1:
    conn,addr = s.accept()
    conns.append(conn)
    sem.acquire(); count += 1; sem.release()
    client = threading.Thread(target=to_client,args=(conn,addr,count))
    client.start()
    th.append(client);
for t in th:
    t.join()


클라이언트.


# -*- encoding: cp949 -*-
import socket,threading,Tkinter

th = []
def listen(s):
    global text
    while 1:
        read = s.recv(1000)
        if read=='-1':
            exit(0)
        text.insert('insert',unicode(read,'cp949'))
        text.insert('insert','\n')

def button1Click(event):
    global s
    global E
    if E.get()=='-1':
        s.sendall('-1')
    s.sendall( E.get().encode('cp949') )
    
def button2Click(event):
    global s
    s.sendall('-1')
    root.destroy()

s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(('127.0.0.1',50000))
try:
    root = Tkinter.Tk()
    f = Tkinter.Frame(root)
    f.pack()
    L = Tkinter.Label(f, text=unicode("메시지 : ",'cp949'))
    L.pack( side = 'left')
    E = Tkinter.Entry(f, bd =5)
    E.pack(side = 'left')

    text=Tkinter.Text(root)
    text.pack()
    text.insert('insert',unicode(s.recv(1000),'cp949'))
    text.insert('insert','\n\n')

    button2=Tkinter.Button(f,text=unicode('나가기','cp949'),background='red')
    button2.pack(side='right')
    button2.bind('<Button-1>',button2Click)

    l = threading.Thread(target=listen,args=(s,))
    th.append(l)
    l.start()

    button1=Tkinter.Button(f,text=unicode('보내기','cp949'),background='white')
    button1.pack(side='right')
    button1.bind('<Button-1>',button1Click)

    root.mainloop()
except:
    pass
    exit(0)
for t in th:
    t.join()