풀스택서비스 네트워킹 프로젝트로 chatting app을 만들게 되었고 중간 과정을 적어보려 한다.
<본 포스팅에 사용된 모든 자료는 이성원교수님 풀스택서비스네트워킹 강의자료 내용이다>
일단 socket api 호출 순서에 대하여 알아보자
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.core.window import Window
from kivy.uix.button import Button
from kivy.uix.scrollview import ScrollView
class Scroll(ScrollView):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.layout = GridLayout(cols=1, size_hint_y=None)
self.add_widget(self.layout)
self.chatHistory = Label(size_hint_y=None, markup=True)
self.scroll = Label()
self.layout.add_widget(self.chatHistory)
self.layout.add_widget(self.scroll)
def updateChat(self, message):
self.chatHistory.text += '\n' + message
self.layout.height = self.chatHistory.texture_size[1] + 15
self.chatHistory.height = self.chatHistory.texture_size[1]
self.chatHistory.text_size = (self.chatHistory.width * 0.98, None)
self.scroll_to(self.scroll)
class chatPage(GridLayout):
def __init__(self, **kwargs):
super(chatPage, self).__init__(**kwargs)
self.rows = 2
self.cols = 1
self.history = Scroll(height=Window.size[1]*0.9,size_hint_y = None)
self.add_widget(self.history)
self.new_message = TextInput(width=Window.size[0]*0.8, size_hint_x=None, multiline=False)
self.send = Button(text="Send")
self.send.bind(on_press=self.sendMessage)
bottom_line = GridLayout(cols=2)
bottom_line.add_widget(self.new_message)
bottom_line.add_widget(self.send)
self.add_widget(bottom_line)
def sendMessage(self,_):
message = self.new_message.text
self.new_message.text=""
if message:
self.history.updateChat(f"[color=20dddd]>:[/color] {message}")
def historyMessage(self, username, message):
self.history.updateChat(
f"[color=20dd20]{username}[/color] [color=20dddd]>:[/color] {message}")
class MyApp(App):
def build(self):
return chatPage()
if __name__ == '__main__':
MyApp().run()
아래 흰색이 입력창이고 send버튼을 누르면 history에 update되게 되어있다.
import socket
HOST = '127.0.0.1'
PORT = 65456
def main():
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as serverSocket:
try:
if serverSocket.bind((HOST, PORT)) == -1:
print('> bind() failed and program terminated')
serverSocket.close()
return
except Exception as exceptionObj:
print('> bind() failed by exception:', exceptionObj)
serverSocket.close()
return
if serverSocket.listen() == -1:
print('> listen() failed and program terminated')
serverSocket.close()
return
clientSocket, clientAddress = serverSocket.accept()
with clientSocket:
print('> client connected by IP address {0} with Port number {1}'.format(clientAddress[0], clientAddress[1]))
while True:
# [=start=]
RecvData = clientSocket.recv(1024)
print('> echoed:', RecvData.decode('utf-8'))
clientSocket.sendall(RecvData)
if RecvData.decode('utf-8') == 'quit':
break
# [==end==]
if __name__ == "__main__":
print('> echo-server is activated')
main()
print('> echo-server is de-activated')
import socket
HOST = '127.0.0.1'
PORT = 65456
def connect():
global clientSocket
clientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
clientSocket.connect((HOST, PORT))
def send(message):
message = message.encode('utf-8')
clientSocket.send(message)
1:1 통신에서는 본 main 함수에서 listen api를 사용하지 않아 지워버렸다.
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.core.window import Window
from kivy.uix.button import Button
from kivy.uix.scrollview import ScrollView
import personClient
class Scroll(ScrollView):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.layout = GridLayout(cols=1, size_hint_y=None)
self.add_widget(self.layout)
self.chatHistory = Label(size_hint_y=None, markup=True)
self.scroll = Label()
self.layout.add_widget(self.chatHistory)
self.layout.add_widget(self.scroll)
def updateChat(self, message):
self.chatHistory.text += '\n' + message
self.layout.height = self.chatHistory.texture_size[1] + 15
self.chatHistory.height = self.chatHistory.texture_size[1]
self.chatHistory.text_size = (self.chatHistory.width * 0.98, None)
self.scroll_to(self.scroll)
class chatPage(GridLayout):
def __init__(self, **kwargs):
super(chatPage, self).__init__(**kwargs)
self.rows = 2
self.cols = 1
self.history = Scroll(height=Window.size[1]*0.9,size_hint_y = None)
self.add_widget(self.history)
self.new_message = TextInput(width=Window.size[0]*0.8, size_hint_x=None, multiline=False)
self.send = Button(text="Send")
self.send.bind(on_press=self.sendMessage)
bottom_line = GridLayout(cols=2)
bottom_line.add_widget(self.new_message)
bottom_line.add_widget(self.send)
self.add_widget(bottom_line)
personClient.connect()
personClient
def sendMessage(self,_):
message = self.new_message.text
self.new_message.text=""
if message:
self.history.updateChat(f"[color=20dddd]>:[/color] {message}")
personClient.send(message)
class MyApp(App):
def build(self):
return chatPage()
if __name__ == '__main__':
MyApp().run()
실행화면
import socketserver
import threading
# {CHAT#1} Create a DB to register all client's socket information
group_queue = []
class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
def handle(self):
# Show a client connection information
print('> client connected by IP address {0} with Port number {1}'.format(self.client_address[0], self.client_address[1]))
# {CHAT#2} Import a client DB into Request Handler
global group_queue
# {CHAT#3} Register a new client connection information into a client DB
group_queue.append(self.request)
while True:
# [=start=]
RecvData = self.request.recv(1024)
if RecvData.decode('utf-8') == 'quit':
# {CHAT#4} Deregister a disconnected client from a client DB
group_queue.remove(self.request)
break
else:
# {CHAT#5} Forward a client message to whole clients (currently a broadcast)
print('> received (', RecvData.decode('utf-8'), ') and echoed to ', len(group_queue), 'clients')
for conn in group_queue:
conn.sendall(RecvData)
# [==end==]
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass
if __name__ == "__main__":
HOST, PORT = "localhost", 65456
print('> echo-server is activated')
server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
with server:
ip, port = server.server_address
# Start a thread with the server -- that thread will then start one
# more thread for each request
server_thread = threading.Thread(target=server.serve_forever)
# Set to exit the server thread when the main thread terminates, then execute the main thread
server_thread.daemon = True
server_thread.start()
print("> server loop running in thread (main thread):", server_thread.name)
# Server termination by input "quit" when all client connections are disconnected
baseThreadNumber = threading.active_count()
while True:
msg = input('> ')
if msg == 'quit':
if baseThreadNumber == threading.active_count():
print("> stop procedure started")
break
else:
print("> active threads are remained :", threading.active_count() - baseThreadNumber, "threads")
print('> echo-server is de-activated')
server.shutdown()
import socketserver
import threading
# {CHAT#1} Create a DB to register all client's socket information
group_queue = []
class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
def handle(self):
# Show a client connection information
print('> client connected by IP address {0} with Port number {1}'.format(self.client_address[0], self.client_address[1]))
# {CHAT#2} Import a client DB into Request Handler
global group_queue
# {CHAT#3} Register a new client connection information into a client DB
group_queue.append(self.request)
while True:
# [=start=]
RecvData = self.request.recv(1024)
if RecvData.decode('utf-8') == 'quit':
# {CHAT#4} Deregister a disconnected client from a client DB
group_queue.remove(self.request)
break
else:
# {CHAT#5} Forward a client message to whole clients (currently a broadcast)
print('> received (', RecvData.decode('utf-8'), ') and echoed to ', len(group_queue), 'clients')
for conn in group_queue:
conn.sendall(RecvData)
# [==end==]
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass
if __name__ == "__main__":
HOST, PORT = "localhost", 65456
print('> echo-server is activated')
server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
with server:
ip, port = server.server_address
# Start a thread with the server -- that thread will then start one
# more thread for each request
server_thread = threading.Thread(target=server.serve_forever)
# Set to exit the server thread when the main thread terminates, then execute the main thread
server_thread.daemon = True
server_thread.start()
print("> server loop running in thread (main thread):", server_thread.name)
# Server termination by input "quit" when all client connections are disconnected
baseThreadNumber = threading.active_count()
while True:
msg = input('> ')
if msg == 'quit':
if baseThreadNumber == threading.active_count():
print("> stop procedure started")
break
else:
print("> active threads are remained :", threading.active_count() - baseThreadNumber, "threads")
print('> echo-server is de-activated')
server.shutdown()
import socket
import threading
HOST = '127.0.0.1'
PORT = 65456
# {CHAT#1} Create a separate receive handler
def connect():
global clientSocket
clientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
clientSocket.connect((HOST, PORT))
return clientSocket
def listen(comming):
while True:
message = clientSocket.recv(1024)
if message.decode('utf-8') == "quit":
break
comming(message)
def startlistening(comming):
clientThread = threading.Thread(target=listen, args=(comming,))
clientThread.daemon = True
clientThread.start()
def send(message):
message = message.encode('utf-8')
clientSocket.send(message)
# [==end==]
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.core.window import Window
from kivy.uix.button import Button
from kivy.uix.scrollview import ScrollView
import client
class Scroll(ScrollView):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.layout = GridLayout(cols=1, size_hint_y=None)
self.add_widget(self.layout)
self.chatHistory = Label(size_hint_y=None, markup=True)
self.scroll = Label()
self.layout.add_widget(self.chatHistory)
self.layout.add_widget(self.scroll)
def updateChat(self, message):
self.chatHistory.text += '\n' + message
self.layout.height = self.chatHistory.texture_size[1] + 15
self.chatHistory.height = self.chatHistory.texture_size[1]
self.chatHistory.text_size = (self.chatHistory.width * 0.98, None)
self.scroll_to(self.scroll)
class chatPage(GridLayout):
def __init__(self, **kwargs):
super(chatPage, self).__init__(**kwargs)
self.rows = 2
self.cols = 1
self.history = Scroll(height=Window.size[1]*0.9,size_hint_y = None)
self.add_widget(self.history)
self.new_message = TextInput(width=Window.size[0]*0.8, size_hint_x=None, multiline=False)
self.send = Button(text="Send")
self.send.bind(on_press=self.sendMessage)
bottom_line = GridLayout(cols=2)
bottom_line.add_widget(self.new_message)
bottom_line.add_widget(self.send)
self.add_widget(bottom_line)
client.connect()
client.startlistening(self.commingMessage)
def sendMessage(self,_):
message = self.new_message.text
self.new_message.text=""
if message:
self.history.updateChat(f"[color=7e4a8f]send:[/color] {message}")
client.send(message)
def commingMessage(self,message):
self.history.updateChat(f"[color=20dddd]recieve:[/color] {message}")
class MyApp(App):
def build(self):
return chatPage()
if __name__ == '__main__':
MyApp().run()
실행화면