Simple 웹 서버 개발 예제
1. 웹서버 로그 처리하기
1.1 총 페이지 뷰 수 계산하기
pageviews = 0
with open('access_log', 'r') as f:
logs = f.readlines()
for log in logs:
log = log.split()
status = log[8]
if status == '200':
pageviews += 1
print('총 페이지뷰: [%d]' %pageviews)
1.2 고유 방문자 수 계산하기
visit_ip = []
with open('access_log', 'r') as f:
logs = f.readlines()
for log in logs:
log = log.split()
ip = log[0]
if ip not in visit_ip:
visit_ip.append(ip)
print('고유 방문자수: [%d]' %len(visit_ip))
1.3 총 서비스 용량 계산하기
KB = 1024
total_service = 0
with open('access_log', 'r') as f:
logs = f.readlines()
for log in logs:
log = log.split()
servicebyte = log[9]
if servicebyte.isdigit():
total_service += int(servicebyte)
total_service /= KB
print('총 서비스 용량: %dKB' %total_service)
1.4 사용자별 서비스 용량 계산하기
services = {}
with open('access_log', 'r') as f:
logs = f.readlines()
for log in logs:
log = log.split()
ip = log[0]
servicebyte = log[9]
if servicebyte.isdigit():
servicebyte = int(servicebyte)
else:
servicebyte = 0
if ip not in services:
services[ip] = servicebyte
else:
services[ip] += servicebyte
ret = sorted(services.items(), key=lambda x: x[1], reverse=True)
print('사용자IP – 서비스용량')
for ip, b in ret:
print('[%s] – [%d]' %(ip, b))
2. 에코 서버 만들기(1)
- 에코 서버
- 네트워크로 메시지를 수신하여 송신자에게 수신한 메시지를 귿로 돌려보내는 서버
- 네트워크 통신의 기본이 되는 소켓 프로그래밍을 접해보자
- 네트워크 소켓
- 네트워크 통신에 있어서 시작점이자 끝점
- 클라이언트-서버 프로그램의 가장 핵심이 되는 모듈
- 서버와 클라이언트는 각자의 네트워크 소켓을 가지고 있음
- 네트워크 통신을 위해 사용되는 프로토콜의 종류에 따라 TCP/UDP/Raw 소켓으로 구분됨
- TCP 소켓:
- TCP(Transmission Control Protocol)를 활용하는 네트워크 소켓
- TCP
- 연결 지향적 프로토콜
- 포트 번호를 이용하여 서비스를 식별
- 데이터의 신뢰성 있는 전송을 보장함
- 데이터의 순서, 무결성, 신뢰성을 보장함
- 주로 웹, 메일, 파일 공유와 같이 데이터 누락을 허용하지 않는 서비스에서 사용됨
- UDP 소켓:
- UDP(User Datagram Protocol)를 활용하는 네트워크 소켓
- UDP
- 비연결 지향적 프로토콜
- 포트 번호를 이용하여 서비스를 식별
- 빠른 데이터 전송을 목적으로함
- 연결 설정 과정 없이 데이터를 전송
- 데이터의 순서나 무결성은 보장하지 않음
- VoIP (Voice over IP)와 같이 속도가 중요한 서비스에서 사용됨
- Raw 소켓:
- 특정한 프로토콜에 대한 전송 계층 포매팅 없이 인터넷 프로토콜 패킷을 직접 주고 받을 수 있는 인터넷 소켓
- 데이터를 전송할 때 직접 프로토콜 헤더를 만들어 전송하고, 데이터를 수신할 때도 프로토콜 헤더를 포함하여 수신
- 라우터나 네트워크 장비 등에 주로 활용되며 사용자 공간에서 새로운 전송 계층 프로토콜을 구현하는 데에도 활용됨
- TCP 소켓:

2.1 서버
#//file: "echo_server1.py"
import socket
HOST = ''
PORT = 9009
def runServer():
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.bind((HOST, PORT))
sock.listen(1)
print('클라이언트 연결을 기다리는 중..')
conn, addr = sock.accept()
with conn:
print('[%s]와 연결됨' %addr[0])
while True:
data = conn.recv(1024)
if not data:
break
print('메시지 수신 [%s]' %data.decode())
conn.sendall(data)
runServer()
2.2 클라이언트
#//file: "echo_client1.py"
import socket
HOST = 'localhost'
PORT = 9009
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.connect((HOST, PORT))
msg = input('메시지 입력: ')
sock.sendall(msg.encode())
data = sock.recv(1024)
print('에코 서버로부터 받은 데이터 [%s]' %data.decode())
3. 에코 서버 만들기(2)
3.1 서버
#//file: "echo_server2.py"
import socketserver
HOST = ''
PORT = 9009
class MyTcpHandler(socketserver.BaseRequestHandler):
# 이 클래스는 서버 하나당 단한번 초기화됩니다.
# handle() 메쏘드에 클라이언트 연결 처리를 위한 로직을 구현합니다.
def handle(self):
print('[%s] 연결됨' %self.client_address[0])
try:
while True:
self.data = self.request.recv(1024)
if self.data.decode() == '/quit':
print('[%s] 사용자에 의해 중단' %self.client_address[0])
return
print('[%s]' %self.data.decode())
self.request.sendall(self.data)
except Exception as e:
print(e)
def runServer():
print('+++ 에코 서버를 시작합니다.')
print('+++ 에코 서버를 끝내려면 Ctrl-C를 누르세요.')
try:
server = socketserver.TCPServer((HOST, PORT), MyTcpHandler)
server.serve_forever()
except KeyboardInterrupt:
print('--- 에코 서버를 종료합니다.')
runServer()
3.2 클라이언트
#//file: "echo_client2.py"
import socket
HOST = 'localhost'
PORT = 9009
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.connect((HOST, PORT))
while True:
msg = input('메시지 입력: ')
if msg == '/quit':
sock.sendall(msg.encode())
break
sock.sendall(msg.encode())
data = sock.recv(1024)
print('에코 서버로부터 받은 데이터 [%s]' %data.decode())
print('클라이언트 종료')
4. 파일 전송 프로그램 만들기
4.1 파일 송신 프로그램
#//file: "file_server.py"
import socketserver
from os.path import exists
HOST = ''
PORT = 9009
class MyTcpHandler(socketserver.BaseRequestHandler):
def handle(self):
data_transferred = 0
print('[%s] 연결됨' %self.client_address[0])
filename = self.request.recv(1024)
filename = filename.decode()
if not exists(filename):
return
print('파일 [%s] 전송 시작...' %filename)
with open(filename, 'rb') as f:
try:
data = f.read(1024)
while data:
data_transferred += self.request.send(data)
data = f.read(1024)
except Exception as e:
print(e)
print('전송완료[%s], 전송량[%d]' %(filename, data_transferred))
def runServer():
print('+++ 파일 서버를 시작합니다.')
print('+++ 파일 서버를 끝내려면 Ctrl-C를 누르세요.')
try:
server = socketserver.TCPServer((HOST, PORT), MyTcpHandler)
server.serve_forever()
except KeyboardInterrupt:
print('--- 파일 서버를 종료합니다.')
runServer()
4.2 파일 수신 프로그램
#//file: "file_client.py"
import socket
HOST = 'localhost'
PORT = 9009
def getFileFromServer(filename):
data_transferred = 0
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.connect((HOST, PORT))
sock.sendall(filename.encode())
data = sock.recv(1024)
if not data:
print('파일[%s]: 서버에 존재하지 않거나 전송중 오류발생' %filename)
return
with open('download/'+filename, 'wb') as f:
try:
while data:
f.write(data)
data_transferred += len(data)
data = sock.recv(1024)
except Exception as e:
print(e)
print('파일 [%s] 전송종료. 전송량 [%d]' %(filename, data_transferred))
filename = input('다운로드 받을 파일이름을 입력하세요: ')
getFileFromServer(filename)
5. 채팅 서비스 만들기
5.1 채팅 서버 만들기
#//file: "chat_server.py"
import socketserver
import threading
HOST = ''
PORT = 9009
lock = threading.Lock()
class UserManager:
def __init__(self):
self.users = {}
def addUser(self, username, conn, addr):
if username in self.users:
conn.send('이미 등록된 사용자입니다.\n'.encode())
return None
# 새로운 사용자를 등록함
lock.acquire()
self.users[username] = (conn, addr)
lock.release()
self.sendMessageToAll('[%s]님이 입장했습니다.' %username)
print('+++ 대화 참여자 수 [%d]' %len(self.users))
return username
def removeUser(self, username):
if username not in self.users:
return
lock.acquire()
del self.users[username]
lock.release()
self.sendMessageToAll('[%s]님이 퇴장했습니다.' %username)
print('--- 대화 참여자 수 [%d]' %len(self.users))
def messageHandler(self, username, msg):
if msg[0] != '/':
self.sendMessageToAll('[%s] %s' %(username, msg))
return
if msg.strip() == '/quit':
self.removeUser(username)
return -1
def sendMessageToAll(self, msg):
for conn, addr in self.users.values():
conn.send(msg.encode())
class MyTcpHandler(socketserver.BaseRequestHandler):
userman = UserManager()
def handle(self):
print('[%s] 연결됨' %self.client_address[0])
try:
username = self.registerUsername()
msg = self.request.recv(1024)
while msg:
print(msg.decode())
if self.userman.messageHandler(username, msg.decode()) == -1:
self.request.close()
break
msg = self.request.recv(1024)
except Exception as e:
print(e)
print('[%s] 접속종료' %self.client_address[0])
self.userman.removeUser(username)
def registerUsername(self):
while True:
self.request.send('로그인ID:'.encode())
username = self.request.recv(1024)
username = username.decode().strip()
if self.userman.addUser(username, self.request, self.client_address):
return username
class ChatingServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass
def runServer():
print('+++ 채팅 서버를 시작합니다.')
print('+++ 채텅 서버를 끝내려면 Ctrl-C를 누르세요.')
try:
server = ChatingServer((HOST, PORT), MyTcpHandler)
server.serve_forever()
except KeyboardInterrupt:
print('--- 채팅 서버를 종료합니다.')
server.shutdown()
server.server_close()
runServer()
5.2 채팅 클라이언트 만들기
#//file: "chat_client.py"
import socket
from threading import Thread
HOST = 'localhost'
PORT = 9009
def rcvMsg(sock):
while True:
try:
data = sock.recv(1024)
if not data:
break
print(data.decode())
except:
pass
def runChat():
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.connect((HOST, PORT))
t = Thread(target=rcvMsg, args=(sock,))
t.daemon = True
t.start()
while True:
msg = input()
if msg == '/quit':
sock.send(msg.encode())
break
sock.send(msg.encode())
runChat()