servidor - socket python
Cliente/servidor TCP con sockets, servidor que envĂa archivos a clientes, bloqueos de clientes, Python (5)
Accidentalmente olvidaste solo usar HTTP y Twisted.
Servidor:
from twisted.web.static import File
from twisted.web.resource import Resource
def resource():
resource = Resource()
resource.putChild(b"", File(u"xkcd/sandwich.png"))
return resource
Cliente:
from filepath import FilePath
from twisted.internet.task import react
from treq import get, content
def main(reactor):
d = get(b"http://localhost:8080/")
d.addCallback(content)
d.addCallback(FilePath(u"image.png").setContent)
return d
react(main, [])
Demostración del servidor:
(everything) exarkun@baryon:/tmp/demo$ twist web --class server.resource
2017-02-23T21:32:14-0500 [-] Site starting on 8080
2017-02-23T21:32:14-0500 [twisted.web.server.Site#info] Starting factory <twisted.web.server.Site instance at 0x7fd1ef81a8c0>
2017-02-23T21:32:14-0500 [twisted.application.runner._runner.Runner#info] Starting reactor...
2017-02-23T21:33:01-0500 [twisted.python.log#info] "127.0.0.1" - - [24/Feb/2017:02:33:01 +0000] "GET / HTTP/1.1" 200 21489 "-" "-"
^C
2017-02-23T21:33:05-0500 [-] Received SIGINT, shutting down.
2017-02-23T21:33:05-0500 [-] (TCP Port 8080 Closed)
2017-02-23T21:33:05-0500 [twisted.web.server.Site#info] Stopping factory <twisted.web.server.Site instance at 0x7fd1ef81a8c0>
2017-02-23T21:33:05-0500 [-] Main loop terminated.
(everything) exarkun@baryon:/tmp/demo$
Demostración del cliente:
(everything) exarkun@baryon:/tmp/demo$ ls -l image.png
ls: cannot access ''image.png'': No such file or directory
(everything) exarkun@baryon:/tmp/demo$ python client.py
(everything) exarkun@baryon:/tmp/demo$ ls -l image.png
-rwxr-xr-x 1 exarkun exarkun 21489 Feb 23 21:33 image.png
(everything) exarkun@baryon:/tmp/demo$
Si desea obtener más información sobre cómo se realiza la red basada en bucle de selección, puede leer detenidamente la implementación de Twisted .
Quiero escribir un servidor TCP simple usando sockets en Python. El servidor debe enviar la imagen al cliente conectado. El cliente debe recibir la imagen. Pero, de ahora en adelante, el cliente recibe solo la parte de la imagen y ni siquiera puedo abrirla.
El servidor es multi-cliente utilizando select, pero no es el problema aquí. Creo que el problema es con el envío de la imagen.
Quería que "el protocolo" fuera muy simple aquí.
SERVER CLIENT
GET
<----------------
IMAGE
----------------->
END OF COMMUNICATION
Por lo tanto, el cliente solo puede enviar el mensaje "GET" al servidor, y el servidor, después de obtener la cadena "GET", debe enviar inmediatamente la imagen completa al cliente. Eso es todo, la comunicación ha terminado.
server.py
#!/usr/bin/env python
import random
import socket, select
from time import gmtime, strftime
image = ''image.png''
HOST = ''127.0.0.1''
PORT = 6666
connected_clients_sockets = []
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind((HOST, PORT))
server_socket.listen(10)
connected_clients_sockets.append(server_socket)
while True:
read_sockets, write_sockets, error_sockets = select.select(connected_clients_sockets, [], [])
for sock in read_sockets:
if sock == server_socket:
sockfd, client_address = server_socket.accept()
connected_clients_sockets.append(sockfd)
else:
try:
data = sock.recv(4096)
bytes = open(image).read()
if data:
sock.send(bytes)
except:
sock.close()
connected_clients_sockets.remove(sock)
continue
server_socket.close()
cliente.py
#!/usr/bin/env python
import socket
import sys
HOST = ''127.0.0.1''
PORT = 6666
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = (HOST, PORT)
sock.connect(server_address)
try:
sock.sendall("GET")
while True:
myfile = open(''imagefromserv.png'', ''w'')
while True:
data = sock.recv(4096)
if not data:
break
myfile.write(data)
myfile.close()
finally:
sock.close()
Estoy usando Python 2.7 en el Ubuntu más nuevo.
-------------------------------------------------- -------------------------------------------------- -------------------------------- EDIT ----------------- -------------------------------------------------- -------------------------------------------------- ---------------
Siguiendo los consejos dados por uno de los usuarios en los comentarios, TRATÉ de implementar un protocolo simple:
CLIENT SERVER
GET/r/n
----------------------------------->
OK/r/n
<-----------------------------------
GET_SIZE/r/n
----------------------------------->
SIZE 1024/r/n
<-----------------------------------
GET_IMG/r/n
----------------------------------->
IMG_DATA/r/r
<-----------------------------------
Todo parece funcionar, pero después de la transferencia de imágenes, mi CPU está 100% ocupada, como dice top
. Y ....
Salida del servidor:
--GET--
--GET_SIZE--
--24518--
--GET_IMG--
Salida del cliente:
--OK--
--SIZE 24518--
--24518--
4096
8192
12288
16384
20480
24523
Image received successfully
Indica que el cliente recibió la imagen con éxito. ¿Está bien ahora? Quiero decir, obtuve la imagen del servidor, pero no sé si implementé el protocolo correctamente. Tal vez algo se puede mejorar aquí?
cliente.py:
#!/usr/bin/env python
import socket
import sys
HOST = ''127.0.0.1''
PORT = 6666
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = (HOST, PORT)
sock.connect(server_address)
fname = ''fromserver.png''
try:
sock.sendall("GET/r/n")
data = sock.recv(4096)
if data:
txt = data.strip()
print ''--%s--'' % txt
if txt == ''OK'':
sock.sendall("GET_SIZE/r/n")
data = sock.recv(4096)
if data:
txt = data.strip()
print ''--%s--'' % txt
if txt.startswith(''SIZE''):
tmp = txt.split()
size = int(tmp[1])
print ''--%s--'' % size
sock.sendall("GET_IMG/r/n")
myfile = open(fname, ''wb'')
amount_received = 0
while amount_received < size:
data = sock.recv(4096)
if not data :
break
amount_received += len(data)
print amount_received
txt = data.strip(''/r/n'')
if ''EOF'' in str(txt) :
print ''Image received successfully''
myfile.write(data)
myfile.close()
else :
myfile.write(data)
finally:
sock.close()
server.py:
#!/usr/bin/env python
import random
import socket, select
from time import gmtime, strftime
image = ''tux.png''
HOST = ''127.0.0.1''
PORT = 6666
connected_clients_sockets = []
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind((HOST, PORT))
server_socket.listen(10)
connected_clients_sockets.append(server_socket)
while True:
read_sockets, write_sockets, error_sockets = select.select(connected_clients_sockets, [], [])
for sock in read_sockets:
if sock == server_socket:
sockfd, client_address = server_socket.accept()
connected_clients_sockets.append(sockfd)
else:
try:
data = sock.recv(4096)
if data :
txt = data.strip()
print ''--%s--''%txt
if txt == ''GET'' :
sock.sendall(''OK/r/n'')
elif txt == ''GET_SIZE'' :
with open (''tux.png'',''rb'') as f1:
file_size = len(f1.read())
f1.seek(0)
print ''--%s--''%file_size
file_size = ''%s'' % file_size
sock.sendall(''SIZE %s/r/n'' % file_size)
elif txt == ''GET_IMG'' :
with open(image, ''rb'') as fp:
image_data = fp.read()
msg = ''%sEOF/r/r'' % image_data
sock.sendall(msg)
print msg
except:
sock.close()
connected_clients_sockets.remove(sock)
continue
server_socket.close()
O tal vez debería hacer:
sock.sendall(image_data)
sock.sendall(''EOF/r/n'')
en lugar de:
msg = ''%sEOF/r/n'' % image_data
sock.sendall(msg)
en cliente?
El código debajo de la sección EDITAR me parece bueno. Estoy completamente de acuerdo con @David Schwartz. Si está buscando implementar un protocolo, debe tener en cuenta muchas cosas con respecto al diseño del protocolo. Ex:
- Si envías un comando, cómo reacciona el servidor.
- que comandos son permitidos
- implementar códigos de error para las respuestas del servidor, etc.
Para eso, puede leer "Implementación de TCP / IP y Protocolo de Linux", es un gran libro para este tema.
Por otro lado, puedes seguir con lo básico y continuar usando sockets TCP. Mi humilde sugerencia es que implementes algún mecanismo para que el cliente sepa si tiene la información completa, como:
- envíe un hash de los datos, verifique el hash en el cliente
- corte la información en fragmentos y envíe la cantidad de fragmentos, luego, en el cliente, compruebe cuántos fragmentos se recibieron y vuelva a solicitar la imagen en caso de fallo
- O todo lo anterior
Esto es realmente extraño. Probé dos imágenes diferentes, y el código funcionó. El problema estaba entonces con la imagen.
Puede leer esto: https://picamera.readthedocs.io/en/release-1.12/recipes1.html y aprender mucho sobre cómo enviar fotografías a través de sockets. "Recetas básicas" le brinda todo lo que necesita.
Su cliente envía la cadena "GET". Solo desea enviar y recibir datos de imagen y "GET" no son datos de imagen.
Es posible que tenga otros errores, es difícil saberlo sin entender su protocolo. Por ejemplo, ¿cómo sabe un lado qué ha obtenido todos los datos de la imagen?