2017-02-20 60 views
17

Я хочу написать простой TCP-сервер, используя сокеты в Python. Сервер должен отправить изображение подключенному клиенту. Клиент должен получить изображение. Но на данный момент клиент получает только часть изображения, и я даже не могу его открыть.TCP-клиент/сервер с сокетами, сервер отправки файлов клиентам, зависания клиента, Python

Сервер - это мультиклиент, использующий select, но это не проблема. Я думаю, что проблема заключается в отправке изображения.

Я хотел, чтобы «протокол» был очень простым здесь.

SERVER     CLIENT 
       GET 
     <---------------- 
       IMAGE 
     -----------------> 
     END OF COMMUNICATION 

Таким образом, клиент может отправить только сообщение «GET» на сервер, а сервер, после получения «GET» строки, следует немедленно отправить весь образ клиента. Вот и все, общение закончено.

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() 

client.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() 

Я использую Python 2.7 на новейшем Ubuntu.

--------------------------------------------- -------------------------------------------------- ------------------------------------- EDIT ------ -------------------------------------------------- -------------------------------------------------- --------------------------

Следуя советам одного из пользователей в комментариях, я TRIED реализую простой протокол:

CLIENT          SERVER 
         GET\r\n 
     -----------------------------------> 
         OK\r\n 
     <----------------------------------- 
        GET_SIZE\r\n 
     -----------------------------------> 
        SIZE 1024\r\n 
     <----------------------------------- 
        GET_IMG\r\n 
     -----------------------------------> 
        IMG_DATA\r\r 
     <----------------------------------- 

Все, кажется, работает, но после передачи изображения мой процессор на 100% занят, как говорит top. И .... выход

Сервера: выход

--GET-- 
--GET_SIZE-- 
--24518-- 
--GET_IMG-- 

клиента:

--OK-- 
--SIZE 24518-- 
--24518-- 
4096 
8192 
12288 
16384 
20480 
24523 
Image received successfully 

Указывает, что клиент получил изображение успешно. Теперь нормально? Я имею в виду, я получил изображение с сервера, но я не знаю, правильно ли я выполнил протокол. Может быть, здесь можно что-то улучшить?

client.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() 

сервер.ру:

#!/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() 

Или, может быть, я предпочел бы сделать:

sock.sendall(image_data) 
sock.sendall('EOF\r\n') 

вместо:

msg = '%sEOF\r\n' % image_data 
sock.sendall(msg) 

в клиенте?

+0

Что такое протокол, который вы используете поверх TCP для отправки и получения файлов? Если вы его не задокументировали, вам нужно - в противном случае, практически невозможно определить, делают ли клиент и сервер правильные вещи. –

+0

@DavidSchwartz: Я только хочу отправлять и получать изображения с использованием сокетов TCP - вот и все, без дополнительных протоколов. – yak

+0

Тогда ваш клиент имеет серьезную ошибку в том, что он отправляет строку «GET», которая не является частью какого-либо изображения. (Честно говоря, я могу сказать вам это с многолетнего опыта - вам действительно нужно точно документировать свой протокол. Как ограничивается запрос? Как сервер отмечает конец данных? Как клиент идентифицирует его?) –

ответ

3

Код, указанный ниже в разделе «Редактирование», мне кажется хорошим. Я полностью согласен с @David Schwartz. Если вы хотите внедрить протокол, вы должны принять во внимание множество вещей, касающихся дизайна протокола. Пример:

  • если вы отправите команду, как сервер реагирует
  • какие команды разрешено
  • реализовать коды ошибок для ответов сервера и т.д.

Для этого вы можете прочитать «TCP/IP и реализация протокола Linux », это отличная книга для такого вопроса.

В другой стороны, вы можете придерживаться основ и продолжать использовать TCP сокетов, мое скромное предложение что вы реализуете какой-то механизм для клиента, чтобы знать, если он имеет полную информацию, как:

  • отправить хэш данных, проверить хэш в клиенте
  • вырезать информацию в кусках и отправить количество кусков, затем в клиенте проверить, сколько кусков было получено, и повторно запросить изображение в случае отказа
  • или все вышеперечисленное
5

Ваш клиент отправляет строку «GET». Вы хотите отправлять и получать только данные изображения, а «GET» - это не данные изображения.

У вас могут быть другие ошибки, трудно сказать, не понимая ваш протокол. Например, как одна сторона знает, что она получила все данные изображения?

+0

Я просто подумал, что клиенту будет достаточно просто отправить GET, поэтому, когда сервер его получит, он просто отправит цельное изображение. Вот и все, и я хотел реализовать его таким образом. Однако, похоже, он не работает так, поскольку я не могу открыть изображение. – yak

+0

Когда сервер получает «это»? Итак, единственная юридическая вещь, которую нужно отправить, - это точно строка «GET»? И как помечен конец файла? Пожалуйста, * пожалуйста * документируйте свой протокол. Невозможно определить, правильно ли работает сервер, если вы не указали, что на самом деле правильно! –

11

Вы случайно забыли использовать HTTP и Twisted.

Сервер:

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 

Клиент:

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, []) 

Сервер демо:

(everything) [email protected]:/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) [email protected]:/tmp/demo$ 

Клиент демо:

(everything) [email protected]:/tmp/demo$ ls -l image.png 
ls: cannot access 'image.png': No such file or directory 
(everything) [email protected]:/tmp/demo$ python client.py 
(everything) [email protected]:/tmp/demo$ ls -l image.png 
-rwxr-xr-x 1 exarkun exarkun 21489 Feb 23 21:33 image.png 
(everything) [email protected]:/tmp/demo$ 

Если вы хотите узнать больше о том, как организована сеть с помощью loop-loop, вы можете просмотреть the Twisted implementation.

+2

Черт побери, вот что я продолжаю делать не так! :-P – daphtdazz

+0

Боюсь, что это не отвечает на мой вопрос, так как я хочу использовать только сокеты. – yak

+0

Хорошие новости - HTTP сделан из сокетов. –

-3

Это действительно странно. Я попробовал два разных изображения, и код работал. Тогда проблема была в изображении.

+0

Вы полностью упустили момент! Ваш код может работать только по везению, а иногда он будет ... пока он этого не сделает. –

+0

@DavidSchwartz: Но почему? Я не понимаю, пожалуйста, объясните – yak

+0

Прочтите мои комментарии по вашему вопросу. Вы никогда не разрабатывали протокол, так что нет никакого способа узнать, будет ли протокол работать надежно. Что бы вы посмотрели в точности? Как вы можете определить, правильно ли код правильно реализует протокол, если нет протокола? –