2015-07-30 4 views
0

У меня возникают проблемы с передачей данных по TCP с удаленным клиентом и сервером, написанным на Python. Сервер расположен в довольно удаленной области с относительно медленным подключением к Интернету (< 2 Мбит/с). Когда клиент запускается в локальной сети с сервером, вся строка передается (2350 байт); однако, когда я запускаю клиент за пределами локальной сети, иногда строка усекается (1485 байт), и иногда полная строка проходит (2350 байт). Размер усеченной строки всегда составляет 1485 байт. Полный размер строки значительно ниже установленного размера буфера для клиента и сервера.Неполная передача данных через сокет python

Я скопировал сокращенные версии клиента и сервера кода ниже, где я попытался изменить все посторонние детали:

Client

import socket 
from time import sleep 

class FTIRdataClient(): 

    def __init__(self,TCP_IP="xxx.xxx.xxx.xxx",TCP_Port=xxx,BufferSize=4096): 

     #----------------------------------- 
     # Configuration parameters of server 
     #----------------------------------- 
     self.TCP_IP   = TCP_IP 
     self.TCP_Port  = int(TCP_Port) 
     self.RECV_BUFFER  = int(BufferSize) 

    def writeTCP(self,message): 

     try: 
      sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)  
      sock.connect((self.TCP_IP,self.TCP_Port)) 
      sock.send(message) 
      incomming = sock.recv(self.RECV_BUFFER) 
      sock.close()  
     except: 
      print "Unable to connect to data server!!"   
      incomming = False 

     return incomming   

if __name__ == "__main__": 

    #---------------------------------- 
    # Initiate remote data client class 
    #---------------------------------- 
    dataClass = FTIRdataClient(TCP_IP=dataServer_IP,TCP_Port=portNum,BufferSize=4096) 

    #-------------------------------- 
    # Ask database for all parameters 
    #-------------------------------- 
    allParms = dataClass.writeTCP("LISTALL") 

Сервер

import os 
import sys 
import socket 
import select 
import smtplib 
import datetime  as  dt 


class FTIRdataServer(object): 

    def __init__(self,ctlFvars): 
     ... 

    def runServer(self): 

     self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
     self.server_socket.bind((self.TCP_IP,self.TCP_Port)) 
     #self.server_socket.setsockopt(socket.IPPROTO_TCP,socket.TCP_NODELAY,1) 
     self.server_socket.listen(10)   
     self.connection_list.append(self.server_socket) 

     #------------------------------------- 
     # Start loop to listen for connections 
     #------------------------------------- 
     while True:  

      #-------------------- 
      # Get list of sockets 
      #-------------------- 
      read_sockets,write_sockets,error_sockets = select.select(self.connection_list,[],[],5) 

      for sock in read_sockets: 

       #----------------------- 
       # Handle new connections 
       #----------------------- 
       if sock == self.server_socket: 

        #---------------------------------------------- 
        # New connection recieved through server_socket 
        #---------------------------------------------- 
        sockfd, addr = self.server_socket.accept() 

        self.connection_list.append(sockfd) 
        print "Client (%s, %s) connected" % addr 

       #------------------------------------- 
       # Handle incomming request from client 
       #------------------------------------- 
       else: 

        #------------------------ 
        # Handle data from client 
        #------------------------ 
        try: 
         data = sock.recv(self.RECV_BUFFER) 

         #------------------------------------------------ 
         # Three types of call to server: 
         # 1) set -- sets the value of a data parameter 
         # 2) get -- gets the value of a data parameter 
         # 3) write -- write data to a file 
         #------------------------------------------------ 
         splitVals = data.strip().split() 

         ... 

         elif splitVals[0].upper() == 'LISTALL': 

          msgLst = [] 

          #---------------------------- 
          # Create a string of all keys 
          # and values to send back 
          #---------------------------- 
          for k in self.dataParams: 
           msgLst.append("{0:}={1:}".format(k," ".join(self.dataParams[k]))) 

          msg = ";".join(msgLst) 

          sock.sendall(msg) 

         ... 

         else: 
          pass 

        #--------------------------------------------------- 
        # Remove client from socket list after disconnection 
        #--------------------------------------------------- 
        except: 
         sock.close() 
         self.connection_list.remove(sock) 
         continue 

     #------------- 
     # Close server 
     #------------- 
     self.closeServer() 


    def closeServer(self): 
     ''' Close the TCP data server ''' 
     self.server_socket.close() 

Ваша помощь очень признательна !!!

+0

@DustinAnderson Спасибо за ответ! Это 1485 байтов, заданных sys.getsizeof() python –

ответ

0

Для всех, кто заинтересован, я нашел решение этой проблемы. Джон Нильсен имеет довольно хорошее объяснение here. По сути, поток TCP гарантирует, что байты не будут выходить из строя или дублироваться; однако он не гарантирует, сколько групп будут отправлены данные. Поэтому нужно постоянно читать (socket.recv), пока все данные не будут отправлены. Предыдущий код работает в локальной сети, потому что сервер отправляет всю строку в одну группу. По удаленному соединению строка была разделена на несколько групп.

Я модифицировал клиента для непрерывного цикла на socket.recv(), пока сокет не будет закрыт, и я изменил сервер, чтобы немедленно закрыть сокет после отправки данных. Есть несколько других способов сделать это, упомянутые в приведенной выше ссылке. Новый код выглядит следующим образом:

Client

class FTIRdataClient(object): 

    def __init__(self,TCP_IP="xxx.xxx.xx.xxx",TCP_Port=xxxx,BufferSize=4024): 

     #----------------------------------- 
     # Configuration parameters of server 
     #----------------------------------- 
     self.TCP_IP   = TCP_IP 
     self.TCP_Port  = int(TCP_Port) 
     self.RECV_BUFFER  = int(BufferSize) 

    def setParam(self,message): 
     try: 
      sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)  
      sock.connect((self.TCP_IP,self.TCP_Port)) 
      sock.sendall("set "+message) 

      #------------------------- 
      # Loop to recieve all data 
      #------------------------- 
      incommingTotal = "" 
      while True: 
       incommingPart = sock.recv(self.RECV_BUFFER) 
       if not incommingPart: break 
       incommingTotal += incommingPart 

      sock.close()  
     except: 
      print "Unable to connect to data server!!"   
      incommingTotal = False 

     return incommingTotal 

Сервер класс FTIRdataServer (объект):

def __init__(self,ctlFvars): 

    ... 

def runServer(self): 

    self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
    self.server_socket.bind((self.TCP_IP,self.TCP_Port)) 
    #self.server_socket.setsockopt(socket.IPPROTO_TCP,socket.TCP_NODELAY,1) 
    self.server_socket.listen(10)   
    self.connection_list.append(self.server_socket) 

    #------------------------------------- 
    # Start loop to listen for connections 
    #------------------------------------- 
    while True:  

     #-------------------- 
     # Get list of sockets 
     #-------------------- 
     read_sockets,write_sockets,error_sockets = select.select(self.connection_list,[],[],5) 

     for sock in read_sockets: 

      #----------------------- 
      # Handle new connections 
      #----------------------- 
      if sock == self.server_socket: 

       #---------------------------------------------- 
       # New connection recieved through server_socket 
       #---------------------------------------------- 
       sockfd, addr = self.server_socket.accept() 

       self.connection_list.append(sockfd) 
       print "Client (%s, %s) connected" % addr 

      #------------------------------------- 
      # Handle incomming request from client 
      #------------------------------------- 
      else: 

       #------------------------ 
       # Handle data from client 
       #------------------------ 
       try: 
        data = sock.recv(self.RECV_BUFFER) 

        ...     

        elif splitVals[0].upper() == 'LISTALL': 

         msgLst = [] 

         #---------------------------- 
         # Create a string of all keys 
         # and values to send back 
         #---------------------------- 
         for k in self.dataParams: 
          msgLst.append("{0:}={1:}".format(k," ".join(self.dataParams[k]))) 

         msg = ";".join(msgLst) 

         sock.sendall(msg) 

        elif splitVals[0].upper() == 'LISTALLTS': # List all time stamps 

         msgLst = [] 

         #---------------------------- 
         # Create a string of all keys 
         # and values to send back 
         #---------------------------- 
         for k in self.dataParamTS: 
          msgLst.append("{0:}={1:}".format(k,self.dataParamTS[k])) 

         msg = ";".join(msgLst) 

         sock.sendall(msg)       

        ... 
        else: 
         pass 

        #------------------------ 
        # Close socket connection 
        #------------------------ 
        sock.close() 
        self.connection_list.remove(sock) 

       #------------------------------------------------------ 
       # Remove client from socket list if client discconnects 
       #------------------------------------------------------ 
       except: 
        sock.close() 
        self.connection_list.remove(sock) 
        continue 

    #------------- 
    # Close server 
    #------------- 
    self.closeServer() 

Безотносительно. Это, наверное, общее знание, и я немного замедлился.