

✅ 여러 명 접속 가능
✅ 실시간 채팅
✅ 닉네임 표시
✅ 시간 표시
예시:
[CHAT] [16:40] 감자
안녕
✅ 입장 알림
[SYSTEM] 감자님이 입장했습니다.
✅ 퇴장 알림
[SYSTEM] 감자님이 퇴장했습니다.
✅ 현재 접속자 목록 자동 표시
[USERS]
감자 / 멍멍이 / 아르
새 유저 들어오면 자동 갱신 😼
명령어:
/w 닉네임 메시지
예시:
/w 감자 안녕
출력:
[WHISPER] [16:41] 멍멍이 → 감자
안녕
명령어:
/?
출력:
[COMMAND]
/? : 명령어 보기
/w 닉네임 메시지 : 귓속말
end : 채팅 종료
IMAGE 버튼 : 이미지 전송
✅ IMAGE 버튼
✅ 이미지 선택
✅ 채팅창 내부 출력
✅ 이미지 미리보기 표시
😼📷
✅ 레트로 블루 감성
✅ BLUE WEB CHAT 타이틀
✅ 파란 CRT 느낌
✅ 채팅창 일체형 디자인
거의 2000년대 웹메신저 감성 🌐✨
✅ 멀티 클라이언트 지원
✅ 스레드 기반 처리
✅ 서버 로그 출력
터미널에서:
[SYSTEM] 감자님 입장했습니다.
[CHAT] [16:52] 감자
안녕
전부 볼 수 있음 😼
✅ TCP 소켓 통신
✅ localhost 연결
✅ 포트 7777 사용
✅ socket reuse 처리
socket
threading
tkinter
PIL (pillow)
datetime
thread_client2.py
import socket
import threading
def thread_recv(client_socket, addr):
while True:
recv_data = client_socket.recv(1024)
print(f"{addr}에서 보낸 메시지 : {recv_data.decode()}")
# 에코 전송
client_socket.sendall(recv_data)
# 종료 처리
if recv_data.decode().strip() == "end":
print(f"{addr}에서 종료하고 싶어합니다.")
client_socket.close()
break
# 1. 소켓 생성
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 바인딩
sock.bind(("", 7777)) #고침
# 3. 접속 대기
sock.listen()
print("The Server Start.....")
# 4. 접속 수락
conn, addr = sock.accept()
# 쓰레드 생성
recv_handler = threading.Thread(
target=thread_recv,
args=(conn, addr)
)
recv_handler.start()
thread_server2.py
import socket
import threading
from datetime import datetime
clients = {} # 닉네임 : 소켓
# 전체 전송
def broadcast(message):
for client_socket in list(clients.values()):
try:
client_socket.send(
message.encode("utf-8")
)
except:
pass
# 현재 접속자 전송
def send_user_list():
user_list = " / ".join(clients.keys())
broadcast(f"[USERS]\n{user_list}")
# 클라이언트 처리
def thread_recv(client_socket, addr):
nickname = client_socket.recv(1024).decode("utf-8").strip()
if nickname == "":
nickname = "guest"
clients[nickname] = client_socket
# 입장 알림
broadcast(
f"[SYSTEM] {nickname}님이 입장했습니다."
)
# 명령어 안내
broadcast(
"[SYSTEM] /? 를 입력하면 명령어를 볼 수 있습니다."
)
# 현재 접속자
send_user_list()
while True:
try:
recv_data = client_socket.recv(4096)
if not recv_data:
break
msg = recv_data.decode("utf-8").strip()
now = datetime.now().strftime("%H:%M")
# 종료
if msg == "end":
break
# 명령어
elif msg == "/?":
help_msg = (
"[COMMAND]\n"
"──────── COMMAND ────────\n"
"/? : 명령어 보기\n"
"/w 닉네임 메시지 : 귓속말\n"
"end : 채팅 종료\n"
"IMAGE 버튼 : 이미지 전송\n"
"────────────────────────"
)
client_socket.send(
help_msg.encode("utf-8")
)
# 귓속말
elif msg.startswith("/w"):
parts = msg.split(" ", 2)
if len(parts) >= 3:
target = parts[1]
whisper_msg = parts[2]
if target in clients:
send_msg = (
f"[WHISPER] "
f"[{now}] "
f"{nickname} → {target}\n"
f"{whisper_msg}"
)
clients[target].send(
send_msg.encode("utf-8")
)
client_socket.send(
send_msg.encode("utf-8")
)
else:
client_socket.send(
"[SYSTEM] 유저를 찾을 수 없습니다.".encode("utf-8")
)
# 이미지
elif msg.startswith("[IMAGE]"):
image_path = msg.replace(
"[IMAGE]",
""
)
broadcast(
f"[IMAGE]{nickname}|{image_path}"
)
# 일반 채팅
else:
send_msg = (
f"[CHAT] "
f"[{now}] "
f"{nickname}\n"
f"{msg}"
)
broadcast(send_msg)
except:
break
# 퇴장 처리
if nickname in clients:
del clients[nickname]
broadcast(
f"[SYSTEM] {nickname}님이 퇴장했습니다."
)
send_user_list()
client_socket.close()
# 서버 생성
sock = socket.socket(
socket.AF_INET,
socket.SOCK_STREAM
)
sock.setsockopt(
socket.SOL_SOCKET,
socket.SO_REUSEADDR,
1
)
sock.bind(("", 7777))
sock.listen()
print("BLUE WEB CHAT SERVER START")
while True:
conn, addr = sock.accept()
recv_handler = threading.Thread(
target=thread_recv,
args=(conn, addr)
)
recv_handler.start()
gui_client.py
import tkinter
import socket
from threading import Thread
from tkinter import simpledialog
from tkinter import filedialog
from PIL import Image, ImageTk
image_refs = []
# 텍스트 추가
def add_text(message, tag="normal"):
chat_text.config(state="normal")
chat_text.insert(
tkinter.END,
message + "\n\n",
tag
)
chat_text.see(tkinter.END)
chat_text.config(state="disabled")
# 이미지 추가
def add_image(nickname, image_path):
chat_text.config(state="normal")
chat_text.insert(
tkinter.END,
f"[ IMAGE ] {nickname}\n",
"image_title"
)
try:
img = Image.open(image_path)
img.thumbnail((220, 220))
photo = ImageTk.PhotoImage(img)
image_refs.append(photo)
chat_text.image_create(
tkinter.END,
image=photo
)
chat_text.insert(
tkinter.END,
"\n\n"
)
except:
chat_text.insert(
tkinter.END,
"[이미지 출력 실패]\n\n",
"system"
)
chat_text.see(tkinter.END)
chat_text.config(state="disabled")
# 메시지 전송
def send(event=None):
msg = input_msg.get().strip()
if msg == "":
return
sock.send(msg.encode("utf-8"))
input_msg.set("")
if msg == "end":
sock.close()
win.quit()
# 이미지 전송
def send_image():
filepath = filedialog.askopenfilename(
filetypes=[
(
"Image Files",
"*.png;*.jpg;*.jpeg;*.gif"
)
]
)
if filepath:
sock.send(
f"[IMAGE]{filepath}".encode("utf-8")
)
# 메시지 수신
def recvMessage():
try:
while True:
msg = sock.recv(4096).decode("utf-8")
# 시스템
if msg.startswith("[SYSTEM]"):
add_text(msg, "system")
# 접속자
elif msg.startswith("[USERS]"):
users = msg.replace(
"[USERS]\n",
""
)
add_text(
"──────── ONLINE ────────\n"
+ users +
"\n────────────────────────",
"users"
)
# 명령어
elif msg.startswith("[COMMAND]"):
add_text(msg, "command")
# 귓속말
elif msg.startswith("[WHISPER]"):
add_text(msg, "whisper")
# 이미지
elif msg.startswith("[IMAGE]"):
data = msg.replace(
"[IMAGE]",
""
)
nickname, image_path = data.split("|", 1)
add_image(
nickname,
image_path
)
# 일반 채팅
elif msg.startswith("[CHAT]"):
add_text(
msg.replace("[CHAT] ", ""),
"chat"
)
else:
add_text(msg)
except:
add_text(
"[SYSTEM] 서버 종료",
"system"
)
# 종료
def on_delete():
try:
sock.send("end".encode("utf-8"))
sock.close()
except:
pass
win.destroy()
# GUI 생성
win = tkinter.Tk()
win.geometry("700x700")
win.configure(bg="#f5f5f5")
# 닉네임
name = simpledialog.askstring(
"nickname",
"닉네임 입력"
)
if name is None or name.strip() == "":
name = "guest"
# 창 제목
win.title(f"BLUE WEB CHAT :: {name}")
# 상단 제목
title = tkinter.Label(
win,
text=f"BLUE WEB CHAT :: {name}",
font=("Courier New", 24, "bold"),
fg="#0033ff",
bg="#f5f5f5"
)
title.pack(pady=15)
# 부제목
sub_title = tkinter.Label(
win,
text="RETRO STYLE NETWORK MESSENGER",
font=("Courier New", 11),
fg="#0033ff",
bg="#f5f5f5"
)
sub_title.pack()
# 장식선
line = tkinter.Label(
win,
text="******************************************************",
font=("Courier New", 10),
fg="#0033ff",
bg="#f5f5f5"
)
line.pack(pady=10)
# 채팅 프레임
chat_frame = tkinter.Frame(
win,
bg="#ffffff",
highlightbackground="#0033ff",
highlightthickness=1
)
chat_frame.pack(
padx=20,
pady=10,
fill=tkinter.BOTH,
expand=True
)
# 스크롤
scroll = tkinter.Scrollbar(chat_frame)
scroll.pack(
side=tkinter.RIGHT,
fill=tkinter.Y
)
# 채팅창
chat_text = tkinter.Text(
chat_frame,
font=("Courier New", 12),
fg="#0033ff",
bg="#ffffff",
yscrollcommand=scroll.set,
wrap="word",
borderwidth=0
)
chat_text.pack(
side=tkinter.LEFT,
fill=tkinter.BOTH,
expand=True
)
scroll.config(command=chat_text.yview)
# 태그 스타일
chat_text.tag_config(
"system",
foreground="#0033ff",
font=("Courier New", 11, "bold")
)
chat_text.tag_config(
"users",
foreground="#0033ff",
font=("Courier New", 11, "bold")
)
chat_text.tag_config(
"command",
foreground="#0033ff",
font=("Courier New", 11)
)
chat_text.tag_config(
"whisper",
foreground="#0033ff",
font=("Courier New", 11, "italic")
)
chat_text.tag_config(
"chat",
foreground="#0033ff",
font=("Courier New", 12)
)
chat_text.tag_config(
"image_title",
foreground="#0033ff",
font=("Courier New", 11, "bold")
)
chat_text.config(state="disabled")
# 하단 프레임
bottom_frame = tkinter.Frame(
win,
bg="#f5f5f5"
)
bottom_frame.pack(
fill=tkinter.X,
padx=20,
pady=15
)
# 입력 변수
input_msg = tkinter.StringVar()
# 입력창
inputbox = tkinter.Entry(
bottom_frame,
textvariable=input_msg,
font=("Courier New", 14),
fg="#0033ff",
bg="#ffffff",
insertbackground="#0033ff",
highlightbackground="#0033ff",
highlightthickness=1
)
inputbox.pack(
side=tkinter.LEFT,
fill=tkinter.X,
expand=True,
ipady=12
)
inputbox.bind("<Return>", send)
# 이미지 버튼
image_button = tkinter.Button(
bottom_frame,
text="IMAGE",
command=send_image,
font=("Courier New", 10, "bold"),
fg="#0033ff",
bg="#ffffff",
activeforeground="#ffffff",
activebackground="#0033ff",
relief="solid",
borderwidth=1
)
image_button.pack(
side=tkinter.RIGHT,
padx=5,
ipadx=10,
ipady=10
)
# 전송 버튼
send_button = tkinter.Button(
bottom_frame,
text="SEND",
command=send,
font=("Courier New", 10, "bold"),
fg="#0033ff",
bg="#ffffff",
activeforeground="#ffffff",
activebackground="#0033ff",
relief="solid",
borderwidth=1
)
send_button.pack(
side=tkinter.RIGHT,
padx=5,
ipadx=10,
ipady=10
)
# 종료 이벤트
win.protocol(
"WM_DELETE_WINDOW",
on_delete
)
# 서버 연결
IP = "localhost"
PORT = 7777
sock = socket.socket(
socket.AF_INET,
socket.SOCK_STREAM
)
sock.connect((IP, PORT))
# 닉네임 전송
sock.send(name.encode("utf-8"))
# 수신 쓰레드
receive_thread = Thread(
target=recvMessage
)
receive_thread.daemon = True
receive_thread.start()
# 포커스
inputbox.focus()
# 실행
win.mainloop()




| 정보보안 (0) | 2026.04.15 |
|---|