โปรแกรมแชท+server(LAN)

ผู้เขียนบทความ : นาย รวิพล ไชยภักดี

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. ผลการดำเนินงาน

หลักการทำงานของโปรแกรมแชท
สามารถพิมแชทโต้ตอบกันได้
สามารถส่ง Emoji หากันได้หลากหลายรูปแบบ
สามารถเปลี่ยนสีแชทหรือตัวอักษรได้หากไม่ชอบ

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

Share

You may also like...

Leave a Reply