2014-11-29 1 views
0

У меня есть клиент и сервер, и мне нужно перенести некоторые файлы с помощью сокетов. Я могу послать небольшие сообщения, но когда я пытаюсь отправить файл, начинается проблемы ...Как закончить передачу файлов сокетов в Python?

client.py:

from socket import * 
from threading import Thread 
import sys 
import hashlib 

class Client(object): 

    ASK_LIST_FILES = "#001" # 001 is the requisition code to list 
           # all the files 
    ASK_SPECIFIC_FILE = "#002" # 002 is the requisition code to a 
           # specific file 
    SEND_FILE   = "#003" # 003 is the requisition code to send one 
           # file 
    AUTHENTICATION = "#004" # 004 is the requisition code to user 
           # authentication 

    listOfFiles = [] 

    def __init__(self): 
     try: 
      self.clientSocket = socket(AF_INET, SOCK_STREAM) 
     except (error): 
      print("Failed to create a Socket.") 
      sys.exit() 


    def connect(self, addr): 
     try: 
      self.clientSocket.connect(addr) 
     except (error): 
      print("Failed to connect.") 
      sys.exit() 

     print(self.clientSocket.recv(1024).decode()) 

    def closeConnection(self): 
     self.clientSocket.close() 

    def _askFileList(self): 
     try: 
      data = Client.ASK_LIST_FILES 
      self.clientSocket.sendall(data.encode()) 
      # self._recvFileList() 
     except (error): 
      print("Failed asking for the list of files.") 
      self.closeConnection() 
      sys.exit() 

     thread = Thread(target = self._recvFileList) 
     thread.start() 

    def _recvFileList(self): 
     print("Waiting for the list...") 
     self.listOfFiles = [] 
     while len(self.listOfFiles) == 0: 
      data = self.clientSocket.recv(1024).decode() 
      if (data): 
       self.listOfFiles = data.split(',') 
       if(len(self.listOfFiles) > 0): 
        print (self.listOfFiles) 

    def _askForFile(self, fileIndex): 

     fileIndex = fileIndex - 1 

     try: 
      data = Client.ASK_SPECIFIC_FILE + "#" + str(fileIndex) 
      self.clientSocket.sendall(data.encode()) 
     except(error): 
      print("Failed to ask for an specific file.") 
      self.closeConnection() 
      sys.exit() 

     self._downloadFile(fileIndex) 

    def _downloadFile(self, fileIndex): 
     print("Starting receiving file") 
     f = open("_" + self.listOfFiles[fileIndex], "wb+") 
     read = self.clientSocket.recv(1024) 
     # print(read) 
     # f.close 
     while len(read) > 0: 
      print(read) 
      f.write(read) 
      f.flush() 
      read = self.clientSocket.recv(1024) 
     f.flush() 
     f.close() 
     self.closeConnection() 

server.py

from socket import * 
from threading import Thread 
import sys 
import glob 

class Server(object): 

    def __init__(self): 
     try: 
      self.serverSocket = socket(AF_INET, SOCK_STREAM) 
     except (error): 
      print("Failed to create a Socket.") 
      sys.exit() 

    def connect(self, addr): 
     try: 
      self.serverSocket.bind(addr) 
     except (error): 
      print ("Failed on binding.") 
      sys.exit() 

    def closeConnection(self): 
     self.serverSocket.close() 

    def waitClients(self, num): 
     while True: 
      print("Waiting for clients...") 
      self.serverSocket.listen(num) 
      conn, addr = self.serverSocket.accept() 
      print("New client found...") 
      thread = Thread(target = self.clientThread, args = (conn,)) 
      thread.start() 

    def clientThread(self, conn): 
     WELCOME_MSG = "Welcome to the server" 
     conn.send(WELCOME_MSG.encode()) 
     while True: 
      data = conn.recv(2024).decode() 
      if(data): 
       # print(data) 
       # reply = 'OK: ' + data 
       # conn.sendall(reply.encode()) 
       if(data == "#001"): 
        listOfFiles = self.getFileList() 
        strListOfFiles = ','.join(listOfFiles) 
        self._sendFileList(strListOfFiles, conn) 
       else: 
        dataCode = data.split('#') 
        print(dataCode) 
        if(dataCode[1] == "002"): 
         print("Asking for file") 
         self._sendFile(int(dataCode[2]), conn) 
        if(dataCode[1] == "003"): 
         print("Pedido de login") 
         if self._authentication(dataCode[2]): 
          conn.send("OK".encode()) 
          # self._recvFile(conn) 
         else: 
          conn.send("FAILED".encode()) 



    def _sendFile(self, fileIndex, conn): 
     listOfFiles = self.getFileList() 
     print(fileIndex) 
     print(listOfFiles[fileIndex]) 
     f = open(listOfFiles[fileIndex], "rb") 
     read = f.read(1024) 
     while len(read) > 0: 
      conn.send(read) 
      read = f.read(1024)   
     f.close() 

    def _sendFileList(self, strList, conn): 
     try: 
      conn.sendall(strList.encode()) 
     except (error): 
      print("Failed to send list of files.") 

    def getFileList(self): 
     return glob.glob("files/*") 

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

ответ

0

Во-первых, вы делаете здесь наиболее распространенную ошибку, используя TCP: предположим, что все данные, отправленные в один адрес send(), будут получены одинаково в одном recv(). Это неверно для TCP, потому что это октет поток, а не поток сообщений. Ваш код будет работать только в идеальных (лабораторных) условиях и может загадочно провалиться в реальном мире. Вы должны либо явно изобретать границы сообщений в потоках TCP, либо переключать, например. к SCTP. Последний доступен почти повсеместно и поддерживает границы сообщений через сетевое соединение.

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

  • Префикс содержимого файла своей длиной. В этом случае клиент будет знать, сколько байтов должно быть получено для файла.
  • Отправлять содержимое файла как последовательность фрагментов, префикс каждой части его длины (только для TCP) и отмечать, последний ли этот кусок (для обоих транспортов). В качестве альтернативы специальная метка «EOF» может быть отправлена ​​без данных.

Аналогично, управляющие сообщения и их ответы должны быть снабжены префиксом длины или терминатором, который не может появляться внутри такого сообщения.

Когда вы закончите развивать это, вы будете смотреть на FTP и HTTP; оба затрагивают все проблемы, которые я описывал здесь, но принципиально разными способами.