Напомню, что этот сервер не для продакшена, а для тестов и изучения.
И снова
напомню никогда не используйте eval
!
Этот сервер, так же как и форкающийся получает математические выражения и возвращает результат вычислений.
Для каждого запроса создаётся отдельная нить и эти нити никак не взаимодействуют.
#!/usr/bin/python
import errno
import threading
import socket
import logging
import time
logger = logging.getLogger('main')
BIND_ADDRESS = ('localhost', 8999)
BACKLOG = 5
def handle(sock, clinet_ip, client_port):
# обработчик, работающий в процессе-потомке
logger.info('Start to process request from %s:%d' % (clinet_ip, client_port))
# получаем все данные до перевода строки
# (это не очень честный подход, может сожрать сразу несколько строк,
# если они придут одним какетом; но для наших целей -- сойдёт)
in_buffer = b''
while not in_buffer.endswith(b'\n'):
in_buffer += sock.recv(1024)
logger.info('In buffer = ' + repr(in_buffer))
# изображаем долгую обработку
time.sleep(5)
# получаем результат
try:
result = str(eval(in_buffer, {}, {}))
except Exception as e:
result = repr(e)
out_buffer = result.encode('utf-8') + b'\r\n'
logger.info('Out buffer = ' + repr(out_buffer))
# отправляем
sock.sendall(out_buffer)
# в отличии от fork-варианта, здесь процесс не завершается
# автоматического закрытия сокета не произойдёт;
# поэтому закрываем сокет руками
sock.close()
logger.info('Done.')
def serve_forever():
# создаём слушающий сокет
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# re-use port
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(BIND_ADDRESS)
sock.listen(BACKLOG)
# слушаем и при получении нового входящего соединения,
# порождаем нить, которая будет его обрабатывать
logger.info('Listning no %s:%d...' % BIND_ADDRESS)
while True:
try:
connection, (client_ip, clinet_port) = sock.accept()
except IOError as e:
if e.errno == errno.EINTR:
continue
raise
# запускаем нить
thread = threading.Thread(
target=handle,
args=(connection, client_ip, clinet_port)
)
thread.daemon = True
thread.start()
def main():
# настраиваем логгинг
logger.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
formatter = logging.Formatter(
'%(asctime)s [%(levelname)s] [%(thread)s] %(message)s',
'%H:%M:%S'
)
ch.setFormatter(formatter)
logger.addHandler(ch)
logger.info('Run')
# запускаем сервер
serve_forever()
main()
Протокол работы может выглядеть как-то так:
13:40:50 [INFO] [140274152228608] Run
13:40:50 [INFO] [140274152228608] Listning no localhost:8999...
13:40:55 [INFO] [140274112243456] Start to process request from 127.0.0.1:53965
13:40:55 [INFO] [140274112243456] In buffer = b'2+3\n'
13:40:56 [INFO] [140274034538240] Start to process request from 127.0.0.1:53966
13:40:56 [INFO] [140274034538240] In buffer = b'800 * 999\n'
13:41:00 [INFO] [140274112243456] Out buffer = b'5\r\n'
13:41:00 [INFO] [140274112243456] Done.
13:41:01 [INFO] [140274034538240] Out buffer = b'799200\r\n'
13:41:01 [INFO] [140274034538240] Done.
Как видите, две нити работают параллельно и одновременно.