No semestre passado da faculdade cursei a disciplina de Redes II. Nessa matéria uma das formas de avaliação é sempre composta de um trabalho prático juntamente com os devidos relatórios. No meu semestre o trabalho consituia em desenvolver um servidor TCP concorrente que atenda à N conexões, podendo ser desenvolvido em qualquer linguagem. Eu fiz o trabalho em dupla com o Thiago Mendes e nós optamos por fazer o nosso em Python. Depois da explicação toda, segue abaixo a nossa implementação:
Servidor
Esse arquivo implementa a classe Server, cuja finalidade é ouvir a porta definida em PORT e criar processos filhos (aka fork) para as requisições recebidas. Depois de criados os sub-processos, trabalha como uma espécie “chat” entre cliente e servidor transmitindo mensagens de um para o outro.
Obs.: Note que como esse programa era para fins acadêmicos, ele está cheio de comentários explicando o comportamento do mesmo.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import sys
from socket import *
from settings import *
class Server:
__addr = (HOST, PORT) # endereço de funcionamento do servidor
__sock = None # instancia do socket para o processo pai
__conn = None # instancia do socket para os processos filhos
__client = 0 # numero do cliente que está sendo atendido
def __init__(self):
try:
print ' * Inicializando o servidor TCP'
print ' * Instancinado o socket do protocolo TCP'
self.__sock = socket(AF_INET, SOCK_STREAM) # instancia o socket
except:
print 'ERRO: Não foi possível instanciar o socket'
sys.exit(1)
def __del__(self):
self.__sock.close() # fecha o socket utilizado pelo processo pai
del(self.__sock)
def __connect(self):
try:
print ' * Alocando porta local ' + str(self.__addr[1])
self.__sock.bind(self.__addr) # reserva a porta local
self.__sock.listen(5) # define o tamanho da fila de espera
except:
print 'ERRO: Não foi possível alocar a porta local', self.__addr[1]
sys.exit(1)
def run(self):
self.__connect() # instancia o socket e aloca uma porta local
while 1:
try: # isso evita que seja exibida a mensagem de erro quando terminar o servidor com CTRL+C
self.__conn, self.__addr = self.__sock.accept() # aceita a requisição do cliente
except:
sys.exit(1)
self.__client += 1 # incrementa o valor para o novo cliente que está sendo atendido
print ' * Requisição recebida de', self.__addr[0] + ':' + str(self.__addr[1])
print ' * Criando processo filho para antender cliente', self.__client
if os.fork(): # cria um processo filho para atender a nova requisição
self.__conn.close() # fecha a conexão com o processo filho
else:
data = None
while data != "quit": # executa enquanto o cliente estiver executando
data = self.__conn.recv(BUFSIZ) # recebe a mensagemd o cliente
self.__conn.send(data) # envia a mensagem de volta, confirmando o recebimento
print " * Recebida a mensagem '" + data + "' do cliente", self.__client
self.__conn.close() # fecha a conexão do processo filho
print ' * Encerrando conexão com o cliente', self.__client
sys.exit(1)
if __name__ == "__main__":
server = Server()
server.run()
Cliente
Esse arquivo implementa a classe Client, cuja finalidade é estabelecer conexões com o servidor definido em HOST e PORT. Depois de estabelecido o canal de comunicação, o programa executa trabalha como uma espécie “chat” trocando mensagens com o servidor, o programa é encerrado quando a mensagem “quit” é enviada pelo cliente.
Obs.: Assim como o servidor, este programa está cheio de comentários explicando tudo o que acontece.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from socket import *
from settings import *
class Client:
__addr = (HOST, PORT) # endereço de funcionamento do servidor
__sock = None # instancia do socket para o processo pai
def __init__(self):
try:
print ' * Inicializando o cliente TCP'
print ' * Instancinado o socket do protocolo TCP'
self.__sock = socket(AF_INET, SOCK_STREAM) # instancia o socket
except:
print 'ERRO: Não foi possível instanciar o socket'
exit(1)
def __def__(self):
self.__sock.close() # fecha o socket utilizado pelo processo
del(self.__sock)
def __connect(self):
try:
print ' * Conectando com o servidor em', self.__addr[0] + ':' + str(self.__addr[1])
self.__sock.connect(self.__addr) # cria a conexao
except:
print 'ERRO: Não foi possível conectar com o servidor'
exit(1)
def run(self):
self.__connect() # instancia o socket e conecta com o servidor
data = None
while data != "quit": # executa até quando o usuário desejar sair
print ' >>>',
data = str(raw_input()) # recebe a mensagem do cliente
self.__sock.send(data) # envia a mensagem para o servidor
data = self.__sock.recv(BUFSIZ) # recebe confirmação do envio
print ' * Encerrando conexão com o servidor'
if __name__ == "__main__":
client = Client()
client.run()
Configuração
Define as configurações da aplicação. Cliente e servidor irão rodar em em localhost e o servidor atenderá requisições na porta 5002.
HOST = 'localhost'
PORT = 5002
BUFSIZ = 1024
Log do sistema operacional no servidor
Log do sistema operacional com o servidor em funcionamento. As primeiras versões do trabalho deixavam alguns processos filhos abertos mesmo depois de finalizado o servidor, o que foi corrigido e explicitado abaixo.
# antes de iniciar todos os processos
afurlan@isengard:~/redes2$ ps ux | grep python | grep server
afurlan@isengard:
# iniciado o servidor
afurlan@isengard:~/redes2$ ps ux | grep python | grep server
afurlan 9657 0.5 0.6 6048 3360 pts/0 S+ 12:02 0:00 /usr/bin/python ./server
afurlan@isengard:
# iniciado o cliente 1
afurlan@isengard:~/redes2$ ps ux | grep python | grep server
afurlan 9657 0.2 0.6 6048 3384 pts/0 S+ 12:02 0:00 /usr/bin/python ./server
afurlan 9663 0.0 0.3 6048 1548 pts/0 S+ 12:02 0:00 /usr/bin/python ./server
afurlan@isengard:
# iniciado o cliente 2
afurlan@isengard:~/redes2$ ps ux | grep python | grep server
afurlan 9657 0.1 0.6 6048 3384 pts/0 S+ 12:02 0:00 /usr/bin/python ./server
afurlan 9663 0.0 0.3 6048 1548 pts/0 S+ 12:02 0:00 /usr/bin/python ./server
afurlan 9671 0.0 0.3 6048 1548 pts/0 S+ 12:02 0:00 /usr/bin/python ./server
afurlan@isengard:
# encerrado o cliente 1
afurlan@isengard:~/redes2$ ps ux | grep python | grep server
afurlan 9657 0.1 0.6 6048 3384 pts/0 S+ 12:02 0:00 /usr/bin/python ./server
afurlan 9671 0.0 0.3 6048 1548 pts/0 S+ 12:02 0:00 /usr/bin/python ./server
afurlan@isengard:
# encerrado o cliente 2
afurlan@isengard:~/redes2$ ps ux | grep python | grep servr
afurlan 9657 0.0 0.6 6048 3384 pts/0 S+ 12:02 0:00 /usr/bin/python ./server
afurlan@isengard:
# encerrado o servidor
afurlan@isengard:~/redes2$ ps ux | grep python | grep server
afurlan@isengard:~/redes2$
O Danilo Cesar cursou essa matéria comigo e também desenvolveu uma versão desse servidor, portanto encham o saco para ele postar a sua porque que ficou bem bacana :)
Clique aqui para baixar um tarball contendo todos os arquivos acima (com excessão do log).



One Trackback/Pingback
[...] prestes a me formar, e lembrando-me de um convite do Arthur Furlan, decidi que estava na hora de liberar alguns trabalinhos legais que escrevi na [...]
Post a Comment