반응형
개인적으로 네트워크 관련 내용을 배울 기회를 갖게 되어, 배운 내용을 기반으로 채팅프로그램을 만들어보았습니다.
서버 소스 코드
''' 서버 예제
각 클라이어언트가 보낸 메세지를 서버에서 클라이언트 정보와 조합하여 각 클라이언트 채팅창에 띄워 보내 주는 역할 '''
import socket
from _thread import *
from tkinter import *
def threaded(client_socket, addr):
global chat_log
chat_log['state'] = 'normal'
chat_log.insert("end", 'Connected by :'+ addr[0] + ':' + str(addr[1]) + '\n')
chat_log['state'] = 'disabled'
for c in c_list:
c.sendall(('[System] ' + str(addr[1]) + ' 님이 접속하였습니다.').encode())
while 1:
try:
data = client_socket.recv(1024)
chat_log['state'] = 'normal'
chat_log.insert("end", 'Received from ' + addr[0] + ' : ' + str(addr[1]) + ' :: ' + str(data.decode()) + '\n')
chat_log['state'] = 'disabled'
for c in c_list:
c.sendall((str(addr[1]) + ' : ' + data.decode()).encode())
except ConnectionResetError as e:
c_list.remove(client_socket)
for c in c_list:
c.sendall(('[System] '+ str(addr[1]) + ' 님이 나갔습니다.').encode())
chat_log['state'] = 'normal'
chat_log.insert("end", 'Disconnected by ' + addr[0] + ':' + str(addr[1]) + '\n')
chat_log['state'] = 'disabled'
break
client_socket.close()
def server_open():
HOST = ip_entry.get(); PORT = int(port_entry.get())
start_new_thread(make_server,(HOST,PORT))
open_button['state'] = 'disabled'
ip_entry['state'] = 'readonly'
port_entry['state'] = 'readonly'
def server_close():
exit()
def make_server(HOST, PORT):
global server_socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 포트사용중이라 연결할 수 없다는 WinError 10048 에러를 해결하기 위해 필요합니다.
# 서버 소켓의 SOL_SOCKET의 SO_REUSEADDR(이미 사용중인 포트에 대해서도 바인드 허용) 를 1(True)로 설정하는 것으로 이해
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind((HOST, PORT))
server_socket.listen()
chat_log['state'] = 'normal'
chat_log.insert("end", 'Server Start\n')
chat_log['state'] = 'disabled'
while 1:
client_socket, addr = server_socket.accept()
c_list.append(client_socket)
start_new_thread(threaded, (c_list[-1], addr))
c_list = []
close = False
server_socket = None
s_root = Tk()
s_root.geometry('500x500')
s_root.title('Server')
s_root.resizable(False, False)
''' Top Menu '''
Label(s_root, text = 'Server IP : ').place(x=20, y=20)
Label(s_root, text = 'Port : ').place(x=250, y=20)
ip_entry = Entry(s_root, width=14, text = '127.0.0.1'); ip_entry.place(x=83, y=21)
ip_entry.insert(0,'127.0.0.1')
port_entry = Entry(s_root, width=5, text = '9999'); port_entry.place(x = 290, y=21)
port_entry.insert(0,'9999')
open_button = Button(s_root,text='Server Open', command=server_open); open_button.place(x=380, y=18)
''' Middle Menu '''
chat_log = Text(s_root, width = 65, height = 29, state = 'disabled', spacing2 = 2) ; chat_log.place(x=20, y=60)
''' Bottom Menu '''
close_button = Button(s_root,text='Server Close',command=server_close); close_button.place(x=200, y = 460)
s_root.mainloop()
클라이언트 소스코드
import socket
from _thread import *
import threading
from tkinter import *
from time import sleep
def send(socket):
global go_send
while True:
if go_send:
message = (message_input.get(1.0,"end")).rstrip()
socket.send(message.encode())
message_input.delete(1.0, "end")
go_send = False
else:
if go_out:
socket.close()
exit()
sleep(0.1)
def receive(socket):
first = True
while True:
try:
data = socket.recv(1024)
chat_log['state'] = 'normal'
if first: # 이걸 처음 체크 이후 의미없이 매번 체크하므로 이렇게 하는 건 효율적이지 않음.
chat_log.insert("end",str(data.decode( )))
first = False
else:
chat_log.insert("end",'\n' + str(data.decode()))
chat_log.see('end')
chat_log['state'] = 'disabled'
except ConnectionAbortedError as e:
chat_log['state'] = 'normal'
chat_log.insert("end", '\n[System] 접속을 종료합니다.\n')
chat_log['state'] = 'disabled'
exit()
def login():
# 서버의 ip주소 및 포트
HOST = ip_entry.get(); PORT = int(port_entry.get())
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect((HOST, PORT))
threading.Thread(target=send, args= (client_socket,)).start()
threading.Thread(target=receive, args= (client_socket,)).start()
exit()
def try_login():
global go_out
start_new_thread(login,())
login_button['state'] = 'disabled'
logout_button['state'] = 'active'
ip_entry['state'] = 'readonly'
port_entry['state'] = 'readonly'
go_out = False
def try_logout():
global go_out
login_button['state'] = 'active'
logout_button['state'] = 'disabled'
ip_entry['state'] = 'normal'
port_entry['state'] = 'normal'
go_out = True
def set_go_send(event):
global go_send
go_send = True
go_out, go_send = False, False
c_root = Tk()
c_root.geometry('500x500')
c_root.title('Client')
c_root.resizable(False, False)
''' Top Menu '''
Label(c_root, text = 'Server IP : ').place(x=20, y=20)
Label(c_root, text = 'Port : ').place(x=250, y=20)
ip_entry = Entry(c_root, width=14); ip_entry.place(x=83, y=21)
ip_entry.insert(0,'127.0.0.1')
port_entry = Entry(c_root, width=5); port_entry.place(x = 290, y=21)
port_entry.insert(0,'9999')
login_button = Button(c_root,text='Log In', command=try_login); login_button.place(x=350, y=18)
logout_button = Button(c_root,text='Log Out',state = 'disabled', command = try_logout); logout_button.place(x=420, y=18)
''' Middle Menu '''
chat_frame = Frame(c_root)
scrollbar = Scrollbar(chat_frame) ; scrollbar.pack(side='right',fill='y')
chat_log = Text(chat_frame, width = 62, height = 24, state = 'disabled', yscrollcommand = scrollbar.set) ; chat_log.pack(side='left')#place(x=20, y=60)
scrollbar['command'] = chat_log.yview
chat_frame.place(x=20, y=60)
message_input = Text(c_root, width = 55, height = 4) ; message_input.place(x=20,y = 390)
send_button = Button(c_root, text = 'Send', command = lambda: set_go_send(None)); send_button.place(x=430, y=405)
message_input.bind("<Return>",set_go_send)
''' Bottom Menu '''
close_button = Button(c_root,text='Close',command=exit); close_button.place(x=200, y = 460)
c_root.mainloop()
반응형
'개인 프로젝트 > 간단한 프로젝트' 카테고리의 다른 글
[개인프로젝트] 수원시 공고알림 크롤링 및 메일링 프로그램 제작 (0) | 2024.02.10 |
---|---|
[개인프로젝트] BOJ 크롤링을 통한 동아리 출석체크 보조 프로그램 제작 (0) | 2021.01.10 |