Pular para o conteúdo principal

📧 Comunicação entre processos: Cliente e Servidor

🎯 Objetivo de aprendizagem​

  • Compreender os mecanismos de comunicação entre processos (IPC) usando sockets.
  • Criar um servidor e um cliente em Python.
  • Identificar o papel do sistema operacional na gestão das conexões de rede.

📜 Enunciado​

Neste exercício, você vai criar uma aplicação simples de comunicação entre um cliente e um servidor usando sockets em Python.

🔧 Etapa 1 – Criar um servidor simples​

Um servidor:

  • Abre uma "porta" (chamada de port) no seu computador.
  • Aguarda uma conexão de um cliente.
  • Recebe mensagens do cliente.

Código do servidor​

Crie um arquivo chamado serveur.py e copie o código a seguir:

import socket

# Crée un socket TCP
serveur = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# L'adresse IP et le port à utiliser
hote = 'localhost'
port = 12345

# Lier le socket à l'adresse
serveur.bind((hote, port))

# Écoute jusqu'à 1 connexion
serveur.listen(1)
print("[Serveur] En attente de connexion...")

# Accepter la connexion
connexion, adresse = serveur.accept()
print(f"[Serveur] Connexion de {adresse}")

# Réception des messages
while True:
message = connexion.recv(1024).decode()
if message.lower() == "exit":
print("[Serveur] Fin de la communication.")
break
print(f"[Client] {message}")

# Fermer les connexions
connexion.close()
serveur.close()

Teste​

Este programa não faz nada enquanto um cliente não se conectar. Inicie o servidor com o comando:

python serveur.py

e conecte-se usando seu navegador acessando http://localhost:12345.

dica

Você pode parar o servidor pressionando Ctrl+C no terminal.

💬 Etapa 2 – Criar um cliente​

Um cliente:

  • Conecta-se a um servidor (aqui via localhost e a porta).
  • Envia mensagens que você digita no console.

2.1 Código do cliente​

Crie um arquivo client.py:

import socket

# Crée un socket TCP
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Se connecter au serveur
client.connect(('localhost', 12345))
print("[Client] Connecté au serveur. Tapez vos messages :")

# Lire et envoyer les messages
while True:
message = input(">>> ")
client.sendall(message.encode())
if message.lower() == "exit":
break

# Fermer la connexion
client.close()

2.2 Teste​

  1. Em um terminal: execute python serveur.py
  2. Em outro terminal: execute python client.py
  3. Troque algumas mensagens.
  4. Digite exit para encerrar.

🔁 Etapa 3 – Comunicação nos dois sentidos​

Atualmente, apenas o cliente fala e o servidor escuta. Agora vamos permitir que o servidor responda.

3.1 Usando threading para escutar e escrever ao mesmo tempo​

Crie um arquivo chamado serveur2.py e copie o código a seguir:

import socket
import threading

def recevoir(connexion):
while True:
try:
message = connexion.recv(1024).decode()
if not message or message.lower() == "exit":
print("[Client] a quitté la conversation.")
print("[Serveur >>>] ", end="", flush=True)
break
print(f"[Client] {message}")
print("[Serveur >>>] ", end="", flush=True)
except: # noqa: E722
break

def envoyer(connexion):
while True:
try:
message = input("[Serveur >>>] ")
connexion.sendall(message.encode())
if message.lower() == "exit":
break
except: # noqa: E722
break

# Création du serveur
serveur = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serveur.bind(('localhost', 12345))
serveur.listen(1)

print("[Serveur] En attente de connexion...")
connexion, adresse = serveur.accept()
print(f"[Serveur] Connecté à {adresse}")

# Démarrage des threads
thread1 = threading.Thread(target=recevoir, args=(connexion,))
thread2 = threading.Thread(target=envoyer, args=(connexion,))
thread1.start()
thread2.start()

thread1.join()
thread2.join()
connexion.close()
serveur.close()

Crie um arquivo chamado client2.py e copie o código a seguir:

import socket
import threading

def recevoir(sock):
while True:
message = sock.recv(1024).decode()
if message.lower() == "exit":
print("[Serveur] a quitté la conversation.")
print("[Client >>>] ", end="", flush=True)
break
print(f"[Serveur] {message}")
print("[Client >>>] ", end="", flush=True)

def envoyer(sock):
while True:
message = input("[Client >>>] ")
sock.sendall(message.encode())
if message.lower() == "exit":
break

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('localhost', 12345))
print("[Client] Connecté au serveur.")

# Démarrage des threads
thread1 = threading.Thread(target=recevoir, args=(client,))
thread2 = threading.Thread(target=envoyer, args=(client,))
thread1.start()
thread2.start()

thread1.join()
thread2.join()
client.close()

✅ Resultado esperado​

  1. Execute serveur2.py em um terminal.
  2. Execute client2.py em outro.
  3. Tente trocar mensagens nos dois sentidos.
  4. Encerre corretamente com exit.

Exemple de communication entre client et serveur

📝 Etapa 4 – O sistema operacional​

O sistema operacional tem um papel crucial na gestão das conexões de rede. Ele gerencia os recursos, as portas e garante que os dados sejam enviados e recebidos corretamente. O mesmo vale para as threads, que permitem executar várias tarefas em paralelo, como escutar e enviar mensagens ao mesmo tempo.

Responda às seguintes perguntas:

  • No código do servidor e do cliente, em que momento o sistema operacional está envolvido?
  • As threads são gerenciadas pelo sistema operacional (OS thread) ou pelo Python (application thread)?
  • Por que é importante fechar as conexões corretamente?
  • O que acontece se você tentar iniciar o servidor várias vezes na mesma porta sem fechá-lo?
  • O que acontece se você tentar iniciar o cliente antes do servidor?