ผู้เขียนบทความ : นาย รวิพล ไชยภักดี
1. ความเป็นมา
เนื่องจากผู้จัดทำมีความสนใจในเรื่อง การเขียน Server เบื้องต้น และ การแชทคุยกับ Server หรือผู้อื่น เลยอยากลองหาความรู้และ พัฒนาต่อยอดจาก บทความที่ผู้จัดทำเรียนรู้มา จึงได้มาเป็นโปรแกรมแชทตัวนี้ ผู้จัดทำหวังว่า ทุกๆท่านจะสามารถนำไปต่อยอดได้อีกและหวังว่าจะมีประโยชน์ต่อ ทุกๆท่านไม่มากก็น้อย
2. วัตถุประสงค์
2.1) เพื่อศึกษาหาความรู้เกี่ยวกับการเขียนโปรแกรมในภาษา python
2.2) เพื่อศึกษาหาความรู้เกี่ยวกับการเขียน Server และ การพูดคุยกับ Server เบื้องต้น
3. ขอบเขต
3.1) สามารถสื่อสารกันในเครื่อข่ายเดียวกันได้อย่างไม่มีข้อผิดพลาด
3.2) สามารถเปลี่ยนสี background หรือ ข้อความ ได้หลากหลาย
3.3) สามารถพูดคุยสื่อสารด้วย Emoji ได้หลากหลายรูปแบบ
4. ประโยชน์ที่สามารถได้รับ
4.1) ได้รับความรู้จากขั้นตอนในกระบวนการทำงานของโปรแกรม
4.2) ถ้าหากโดนสั่งปิดโปรแกรมแชทอื่นๆก็สามารถนำ โปรแกรมแชทตัวนี้ ไปคุยกันได้โดย การเปิด Server จากระยะไกล(มีค่าบริการ)
5. ความรู้ที่เกี่ยวข้อง
5.1) พื้นฐานในการใช้งานภาษา python ทั่วไป
5.2)ความรู้เกี่ยวกับการใช้งาน module threading ในการทำ multitasking
ยกตัวอย่างการทำงานเบื้องต้น เช่น การ print ข้อความ หรือ ค่าตัวเลข พร้อมกันแต่ ดีเลย์ไม่เท่ากัน (เช่น print 1
delay 1 วิ และ print 3 delay 3 วิ
เริ่มการทำงานโดยการ
import time
import threading
และ สร้างฟังก์ชัน ที่อยากจะให้ทำงานพร้อมกันขึ้นมาเช่น
def numone(one):
for i in range(10):
print('นับ :{} '.format(one))
time.sleep(1)
def numthree(three):
for i in range(10):
print('นับ :{}'.format(three))
time.sleep(3)
ต่อมาเริ่มคำสั่ง threading หรือการทำ multitasking
task1 = threading.Thread(target=numone, args=('1',))
task2 = threading.Thread(target=numthree, args=('3',))
และเริ่มการทำงานด้วย
task1.start()
task2.start()
task1.join()
task2.join()
5.3) ความรู้เกี่ยวกับการใช้งาน module socket ในการสร้าง Server
โดยการทำงานของ code ในส่วนของ server จะทำงานสัมพันธ์กับ code ตรงส่วนของ client โดย ในที่นี้ จะอธิบายการทำงานของ code ทั้ง 2 ส่วน
เริ่มต้นด้วยการ import module และ กำหนด ip กับ port ไว้
import socket
serverip = 'localhost' #ในที่นี้ คือ เครื่องของตนเอง
port = 7000
และเริ่มทำการสร้าง Server
while True:
server = socket.socket()
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)
while True:
server = socket.socket()
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)
server.bind((serverip,port))
#สั่ง server bind ด้วย ip และ Port ที่กำหนดไว้ด้านบน
server.listen(5)
print('Wating for client....')
# server.listen คือ การกำหนดให้ server รับคนเข้า พร้อมกันได้มาสูงสุด 5 คน เข้าพร้อมกัน ณ ที่นี้คือ การ join เข้า server พร้อมกัน เช่น มีผู้ใช้งานกด join เข้า server พร้อมกัน จะสามารถ รับได้ทั้งหมด 5 คน แต่ถ้าหากผู้ใช้งานกด join เข้า server เดียวกันแต่ต่างเวลาก็สามารถ เข้าได้ กี่คนก็ได้ โดยที่คนใน server ก็ยังอยู่
และในส่วนของ client หรือ ผู้ที่ connect เข้ามา เริ่มจาการ import module และกำหนด ip กับ port ไว้
import socket
serverip ='localhost'
port = 7000
และเขียนโค้ดทำการ connect เข้า server ที่สร้างไว้
while True:
server = socket.socket()
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)
server.connect((serverip,port)) #สั่งให้ client ทำการ connect ด้วย ip และ port ที่กำหนดไว้
5.4) ความรู้เกี่ยวกับการใช้งาน module tkinter ในการสร้าง GUI(Graphical User Interface)
เริ่มด้วยการ import module tkinter มา
from tkinter import *
from tkinter import ttk, messagebox
import tkinter.scrolledtext as st
การเริ่มสร้าง GUI จะเริ่มด้วยการสร้าง frame ขึ้นมา 1 frame
GUI = Tk()
w = 650 #ขนาด
h = 680 #ขนาด
ws = GUI.winfo_screenwidth()
hs = GUI.winfo_screenheight()
x = (ws/2) - (w/2)
y = (hs/2) - (h/2)
GUI.geometry(f'{w}x{h}+{x:.0f}+{y:.0f}')
GUI.title('WHY-CHAT') # ชื่อของ frame นี้
ขั้นตอนต่อไปคือการสร้าง chatbox เบื้องต้น
F1 = Frame(GUI)
F1.place(x=5,y=5)
chatbox = st.ScrolledText(F1,width =47,heigh =10)
chatbox.pack(expand=True , fill='x')
ขั้นตอนต่อไปคือการสร้าง button เบื้องต้น ต้องทำการสร้าง ฟังก์ชันขึ้นมา และกำหนดว่า ต้องการให้ ปุ่มนั้นทำอะไร เช่น การเปลี่ยนสี chatbox
def backgroundcolor1():
chatbox.configure(bg='lightcyan') #สั่งให้เปลี่ยน chatbox เป็นสี lightcyan
bgcolor1 = Frame(GUI) #สร้างใน GUI
bgcolor1.place(x=15,y=167) #ตำแหน่ง
colorbg1 = Button(bgcolor1,bg='lightcyan',command = backgroundcolor1)
colorbg1.pack(ipadx=20,ipady=5)
โค้ดส่วน GUI
(เบื้องต้น ที่ตัดในส่วนของ Emoji ออกไป บางตัว เนื่องจากโค้ดยาวเกินไป)
# GUI-Chat.py
from tkinter import *
from tkinter import ttk, messagebox
import tkinter.scrolledtext as st
from tkinter import simpledialog
####################NETWORK##########################
import socket
import threading
import sys
PORT = 7500
BUFSIZE = 4096
SERVERIP = '172.16.251.220' # SERVER IP
global client
def server_handler(client):
while True:
try:
data = client.recv(BUFSIZE) # Data from server
except:
print('ERROR')
break
if (not data) or (data.decode('utf-8') == 'byechat'):
print('OUT!')
break
messagechat = data.decode('utf-8')
allmsg.set(allmsg.get() + messagechat + '\n')
chatbox.delete(1.0,END) # clear old msg
chatbox.insert(INSERT,allmsg.get()) # insert new
chatbox.yview(END)
#print('USER: ', data.decode('utf-8'))
client.close()
discon = messagebox.showerror('Connection Failed','Disconnect')
print ('Discon ',discon)
if discon == 'ok':
GUI.destroy()
##############################################
GUI = Tk()
w = 650
h = 680
ws = GUI.winfo_screenwidth() #screen width
hs = GUI.winfo_screenheight() #screen height
x = (ws/2) - (w/2)
y = (hs/2) - (h/2)
GUI.geometry(f'{w}x{h}+{x:.0f}+{y:.0f}')
GUI.configure(bg='silver')
GUI.title('WHY-CHAT')
FONT1 = ('Angsana New',30)
FONT2 = ('Angsana New',20)
#############chatbox###############
F1 = Frame(GUI)
F1.place(x=5,y=5)
allmsg = StringVar()
chatbox = st.ScrolledText(F1,width=47,heigh=10,font=FONT1)
chatbox.pack(expand=True, fill='x')
#############message form###############
v_msg = StringVar()
F2 = Frame(GUI)
F2.place(x=10,y=560)
E1 = Entry(F2,textvariable=v_msg,font=FONT2,width=50)
E1.pack(ipady=20)
#############button###############
def SendMessage(event=None):
msg = v_msg.get()
client.sendall(msg.encode('utf-8')) ######SEND to SERVER######
chatbox.delete(1.0,END) # clear old msg
chatbox.insert(INSERT,allmsg.get()) # insert new
chatbox.yview(END)
v_msg.set('') # clear msg
E1.focus()
F3 = Frame(GUI)
F3.place(x=480,y=565)
B1 = Button(F3,text='Send',command=SendMessage,bg = 'lime', font=FONT2)
B1.pack(ipadx=40,ipady=5)
E1.bind('<Return>',SendMessage)
username = StringVar()
global getname
getname = ''
##################buttonemoji##################
def GUIEmoji():
GUI4 = Tk()
GUI4.attributes('-topmost',True)
w = 300
h = 300
ws = GUI4.winfo_screenwidth() #screen width
hs = GUI4.winfo_screenheight() #screen height
x = (ws/2) - (w/2)
y = (hs/2) - (h/2)
GUI4.geometry(f'{w}x{h}+{x:.0f}+{y:.0f}')
textemoji = ttk.Label(GUI4,text='Emoji',font=FONT2).pack()
def emogi1():
emogi1 = '❤️'
client.sendall(emogi1.encode('utf-8'))
chatbox.insert(INSERT,emogi1)
chatbox.yview(END)
GUI4.destroy()
emogibutton = Frame(GUI4)
emogibutton.place(x=110,y=35)
buttonemoji = ttk.Button(emogibutton,text='❤️',command=emogi1)
buttonemoji.pack(ipadx=3,ipady=6)
def emogi2():
emogi2 = '👍'
client.sendall(emogi2.encode('utf-8'))
chatbox.insert(INSERT,emogi2)
chatbox.yview(END)
GUI4.destroy()
emogibutton = Frame(GUI4)
emogibutton.place(x=10,y=35)
buttonemoji = ttk.Button(emogibutton,text='👍',command=emogi2)
buttonemoji.pack(ipadx=3,ipady=6)
emojibutton = Frame(GUI)
emojibutton.place(x=15,y=643.5)
buttomemojipopup = Button(emojibutton,text= 'Emoji',command=GUIEmoji)
buttomemojipopup.pack(ipady=3,ipadx=200)
#####################popupcolor################
def GUI3color():
GUI3 = Tk()
GUI3.attributes('-topmost',True)
w = 300
h = 300
ws = GUI3.winfo_screenwidth() #screen width
hs = GUI3.winfo_screenheight() #screen height
x = (ws/2) - (w/2)
y = (hs/2) - (h/2)
GUI3.geometry(f'{w}x{h}+{x:.0f}+{y:.0f}')
GUI3.configure(bg='silver')
textc = Label(GUI3,text='textcolor',font=FONT2,fg='black',bg='silver').pack()
def textcolor1():
chatbox.configure(fg='darkred')
GUI3.destroy()
color1 = Frame(GUI3)
color1.place(x=15,y=40)
bcolor1 = Button(color1,bg='darkred',command = textcolor1)
bcolor1.pack(ipadx=20,ipady=5)
def textcolor2():
chatbox.configure(fg='darkgreen')
GUI3.destroy()
color2 = Frame(GUI3)
color2.place(x=85,y=40)
bcolor2 = Button(color2,bg='darkgreen',command = textcolor2)
bcolor2.pack(ipadx=20,ipady=5)
colorbutton = Frame(GUI)
colorbutton.place(x=530,y=640)
buttompopup = Button(colorbutton,text= 'chengcolor',command=GUI3color)
buttompopup.pack(ipady=6,ipadx=6)
################CUSTOM DIALOG##################
def GUI2Dialog():
GUI2 = Toplevel()
GUI2.attributes('-topmost',True)
w = 250
h = 150
ws = GUI2.winfo_screenwidth() #screen width
hs = GUI2.winfo_screenheight() #screen height
x = (ws/2) - (w/2)
y = (hs/2) - (h/2)
GUI2.geometry(f'{w}x{h}+{x:.0f}+{y:.0f}')
v_getname = StringVar()
L = ttk.Label(GUI2, text='Name',font=FONT2).pack()
EN1 = ttk.Entry(GUI2,textvariable=v_getname,font=('Angsana New',25),width=40)
EN1.pack(padx=30,pady=00)
def EnterName(event=None):
global getname
getname = v_getname.get()
GUI2.withdraw()
GUI.attributes('-topmost',True)
E1.focus()
GUI.attributes('-topmost',False)
import random
print('GETNAME',getname)
if getname == '' or getname == None:
num = random.randint(10000,99999)
getname = str(num)
username.set(getname)
chatbox.insert(INSERT,'HI ' + getname)
###########RUN SERVER#############
global client
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)
try:
client.connect((SERVERIP,PORT))
firsttext = 'NAME|' + username.get()
client.send(firsttext.encode('utf-8'))
task = threading.Thread(target=server_handler, args=(client,))
task.start()
except:
print('ERROR!')
checkconnect = messagebox.showerror('ConnectionFailed...','can not connect')
print('CONNECT',checkconnect)
if checkconnect == 'ok':
GUI.destroy()
BB2 = ttk.Button(GUI2,text='Enter',command=EnterName)
BB2.pack(ipady=10,ipadx=20,pady=10)
EN1.bind('<Return>',EnterName)
def CheckClose():
GUI2.attributes('-topmost',False)
checkenter = messagebox.askyesno('No_name','ต้องการออกจากโปรแกรม')
print('CHECK: ',checkenter)
if checkenter == True:
GUI.destroy()
else:
GUI2.attributes('-topmost',True)
EN1.focus()
GUI2.protocol('WM_DELETE_WINDOW', CheckClose) # root is your root window
GUI2.mainloop()
GUI2Dialog()
GUI.mainloop()
โค้ดส่วน Server
import socket
import datetime
import threading
import colorama
from colorama import Fore, Back
colorama.init(autoreset=True)
PORT = 7500
BUFSIZE =4096
SERVERIP = '172.16.251.220'
clist = []
cdict ={}
def clinet_handler(client,addr):
while True:
try:
data = client.recv(BUFSIZE)
check = data.decode('utf-8').split('|')
if check[0] == 'NAME':
cdict[str(addr)] = check[1]
except:
clist.remove(client)
break
if (not data) or (data.decode('utf-8') == 'byechat'):
clist.remove(client)
print('OUT: ',client)
break
try:
username = cdict[str(addr)]
msg = username +' >> : ' + data.decode('utf-8')
except :
msg = str(addr) + ' >> : ' + data.decode('utf-8')
print('USER: ',msg)
print('---------')
try:
check = data.decode('utf-8').split('|')
if check[0] =='NAME':
pass
else:
for c in clist:
c.sendall(msg.encode('utf-8'))
except :
for c in clist:
c.sendall(msg.encode('utf-8'))
client.close()
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)
server.bind((SERVERIP,PORT))
server.listen(5)
while True:
client,addr = server.accept()
clist.append(client)
print('ALL CLIENT', clist)
task = threading.Thread(target=clinet_handler, args=(client, addr))
task.start()
6. ผลการดำเนินงาน

7. สรุปผลและข้อเสนอแนะ
การทำงานของโปรแกรมสามารถทำงานได้อย่างมีประสิทธิภาพ เนื่องจากโปรแกรมไม่ต้องเก็บข้อมูลของสิ่งที่คุยไว้เป็นระยะเวลานาน(หากปิด client ข้อมูลก็จะหายไปในส่วนข้องผู้ใช้งาน และถ้าหากปิด Server ข้อมูลก็จะหายไปทั้งหมด) จึงทำให้โปรแกรมไม่กินทรัพยากรในการใช้งานมากนัก
ข้อเสนอแนะ
โปรแกรมนี้ยังสามารถนำไปพัฒนาหรือต่อยอดได้อีกมากมายเช่นการส่งรูป การอัดเสียง หรืออื่นๆ และโปรแกรมนี้เป็นโปรแกรมที่ใช้งานฟรี จึงทำให้ทุกคนสามารถเข้าถึงได้และนำไปพัฒนาต่อยอดได้
8. ข้อมูลอ้างอิง
1.การสร้างโปรแกรมแชทและสร้าง Server
Python Network (Socket) เขียนโปรแกรมแชท + เกมออนไลน์ง่ายๆ – YouTube
2.การเลือกสี หรือ สี ที่มีทั้งหมดในโปรแกรม แชท
List of named colors — Matplotlib 3.3.4 documentation
3.วิธีการตกแต่งโปรแกรมแชท
Tkinter Window Background Color – Python Examples