首页 > 解决方案 > 如何欺骗 Thread.start() 在 python 中多次运行

问题描述

介绍

我正在构建一个 p2pvideo 通话应用程序。

从接到电话的一方

我让kivy主循环作为一个线程运行,另一个线程slistner_bind_thread.start()(一个socket.bind函数)监听调用并返回一个拾取报价或拒绝报价,如果它拾取报价它启动另一个线程sstream_bind_thread .start() 哪个 cv2capture 和打包发送提要(socket.bind)。

从打电话的那一侧

我有与另一个线程 scall_CON_thread.start() (socket.connect) 的线程运行相同的 kivy 主循环,该线程以呼叫按钮开始,如果它被拾取报价,则等待对方的取货报价或拒绝调用另一个线程 feed_CON_thread.start() 来审查 opencv2 数据包。

问题

当我拨打电话时,一切都按预期工作,但是当挂断电话并再次拨打电话时,我明白线程不能多次启动。如果我尝试使用其他任何东西,则 gui 显然会被窃听。我有一些想法,但我不知道它们是否可行,如果有人可以提出建议,请至少发表评论。

这是main.py

import socket 
import gi
gi.require_version('Gst', '1.0')
import kivy
from kivy.uix.gridlayout import GridLayout
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget 
from kivy.uix.textinput import TextInput
from kivy.clock import Clock
from kivy.uix.image import Image
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from kivy.core.window import Window
from kivy.config import Config
from kivy.lang import Builder
from kivy.properties import StringProperty, ObjectProperty, NumericProperty, ReferenceListProperty
from kivy.graphics.texture import Texture
from kivy.core.camera import Camera
from kivy.graphics import *
import time
import os
from pathlib import Path 
import cv2                                          
import struct
import threading
import pickle
Builder.load_file('the.kv')

  ##########go to line 233 and 240

################################ GLOBAL VARIABLAS  BEGIN  #######################
                ########    PORTS   ############
sfeed_CON_port = 2430
slistner_bind_port = 1330
sstream_bind_port = 2430
scall_CON_port = 1330

            ###################### ################# #########

action = ''
endcall = False
endcall_string = '' 
myip = ''
sfeed_cv_frame_recieve = None
incall_add = 'nocalls'
outcall_add = ''


################################ GLOBAL VARIABLS END #########################



def feedconnect():
    global outcall_add, sfeed_CON_port, endcall  ########### addr conn from listenr
    global sfeed_cv_frame_recieve
    try:
        sfeed_CON_ad = outcall_add
        sfeed_CON = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sfeed_CON.connect((sfeed_CON_ad, sfeed_CON_port))

        data = b""
        payload_size = struct.calcsize("Q")
        while endcall == False:
            while len(data) < payload_size:
                packet = sfeed_CON.recv(4*1024)
                if not packet: break
                data+=packet
            packed_msg_size = data[:payload_size]
            data =data[payload_size:]
            msg_size = struct.unpack("Q", packed_msg_size)[0]

            while len(data) < msg_size:
                data+=sfeed_CON.recv(4*2024)
            frame_data = data[:msg_size]
            data =data[msg_size:]
            frame = pickle.loads(frame_data)

            sfeed_cv_frame_recieve = frame

            key = cv2.waitKey(1) & 0xFF     
            if endcall == True:
                print(endcall_string + '  closing feedconnect() socket')
                sfeed_CON.close()
                break
        sfeed_CON.close()
    except Exception as e:
        print("client: " + outcall_add + 'disconnected ')


sfeed_CON_thread = threading.Thread(target = feedconnect)   ####### THREAD

def callconnect():
    global outcall_add, scall_CON_port

    scall_CON_ad = outcall_add
    scall_CON = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    scall_CON.connect((scall_CON_ad, scall_CON_port))

    print('callconnect is waiting for an answer from  :' + scall_CON_ad + ' on port: ' + str(scall_CON_port))   
    time.sleep(15)

    answer = scall_CON.recv(4*1024)
    decoded_answer = answer.decode("utf-8")

    if decoded_answer == 'decilned':
            print(decoded_answer)

    elif decoded_answer == 'picked up' :
            print(decoded_answer)

            f = True       ############  to check
            #feedconnect()
            sfeed_CON_thread.start()
            print(f)
    else:
            print(decoded_answer)

    scall_CON.close()

scall_CON_thread = threading.Thread(target = callconnect) #### THREAD

def streambind():
    global myip, sstream_bind_port

    sstream_bind_ad = myip
    sstream_bind = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sstream_bind.bind((sstream_bind_ad, sstream_bind_port))
    print('myip is : ' + myip + ' from streambind() ' )
    sstream_bind.listen(5)

    sstream_bind_sclient, sstream_bind_sclient_ad = sstream_bind.accept()
    
    if sstream_bind_sclient:
        opcv_vid = cv2.VideoCapture(0)
        while (opcv_vid.isOpened()):
            try:
                img, frame = opcv_vid.read()
                pickled_frame = pickle.dumps(frame)
                struct_pickled_frame = struct.pack("Q", len(pickled_frame)) + pickled_frame
                sstream_bind_sclient.sendall(struct_pickled_frame)
                key = cv2.waitKey(1) & 0xFF
                if key == ord('q'):
                    slistner_bind_sclient.close()
            except:
                endcall = True
                endcall_string ='endcall'
                print(endcall_string, endcall)
                sstream_bind.close()
                
                break
                #sstream_bind_sclient.close()      ############ hint i am closing only the client

sstream_bind_thread = threading.Thread(target = streambind)    #### THREAD

def listnerbind():
    global myip, slistner_bind_port, incall_add
    global action
    host_name = socket.gethostname()
    myip = socket.gethostbyname(host_name + ".local")
    slistner_bind_ad = myip

    slistner_bind = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    slistner_bind.bind((slistner_bind_ad, slistner_bind_port))
    slistner_bind.listen(10)
    print('listnerbind() on thread')
    print('listning for calls in: ' + slistner_bind_ad + ' on port: ' + str(slistner_bind_port))

    while True:
        slistner_bind_sclient, slistner_bind_sclient_ad = slistner_bind.accept()
        ###### block note start ###
        #### the incall_add should be a list for multiple incalls at once #####
        incall_add = str(slistner_bind_sclient_ad[0])
        ###### block note end ####
        print('incall from: ' + incall_add + ' at port: ' +  str(slistner_bind_port))
        time.sleep(15)  #####  needs to be edited later #########
        if action == '':
            slistner_bind_sclient.send(bytes('no answer',"utf-8"))
        elif action == 'picked up':
            sstream_bind_thread.start()
            slistner_bind_sclient.send(bytes(action,"utf-8"))
        elif action == 'declined':
            slistner_bind_sclient.send(bytes(action,"utf-8"))

slistner_bind_thread = threading.Thread(target = listnerbind)

class fscreen(Widget):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
    def on_touch_down(self, touch):
        if touch.x !=0:
            theapp.screenm.current = "secondscreen" 

class secscreen(Widget):
    waiter = StringProperty()
    callerid = StringProperty()
    def __init__(self,**kwargs):
        super().__init__(**kwargs)
        global outcall_add
        Clock.schedule_interval(self.callcheck, 0.5)     

    def callcheck(self, *args):
        global incall_add 
        self.waiter = incall_add
        self.callerid = incall_add

    def decline(self):
        global action 
        action = "declined"

    def pickup(self):
        global action
        action = "picked up"
        theapp.screenm.current = "thirdscreen"

    def call(self):
        global callconnect
        global outcall_add
        self.ip = self.ids.ipcall.text
        outcall_add = self.ip
        theapp.screenm.current = "thirdscreen"
        if outcall_add != '':
            print('')
            #### note start  sstreamer_bind_thread should also start  but i dont have another camera###
            #callconnect()
            scall_CON_thread.start()
            ########## note end ########
            


class thscreen(Widget):
    def __init__(self,**kwargs):
        super().__init__(**kwargs)
        global sfeed_cv_frame_recieve, endcall
        Config.set('graphics', 'resizable', True)
        Config.set('graphics', 'width', '800')
        Config.set('graphics', 'height', '800')
        #######################
        Clock.schedule_interval(self.feedupdate, 0.01)

    def feedupdate(self, dt):          # MULTIPLE USERS NEDSS MORE WORK OF POS AND SIZE AFTER SOCKETS
        global sfeed_cv_frame_recieve, endcall
            ############## note begin sfeed_cv_frame_recieve is converted to texture ############
        if sfeed_cv_frame_recieve is not None and endcall == False:
    

            self.frame = sfeed_cv_frame_recieve
            buf1 = cv2.flip(self.frame, 0)
            buf = buf1.tostring()
            image_texture = Texture.create(
                    size=(self.frame.shape[1], self.frame.shape[0]), colorfmt='bgr')
            image_texture.blit_buffer(buf, colorfmt='bgr', bufferfmt='ubyte')
                     
            self.texture = image_texture
            
            with self.canvas:       
                Rectangle(texture=self.texture, pos=(self.pos[0], self.pos[1]+(self.size[1]*0.1)), size=(self.size[0],self.size[1]*0.9))
            ###################### note end ##############
        elif endcall == True:   
            theapp.screenm.current = "secondscreen"
            
    def hangup(self):
        global endcall
        endcall = True

        endcall_string = 'endcall'
        print(endcall_string)
        theapp.screenm.current = "secondscreen"


class theapp(App):
    def build(self):
        
        self.screenm = ScreenManager() #(transition=FadeTransition())

        self.fscreen = fscreen()
        screen = Screen(name = "first screen")
        screen.add_widget(self.fscreen)
        self.screenm.add_widget(screen)

        self.secscreen = secscreen()
        screen  = Screen(name = "secondscreen")
        screen.add_widget(self.secscreen)
        self.screenm.add_widget(screen)

        self.thscreen = thscreen()
        screen  = Screen(name = "thirdscreen")
        screen.add_widget(self.thscreen)
        self.screenm.add_widget(screen)
        return self.screenm

if __name__ == "__main__":
    theapp = theapp()
    slistner_bind_thread.start()                                            ###########   LISTING THREAD 
    threading.Thread(target = theapp.run())            ################ GUI APP THREAD

这是.kv文件

<fscreen>
    Label:
        text: '0I Mechanics'
        font_size: root.width*0.05
        pos: root.width*0.4, root.height*0.8
        

<secscreen>
    TextInput:
        id: ipcall
        hint_text: 'Ip'
        width: root.width*0.3
        height: root.height*0.05
        pos: root.width*0.4, root.height*0.8

    Button:
        text: 'call'
        width: root.width*0.3
        height: root.height*0.05
        pos: root.width*0.4, root.height*0.7
        on_press: root.call()

    Label:
        
        text: root.callerid
        font_size: root.width*0.02
        pos: root.width*0.4, root.height*0.4

    Button:
        text: 'pickup'
        width: root.width*0.2
        height: root.height*0.05
        disabled: True if root.waiter == 'nocalls' else False
        pos: root.width*0.5, root.height*0.3
        on_press: root.pickup()

    Button:
        text: 'decline'
        width: root.width*0.2
        height: root.height*0.05
        disabled: True if root.waiter == 'nocalls' else False
        pos: root.width*0.3, root.height*0.3
        on_press: root.decline()


<thscreen>
    Widget:

        Button:
            text: 'hangup'
            width: root.width*0.2
            height: root.height*0.05
            pos: root.width*0.45, root.height*0.01
            on_press: root.hangup()

标签: pythonmultithreadingsocketskivyp2p

解决方案


推荐阅读