简体   繁体   English

线程化的Python TCP Client类不断调用Receiver方法并阻止Send方法

[英]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. 我正在使用以下Python客户端类。

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 data != "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, "Hello\n"))

            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. 一旦线程启动,并调用了接收处理​​程序,我将获得一些数据,如果该数据不是“ Step1”,则只需调用一个函数(另一个脚本)进行打印即可。

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. 但是,如果数据是“ step1”,则我调用一个函数,该函数将继续打印服务器接下来发送的任何数据,直到服务器发送“ Stop”消息。 At this point, I break out of the "Receive Handler", and try to send an "Ok" message to the Server. 在这一点上,我突破了“接收处理程序”,并尝试向服务器发送“ Ok”消息。

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. 如果您需要更多详细信息,请在决定无故标记我的问题之前让我知道。

However, as soon as I break out of the "Receive Handler", it automatically calls upon that function again. 但是,一旦我脱离“接收处理程序”,它就会自动再次调用该函数。

This is because you call sct.cmd_q.put(ClientCommand(ClientCommand.RECEIVE)) within the while True loop that's run through for each single chunk of data received, ie for each data before "step1" one more command to call the "Receive Handler" (which itself loops until "Stop") is put into the ClientCommand queue, and those commands are of course then executed before the SEND command. 这是因为您在while True循环中调用了sct.cmd_q.put(ClientCommand(ClientCommand.RECEIVE)) ,该循环针对接收到的每个数据块(即“ step1”之前的每个数据)运行另外一个命令来调用“接收”将“处理程序”(自身循环直到“停止”)放入ClientCommand队列中,然后当然要在SEND命令之前执行这些命令。 If you place the RECEIVE call before this while True loop, your approach can work. 如果while True循环之前将RECEIVE调用放置在此位置之前,则您的方法可以使用。

The error is 错误是

    if msgid != "step1":
NameError: name 'msgid' is not defined

Instead of 代替

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

you had better written 你最好写

    raise

and spotted it immediately. 并立即发现它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM