mardi 21 juin 2016

Threaded Python TCP Client Class continuously calls Receiver method and blocks Send method


I hope the title is appropriate. If not please suggest an alternative. I am working with the following Python Client Class.

import Queue
import socket
import struct
import threading
import time

class ClientCommand(object):
    CONNECT, SEND, RECEIVE, CLOSE = range(4)

    def __init__(self, type, data=None):
        self.type = type
        self.data = data

class ClientReply(object):

    ERROR, SUCCESS = range(2)

    def __init__(self, type, data = None):
        self.type = type
        self.data = data

class SocketClientThread(threading.Thread):

    def __init__(self, cmd_q = Queue.Queue(), reply_q = Queue.Queue()):
        super(SocketClientThread, self).__init__()
        self.cmd_q = cmd_q 
        self.reply_q = reply_q
        self.alive = threading.Event()
        self.alive.set()
        self.socket = None
        #self.stopped = False

        self.handlers = {
                ClientCommand.CONNECT: self._handle_CONNECT,
                ClientCommand.CLOSE: self._handle_CLOSE,
                ClientCommand.SEND: self._handle_SEND,
                ClientCommand.RECEIVE: self._handle_RECEIVE
                }

    def run(self):
        while self.alive.isSet():
            #while not self.stopped:
            try:
                cmd = self.cmd_q.get(True, 0.1)
                self.handlers[cmd.type](cmd)

            except Queue.Empty as e:
                continue

    def stop(self):
        self.alive.clear()

    def join(self, timeout=None):
        self.alive.clear()
        threading.Thread.join(self, timeout)

    def _handle_CONNECT(self, cmd):
        try:
            self.socket = socket.socket(
                    socket.AF_INET, socket.SOCK_STREAM)
            self.socket.connect((cmd.data[0], cmd.data[1]))
            self.reply_q.put(self._success_reply())
        except IOError as e:
            self.reply_q.put(self._error_reply(str(e)))

    def _handle_CLOSE(self, cmd):
        self.socket.close()
        reply = ClientReply(ClientReply.SUCCESS)
        self.reply_q.put(reply)


    def _handle_SEND(self, cmd):
        try:

            print "about to send: ", cmd.data
            self.socket.sendall(cmd.data)
            print "sending data"
            self.reply_q.put(self._success_reply())
        except IOError as e:
            print "Error in sending"
            self.reply_q.put(self._error_reply(str(e)))


    def _handle_RECEIVE(self, cmd):
         try:
             #TODO Add check for len(data)


            flag = True
            while flag:
                print "Receiving Data"
                data = self._recv_n_bytes()

                if len(data) != '':
                    self.reply_q.put(self._success_reply(data))

                if data == "Stop":
                    print "Stop command"
                    flag = False

         except IOError as e:
             self.reply_q.put(self._error_reply(str(e)))


    def _recv_n_bytes(self):
        data = self.socket.recv(1024)
        return data

    def _error_reply(Self, errstr):
        return ClientReply(ClientReply.ERROR, errstr)

    def _success_reply(self, data = None):
        return ClientReply(ClientReply.SUCCESS, data)

My main script code -

import socket
import time
import Queue
import sys
import os

from client import *

sct = SocketClientThread()
sct.start()
host = '127.0.0.1'
port = 1234


sct.cmd_q.put(ClientCommand(ClientCommand.CONNECT, (host, port)))

try:

    while True:
        sct.cmd_q.put(ClientCommand(ClientCommand.RECEIVE))


        reply = sct.reply_q
        tmp = reply.get(True)
        data = tmp.data

        if data != None:

            if msgid != "step1":
                //call function to print something
            else:
                // call_function that prints incoming data till server stops sending data 

                print "Sending OK msg"
                sct.cmd_q.put(ClientCommand(ClientCommand.SEND, "Hellon"))

            print "Done"
        else:
            print "No Data"

except:
    #TODO Add better error handling than a print
    print "Server down"

So here is the issue. Once the thread starts, and the Receive handler is called, I get some data, if that data is not "Step1", I just call a function (another script) to print it.

However, if the data is "step1", I call a function which will then continue printing whatever data the server sends next, till the server sends a "Stop" message. At this point, I break out of the "Receive Handler", and try to send an "Ok" message to the Server.

However, as soon as I break out of the "Receive Handler", it automatically calls upon that function again. So while I am trying to send back a message, the client is again waiting for data from the server. So due to the "Receiver function" being called again, the "Send function" blocks.

I can't seem to understand how to switch between receiving and sending. What is wrong with my approach here and how should I fix this? Do I need to re-write the code to have two separate threads for sending and receiving?

If you require any more details please let me know before you decide to flag my question for no reason.


Aucun commentaire:

Enregistrer un commentaire