멀티쓰레딩을 이용하여 여러 개의 클라이언트를 받을 수 있는 소켓 서버를 파이썬으로 구현해 보았습니다.
server.py
# -*- coding: utf-8 -*-
import threading
import SocketServer
import socket
import sys
class CustomException(Exception):
"""
Exception 클래스를 상속한 클래스를 만든다
"""
def __init__(self, value):
"""
생성할때 value 값을 입력 받음
"""
self.value = value
def __str__(self):
"""
생성할때 받은 value 값을 확인
"""
return self.value
def raise_exception(err_msg, ip_addr=None):
"""
예외를 발생하는 함수
"""
raise CustomException(err_msg)
def print_delimiter():
print("="*20)
class ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler):
def handle(self):
"""
클라이언트와 연결될 때 호출되는 함수
상위 클래스에는 handle() 메서드가 정의되어 있지 않기 때문에
여기서 오버라이딩을 해야함
"""
cur_thread = threading.current_thread()
print("{} was started for {}"
.format(cur_thread.getName(), self.client_address[0]))
while True:
try:
self.recv_data = self.request.recv(1024).strip()
# :/quit 를 입력하거나 데이터가 없다면 루프를 종료
if self.recv_data == ":/quit" or not self.recv_data:
print_delimiter()
raise_exception("{} was gone".format(self.client_address[0]))
except NameError as e:
print "{0} got an error : {1}".format(self.client_address[0], e)
self.request.send("Bye")
break
except CustomException as e:
print e
self.request.send("Bye")
break
print "{} wrote:".format(self.client_address[0]),
print self.recv_data
# 영어의 소문자 데이터를 receive 하면 대문자로 변환해 send
self.request.sendall(self.recv_data.upper())
print("{} was ended for {}"
.format(cur_thread.getName(), self.client_address[0]))
print_delimiter()
class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
pass
if __name__ == "__main__":
# Port 0 means to select an arbitrary unused port
HOST, PORT = "0.0.0.0", 3000
# 소켓 객체 생성
try:
server = ThreadedTCPServer((socket.gethostbyname(HOST), PORT), ThreadedTCPRequestHandler)
except socket.error, msg:
print "Bind failed. Closing..."
print "Error code: %s \nError Message: %s"\
% (str(msg[0]), msg[1])
sys.exit()
print "Socket bound on {}".format(PORT)
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)
# Exit the server thread when the main thread terminates
server_thread.daemon = True
server_thread.start()
print "Server loop running in thread:", server_thread.name
server.serve_forever()
server.shutdown()
server.server_close()
client.py
# -*- coding: utf-8 -*-
import socket
import sys
HOST, PORT = "192.168.228.128", 8080
# data = "This will be nickname" # 실제 서비스에는 닉네임을 전송하자
try:
# 소켓을 생성 (SOCK_STREAM 은 TCP 소켓을 의미)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((HOST, PORT))
except socket.error, msg:
print "Failed to create socket. Error code: %s \nError Message: %s"\
% (str(msg[0]), msg[1])
sys.exit(0)
print "Socket created"
# input 값이 :/quit 일 때까지 데이터 입력 및 전송 반복
# :/quit 를 입력하면 socket 종료
while True:
try:
data = raw_input("Input : ")
if data == ":/quit" or not data:
sock.close()
break
try:
# 서버에 연결하고 데이터를 전송
sock.sendall(data + "\n")
# 데이터를 수신하고 소켓 연결을 닫음
received = sock.recv(1024)
finally:
print "Sent: {}".format(data)
print "Received: {}".format(received)
except KeyboardInterrupt as e:
print e
data = ":/quit"
sock.close()