This program has several sockets usage errors.
1) In TCP, bytes are sent and received, not complete messages. There is no guarantee that a send () will send all the string provided, nor that recv () will receive a complete message, let alone the total bytes specified, which is only a maximum.
2) It is necessary to observe the return value of send () to see how many bytes were actually sent; if not all, you must call send () again to send the rest, usually done in a loop.
3) The same goes for recv (), but for variable-length messages, you need to decide how to detect the "end of message" - whether it's a line break, or an X number of bytes, or other criterion. There is no equivalent of the hammer saying "over" or "exchange":)
4) In both send () and recv () you need to test whether you have sent or received zero bytes. This means that the connection has been closed. In the case of send (), a return less than zero means error, which usually happens when trying to use an already closed connection. (In the recv () of the server you are already doing this check.)
5) This is not an error, it is a technique: for a server to serve multiple clients, it is necessary to throw threads (a thread taking care of each connection socket) or asynchronous programming (using select ()) .
Improved customer (not perfect, but will serve multiple customers at once, which is what you want):
from socket import *
import time
serverHost = 'localhost'
serverPort = 50007
# Adotei o caractere \n como fim de mensagem
mensagem = [b'Ola mundo da internet!\n', b'bla\n', b'ble\n']
sockobj = socket(AF_INET, SOCK_STREAM)
sockobj.connect((serverHost, serverPort))
for linha in mensagem:
time.sleep(1)
while linha:
enviado = sockobj.send(linha)
if enviado == 0:
print("Servidor fechou")
break
# corta parte da linha ja enviada
linha = linha[enviado:]
data = b''
# Adotei o caractere \n como fim de mensagem
while b'\n' not in data:
newdata = sockobj.recv(1024)
if not newdata:
print('Servidor fechou conexao')
break
data += newdata
print('Cliente recebeu:', data)
sockobj.close()
Improved server:
from socket import *
import time
import threading
def cuida_cliente(conexao, endereco):
print('Server conectado a', endereco)
aberto = True
while aberto:
data = b''
while b'\n' not in data:
newdata = conexao.recv(1024)
if not newdata:
print("Cliente fechou")
data = b''
aberto = False
break
data += newdata
if not data:
break
msg = b'Eco=>' + data + b'\n'
while msg:
enviado = conexao.send(msg)
if enviado <= 0:
print("Cliente fechou")
aberto = False
break
msg = msg[enviado:]
conexao.close()
return
meuHost = ''
minhaPort = 50007
sockobj = socket(AF_INET, SOCK_STREAM)
sockobj.bind((meuHost, minhaPort))
sockobj.listen(5)
while True:
conexao, endereco = sockobj.accept()
t = threading.Thread(target=cuida_cliente, args=(conexao, endereco))
t.start()
For completeness, I'll include a second version of the server, based on select (), which does not need threads and works completely asynchronously. Note that it is much more complex, but is considered much better.
from socket import *
import time
import select
# Estrutura que representa um cliente: [socket, buffer recv, buffer send]
clientes = []
# Retorna índice de "clientes" cujo socket seja igual ao passado
def acha_cliente(socket):
for i, cliente in enumerate(clientes):
if socket is cliente[0]:
return i
return -1
# Cria um cliente novo
def abre_cliente(conexao, endereco):
print('Server conectado a', endereco)
clientes.append([conexao, b'', b''])
# Fecha e exclui cliente com erro
def erro_cliente(socket):
i = acha_cliente(socket)
if i >= 0:
clientes[i][0].close()
del clientes[i]
return
def recv_cliente(socket):
i = acha_cliente(socket)
if i < 0:
socket.close()
return
newdata = socket.recv(1024)
if not newdata:
print("Cliente fechou")
clientes[i][0].close()
del clientes[i]
return
clientes[i][1] += newdata
if b'\n' in clientes[i][1]:
# Recebeu msg completa do cliente
# Coloca mensagem para envio no buffer de transmissão
clientes[i][2] = b'Eco=>' + clientes[i][1] + b'\n'
# Limpa buffer de recepcão
clientes[i][1] = b''
def send_cliente(socket):
i = acha_cliente(socket)
if i < 0:
socket.close()
return
# Tenta enviar todo o buffer de transmissão
enviado = socket.send(clientes[i][2])
if enviado <= 0:
print("Cliente fechou")
clientes[i][0].close()
del clientes[i]
return
# Remove parte já enviada do buffer
clientes[i][2] = clientes[i][2][enviado:]
meuHost = ''
minhaPort = 50007
sockobj = socket(AF_INET, SOCK_STREAM)
sockobj.bind((meuHost, minhaPort))
sockobj.listen(5)
while True:
ler = [sockobj]
gravar = []
erro = [sockobj]
for cliente in clientes:
# Todo cliente pode enviar dados quando quiser
ler.append(cliente[0])
erro.append(cliente[0])
if cliente[2]:
# Dados pendentes para envio ao cliente
gravar.append(cliente[0])
ler, gravar, erro = select.select(ler, gravar, erro, 10)
if not ler and not gravar and not erro:
print("Timeout")
continue
# Despacha erros
for socket in erro:
erro_cliente(socket)
# Despacha leituras
for socket in ler:
if socket is sockobj:
# Socket principal do servidor
conexao, endereco = sockobj.accept()
abre_cliente(conexao, endereco)
else:
recv_cliente(socket)
# Despacha gravações
for socket in gravar:
send_cliente(socket)