기본 코드에 조금 더 살을 붙여서 CLI환경에서 단채 채팅방 환경을 구축했다.
아직 최적화시킬 부분도 남았고, slack처럼 늦게 들어와도 채팅방의 기록을 볼 수 있게 구축하고 싶기 때문에 시간이 남으면 세 번째 작업도 진행할 예정이다.
서버 코드
import socket
import argparse
import threading
import time
host = "127.0.0.1"
port = 4000
user_list = {}
notice_flag = 0
def msg_func(msg):
print(msg)
for con in user_list.values():
try:
con.send(msg.encode('utf-8'))
except:
print("연결이 비 정상적으로 종료된 소켓 발견")
def handle_receive(client_socket, addr, user):
msg = "---- %s님이 들어오셨습니다. ----"%user
msg_func(msg)
while 1:
data = client_socket.recv(1024)
string = data.decode('utf-8')
if "/종료" in string:
msg = "---- %s님이 나가셨습니다. ----"%user
#유저 목록에서 방금 종료한 유저의 정보를 삭제
del user_list[user]
msg_func(msg)
break
string = "%s : %s"%(user, string)
msg_func(string)
client_socket.close()
def handle_notice(client_socket, addr, user):
pass
def accept_func():
#IPv4 체계, TCP 타입 소켓 객체를 생성
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#포트를 사용 중 일때 에러를 해결하기 위한 구문
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
#ip주소와 port번호를 함께 socket에 바인드 한다.
#포트의 범위는 1-65535 사이의 숫자를 사용할 수 있다.
server_socket.bind((host, port))
#서버가 최대 5개의 클라이언트의 접속을 허용한다.
server_socket.listen(5)
while 1:
try:
#클라이언트 함수가 접속하면 새로운 소켓을 반환한다.
client_socket, addr = server_socket.accept()
except KeyboardInterrupt:
for user, con in user_list:
con.close()
server_socket.close()
print("Keyboard interrupt")
break
user = client_socket.recv(1024).decode('utf-8')
user_list[user] = client_socket
#accept()함수로 입력만 받아주고 이후 알고리즘은 핸들러에게 맡긴다.
notice_thread = threading.Thread(target=handle_notice, args=(client_socket, addr, user))
notice_thread.daemon = True
notice_thread.start()
receive_thread = threading.Thread(target=handle_receive, args=(client_socket, addr,user))
receive_thread.daemon = True
receive_thread.start()
if __name__ == '__main__':
#parser와 관련된 메서드 정리된 블로그 : https://docs.python.org/ko/3/library/argparse.html
#description - 인자 도움말 전에 표시할 텍스트 (기본값: none)
#help - 인자가 하는 일에 대한 간단한 설명.
parser = argparse.ArgumentParser(description="\nJoo's server\n-p port\n")
parser.add_argument('-p', help="port")
args = parser.parse_args()
try:
port = int(args.p)
except:
pass
accept_func()
기본 코드에서 살을 더 붙여 좀 더 채팅방 느낌이 날 수 있도록 개발을 진행했다.
전송에 필요한 로직을 묶어 하나의 함수로 정의하여 조금 더 깔끔하게 코드를 짜려고 노력했다. (msg_func)
handle_notice함수는 추후에 채팅 서버에서 공지를 할 수 있는 기능을 추가하기 위해 만들었다.
딕셔너리 user_list를 계속 갱신하며 접속중인 유저들의 이름과 socket정보를 유지해준다.
클라이언트 코드
import socket
import argparse
import threading
#접속하고 싶은 ip와 port를 입력받는 클라이언트 코드를 작성해보자.
# 접속하고 싶은 포트를 입력한다.
port = 4000
def handle_receive(lient_socket, user):
while 1:
try:
data = client_socket.recv(1024)
except:
print("연결 끊김")
break
data = data.decode('utf-8')
if not user in data:
print(data)
def handle_send(client_socket):
while 1:
data = input()
client_socket.send(data.encode('utf-8'))
if data == "/종료":
break
client_socket.close()
if __name__ == '__main__':
#parser와 관련된 메서드 정리된 블로그 : https://docs.python.org/ko/3/library/argparse.html
#description - 인자 도움말 전에 표시할 텍스트 (기본값: none)
#help - 인자가 하는 일에 대한 간단한 설명.
#nargs - 소비되어야 하는 명령행 인자의 수. -> '+'로 설정 시 모든 명령행 인자를 리스트로 모음 + 없으면 경고
#required - 명령행 옵션을 생략 할 수 있는지 아닌지 (선택적일 때만).
parser = argparse.ArgumentParser(description="\nJoo's client\n-p port\n-i host\n-s string")
parser.add_argument('-p', help="port")
parser.add_argument('-i', help="host", required=True)
parser.add_argument('-u', help="user", required=True)
args = parser.parse_args()
host = args.i
user = str(args.u)
try:
port = int(args.p)
except:
pass
#IPv4 체계, TCP 타입 소켓 객체를 생성
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 지정한 host와 prot를 통해 서버에 접속합니다.
client_socket.connect((host, port))
client_socket.send(user.encode('utf-8'))
receive_thread = threading.Thread(target=handle_receive, args=(client_socket, user))
receive_thread.daemon = True
receive_thread.start()
send_thread = threading.Thread(target=handle_send, args=(client_socket,))
send_thread.daemon = True
send_thread.start()
send_thread.join()
receive_thread.join()
클라이언트 코드 또한 조금 더 채팅방 느낌이 날 수 있도록 개발을 진행했다.
서버에서 최대한 많은 일을 수행하고 클라이언트는 가볍게 유지하려고 노력했다.
결과
좌측 상단 - 서버
우측 상단 - 클라이언트1
좌측 하단 - 클라이언트2
우측 하단 - 클라이언트3
서버에는 항상 채팅 기록을 보여주고, 각 클라이언트는 자신의 메시지를 제외한 다른 사람의 메시지만 받게 된다.
클라이언트들의 접속과 종료를 알려주는 기능도 추가했다.
'Toy_Project > Python multi thread TCP server' 카테고리의 다른 글
[Python] 파이썬 멀티 스레드 채팅 서버 만들기 - 1 (5) | 2019.11.20 |
---|