title: Python TCP socket send receive large delay
body: <p>I used python socket to make a server on my Raspberry Pi 3 (Raspbian) and a client on my laptop (Windows 10). The server stream images to the laptop at a rate of 10fps, and can reach 15fps if I push it. The problem is when I want the laptop to send back a command based on the image, the frame rate drop sharply to 3fps. The process is like this:</p>
  
  <p>Pi send img => Laptop receive img => Quick process => Send command based on process result => Pi receive command, print it => Pi send img => ...</p>
  
  <p>The process time for each frame does not cause this (0.02s at most for each frame), so currently I am at a loss as to why the frame rate drop so much. The image is quite large, at around 200kB and the command is only a short string at 3B. The image is in matrix form and is pickled before sending, while the command is sent as is.</p>
  
  <p>Can someone please explain to me why sending back such a short command would make the frame rate drop so much? And if possible, a solution for this problem. I tried making 2 servers, one dedicated to sending images and one for receiving command, but the result is the same.</p>
  
  <p>Server:</p>
  
  <pre><code>import socket
  import pickle
  import time
  import cv2
  import numpy as np
  from picamera.array import PiRGBArray
  from picamera import PiCamera
  from SendFrameInOO import PiImageServer
  
  def main():
      # initialize the server and time stamp
      ImageServer = PiImageServer()
      ImageServer2 = PiImageServer()
      ImageServer.openServer('192.168.0.89', 50009)
      ImageServer2.openServer('192.168.0.89', 50002)
  
      # Initialize the camera object
      camera = PiCamera()
      camera.resolution = (320, 240)
      camera.framerate = 10 # it seems this cannot go higher than 10
                            # unless special measures are taken, which may
                            # reduce image quality
      camera.exposure_mode = 'sports' #reduce blur
      rawCapture = PiRGBArray(camera)
  
      # allow the camera to warmup
      time.sleep(1)
  
      # capture frames from the camera
      print('&lt;INFO&gt; Preparing to stream video...')
      timeStart = time.time()
      for frame in camera.capture_continuous(rawCapture, format="bgr",
                                             use_video_port = True):
          # grab the raw NumPy array representing the image, then initialize 
          # the timestamp and occupied/unoccupied text
          image = frame.array 
          imageData = pickle.dumps(image) 
          ImageServer.sendFrame(imageData) # send the frame data
  
          # receive command from laptop and print it
          command = ImageServer2.recvCommand()
          if command == 'BYE':
              print('BYE received, ending stream session...')
              break
          print(command)
  
          # clear the stream in preparation for the next one
          rawCapture.truncate(0) 
  
      print('&lt;INFO&gt; Video stream ended')
      ImageServer.closeServer()
  
      elapsedTime = time.time() - timeStart
      print('&lt;INFO&gt; Total elapsed time is: ', elapsedTime)
  
  if __name__ == '__main__': main()
  </code></pre>
  
  <p>Client:</p>
  
  <pre><code>from SupFunctions.ServerClientFunc import PiImageClient
  import time
  import pickle
  import cv2
  
  def main():
      # Initialize
      result = 'STP'
      ImageClient = PiImageClient()
      ImageClient2 = PiImageClient()
  
      # Connect to server
      ImageClient.connectClient('192.168.0.89', 50009)
      ImageClient2.connectClient('192.168.0.89', 50002)
      print('&lt;INFO&gt; Connection established, preparing to receive frames...')
      timeStart = time.time()
  
      # Receiving and processing frames
      while(1):
          # Receive and unload a frame
          imageData = ImageClient.receiveFrame()
          image = pickle.loads(imageData)        
  
          cv2.imshow('Frame', image)
          key = cv2.waitKey(1) &amp; 0xFF
  
          # Exit when q is pressed
          if key == ord('q'):
              ImageClient.sendCommand('BYE')
              break
  
          ImageClient2.sendCommand(result)
  
      ImageClient.closeClient()
  
      elapsedTime = time.time() - timeStart
      print('&lt;INFO&gt; Total elapsed time is: ', elapsedTime)
      print('Press any key to exit the program')
      #cv2.imshow('Picture from server', image)
      cv2.waitKey(0)  
  
  if __name__ == '__main__': main()
  </code></pre>
  
  <p>PiImageServer and PiImageClient:</p>
  
  <pre><code>import socket
  import pickle
  import time
  
  class PiImageClient:
      def __init__(self):
          self.s = None
          self.counter = 0
  
      def connectClient(self, serverIP, serverPort):
          self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
          self.s.connect((serverIP, serverPort))
  
      def closeClient(self):
          self.s.close()
  
      def receiveOneImage(self):
          imageData = b''
          lenData = self.s.recv(8)
          length = pickle.loads(lenData) # should be 921764 for 640x480 images
          print('Data length is:', length)
          while len(imageData) &lt; length:
              toRead = length-len(imageData)
              imageData += self.s.recv(4096 if toRead&gt;4096 else toRead)
              #if len(imageData)%200000 &lt;= 4096:
              #    print('Received: {} of {}'.format(len(imageData), length))
          return imageData
  
      def receiveFrame(self):        
          imageData = b''
          lenData = self.s.recv(8) 
          length = pickle.loads(lenData)
          print('Data length is:', length)
          '''length = 921764 # for 640x480 images
          length = 230563 # for 320x240 images'''
          while len(imageData) &lt; length:
              toRead = length-len(imageData)
              imageData += self.s.recv(4096 if toRead&gt;4096 else toRead)
              #if len(imageData)%200000 &lt;= 4096:
              #    print('Received: {} of {}'.format(len(imageData), length))
          self.counter += 1
          if len(imageData) == length: 
              print('Successfully received frame {}'.format(self.counter))                
          return imageData
  
      def sendCommand(self, command):
          if len(command) != 3:
              print('&lt;WARNING&gt; Length of command string is different from 3')
          self.s.send(command.encode())
          print('Command {} sent'.format(command))
  
  
  class PiImageServer:
      def __init__(self):
          self.s = None
          self.conn = None
          self.addr = None
          #self.currentTime = time.time()
          self.currentTime = time.asctime(time.localtime(time.time()))
          self.counter = 0
  
      def openServer(self, serverIP, serverPort):
          print('&lt;INFO&gt; Opening image server at {}:{}'.format(serverIP,
                                                              serverPort))
          self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
          self.s.bind((serverIP, serverPort))
          self.s.listen(1)
          print('Waiting for client...')
          self.conn, self.addr = self.s.accept()
          print('Connected by', self.addr)
  
      def closeServer(self):
          print('&lt;INFO&gt; Closing server...')
          self.conn.close()
          self.s.close()
          #self.currentTime = time.time()
          self.currentTime = time.asctime(time.localtime(time.time()))
          print('Server closed at', self.currentTime)
  
      def sendOneImage(self, imageData):
          print('&lt;INFO&gt; Sending only one image...')
          imageDataLen = len(imageData)
          lenData = pickle.dumps(imageDataLen)
          print('Sending image length')
          self.conn.send(lenData)
          print('Sending image data')
          self.conn.send(imageData)
  
      def sendFrame(self, frameData):
          self.counter += 1
          print('Sending frame ', self.counter)
          frameDataLen = len(frameData)
          lenData = pickle.dumps(frameDataLen)        
          self.conn.send(lenData)        
          self.conn.send(frameData)
  
      def recvCommand(self):
          commandData = self.conn.recv(3)
          command = commandData.decode()
          return command
  </code></pre>