https://chinpa.tistory.com/9
server.py
import sys
import socket
from tkinter import *
from _thread import *
from PyQt5.QtCore import Qt, QSize
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QLineEdit, QTextEdit, QVBoxLayout, QHBoxLayout, QScrollArea, \
QPushButton
class MyApp(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.vbox = QVBoxLayout()
self.hbox = QHBoxLayout()
self.hbox.addStretch(1)
self.iplbl = QLabel('Server ip:')
self.hbox.addWidget(self.iplbl)
self.iptxt = QLineEdit('14.33.178.245')
self.hbox.addWidget(self.iptxt)
self.iptxt.setMaximumWidth(100)
self.hbox.addStretch(1)
self.portlbl = QLabel('port:')
self.hbox.addWidget(self.portlbl)
self.porttxt = QLineEdit('9999')
self.porttxt.setMaximumWidth(50)
self.hbox.addWidget(self.porttxt)
self.hbox.addStretch(1)
self.vbox.addLayout(self.hbox)
self.scrArea = QScrollArea()
self.vbox.addWidget(self.scrArea)
self.hbox2 = QHBoxLayout()
self.serveropenbtn = QPushButton('server open')
self.serveropenbtn.clicked.connect(self.serveropen)
self.serveropenbtn.setCheckable(False)
self.serveropenbtn.toggle()
# self.serveropenbtn.setMaximumWidth(100)
self.hbox2.addWidget(self.serveropenbtn)
self.serverclosebtn = QPushButton('Server close')
self.serveropenbtn.clicked.connect(self.serverclose)
self.serverclosebtn.setCheckable(False)
self.serverclosebtn.toggle()
# self.serverclosebtn.setMaximumWidth(100)
self.hbox2.addWidget(self.serverclosebtn)
self.vbox.addLayout(self.hbox2)
self.setLayout(self.vbox)
self.setWindowTitle('server')
self.move(300, 300)
self.setFixedSize(QSize(500, 500))
self.setWindowFlags(Qt.WindowTitleHint | Qt.WindowMinimizeButtonHint | Qt.WindowCloseButtonHint)
self.show()
def serveropen(self):
HOST = self.iptxt.text()
PORT = int(self.porttxt.text())
start_new_thread(self.make_server, (HOST, PORT))
self.serveropenbtn['state'] = 'disabled'
self.iptext['state'] = 'readonly'
self.porttxt['state'] = 'readonly'
def serverclose(self):
exit()
def make_server(self, 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(self.threaded, (c_list[-1], addr))
def threaded(self, 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()
c_list = []
close = False
server_socket = None
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = MyApp()
sys.exit(app.exec_())
client.py
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, '14.33.178.245')
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()
top of page
기능을 테스트하려면 라이브 사이트로 이동하세요.
채팅
채팅
댓글 0개
좋아요
댓글(0)
bottom of page