python客户端挂起时没有数据从服务器接收和挂起在该线程w/o让客户端发送

问题描述:

我想弄清楚如何让我的客户端'同时'发送和接收数据,并使用线程。我的问题是,根据我设置它的方式,这里的方式是在recieveFromServer函数中等待来自服务器的数据,该函数位于它自己的线程中,并且无法在发送任何内容时停止它。另一种方式,它只是等待用户输入,然后发送到服务器,然后在客户端向服务器发送消息后调用函数recieveFromServer,该消息不允许流畅的通信,但不能让它自动交替。当客户端没有任何要发送的内容,或者没有更多的内容从服务器接收时,我如何释放线程。python客户端挂起时没有数据从服务器接收和挂起在该线程w/o让客户端发送

如果我试图解释我所尝试过的一切,它会变得很长。 :)

谢谢。

客户端:

from socket import * 
from threading import * 
import thread 
import time 
from struct import pack,unpack 
from networklingo import * 
#from exception import * 

HOST = '192.168.0.105' 
PORT = 21567 
BUFFSIZE = 1024 
ADDR = (HOST,PORT) 

lock = thread.allocate_lock() 

class TronClient: 

    def __init__(self,control=None): 
     self.tcpSock = socket(AF_INET,SOCK_STREAM) 
     #self.tcpSock.settimeout(.2) 
     self.recvBuff = [] 

    def connect(self): 
     self.tcpSock.connect(ADDR) 
     self.clientUID = self.tcpSock.recv(BUFFSIZE) 
     print 'My clientUID is ', self.clientUID 
     t = Thread(target = self.receiveFromSrv()) 
     t.setDaemon(1) 
     t.start() 
     print 'going to main loop' 
     self.mainLoop() 
     #t = Thread(target = self.mainLoop()) 
     #t.setName('mainLoop') 
     #t.setDaemon(1) 
     #t.start() 

    def receiveFromSrv(self): 
     RECIEVING = 1 
     while RECIEVING: 
      #print 'Attempting to retrieve more data' 
      #lock.acquire() 
      #print 'Lock Aquired in recieveFromSrv' 

      #try: 
      data = self.tcpSock.recv(BUFFSIZE) 
      #except socket.timeout,e: 
        #print 'Error recieving data, ',e 
        #continue 
      #print data 
      if not data: continue 

      header = data[:6] 
      msgType,msgLength,clientID = unpack("hhh",header) 
      print msgType 
      print msgLength 
      print clientID,'\n' 

      msg = data[6:] 

      while len(msg) < msgLength: 
       data = self.tcpSock.recv(BUFFSIZE) 
       dataLen = len(data) 

       if dataLen <= msgLength: 
        msg += data 
       else: 
        remLen = msgLength-len(data) #we just need to retrieve first bit of data to complete msg 
        msg += data[:remLen] 
        self.recvBuff.append(data[remLen:]) 

      print msg 
      #else: 
       #lock.release() 
      # print 'lock release in receiveFromSrv' 
       #time.sleep(2) 
      #RECIEVING = 0 

    def disconnect(self,data=''): 
     self.send(DISCONNECT_REQUEST,data) 
     #self.tcpSock.close() 

    def send(self,msgType,msg): 
     header = pack("hhh",msgType,len(msg),self.clientUID) 
     msg = header+msg 
     self.tcpSock.send(msg) 

    def mainLoop(self): 
     while 1: 
      try: 
       #lock.acquire() 
       #print 'lock aquired in mainLoop' 
       data = raw_input('> ') 
      except EOFError:   # enter key hit without any data (blank line) so ignore and continue 
       continue     

      #if not data or data == '': # no valid data so just continue 
      # continue 

      if data=='exit':   # client wants to disconnect, so send request to server 
       self.disconnect() 
       break 
      else: 
       self.send(TRON_CHAT,data) 

      #lock.release() 
      #print 'lock released in main loop' 
      #self.recieveFromSrv() 
      #data = self.tcpSock.recv(BUFFSIZE) 
      #t = Thread(target = self.receiveFromSrv()) 
      #t.setDaemon(1) 
      #t.start() 



if __name__ == "__main__": 
    cli = TronClient() 
    cli.connect() 
    #t = Thread(target = cli.connect()) 
    #t.setName('connect') 
    #t.setDaemon(1) 
    #t.start() 

服务器(增加或减少客户端的数量时使用的锁):

from socket import * 
from threading import * 
import thread 
from controller import * 
from networklingo import * 
from struct import pack,unpack 

HOST = '' 
PORT = 21567 
BUFSIZE = 1024 
ADDR = (HOST,PORT) 

nclntlock = thread.allocate_lock() 

class TronServer: 

    def __init__(self,maxConnect=4,control=None): 
     self.servSock = socket(AF_INET,SOCK_STREAM) 

     # ensure that you can restart server quickly when it terminates 
     self.servSock.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) 

     self.servSock.bind(ADDR) 
     self.servSock.listen(maxConnect) 

     # keep track of number of connected clients 
     self.clientsConnected = 0 

     # give each client a unique identfier for this run of server 
     self.clientUID = 0 

     # list of all clients to cycle through for sending 
     self.allClients = {} 

     # keep track of threads 
     self.cliThreads = {} 

     #reference back to controller 
     self.controller = control 

     self.recvBuff = [] 

    def removeClient(self,clientID,addr): 
     if clientID in self.allClients.keys(): 
      self.allClients[clientID].close() 
      print "Disconnected from", addr 
      nclntlock.acquire() 
      self.clientsConnected -= 1 
      nclntlock.release() 
      del self.allClients[clientID] 
     else: 
      print 'ClientID is not valid' 

    def recieve(self,clientsock,addr): 
     RECIEVING = 1 

     # loop serving the new client 
     while RECIEVING: # while PLAYING??? 
      try: 
       data = clientsock.recv(BUFSIZE) 
      except: 
       RECIEVING = 0 
       continue 

#   if not data: break #no data was recieved 

      if data != '': 
       print 'Recieved msg from client: ',data 

       header = data[:6] 
       msgType,msgLength,clientID = unpack("hhh",header) 
       print msgType 
       print msgLength 
       print clientID,'\n' 

       if msgType == DISCONNECT_REQUEST:    #handle disconnect request 
        self.removeClient(clientID,addr) 
       else:           #pass message type and message off to controller 

        msg = data[6:] 

        while len(msg) < msgLength: 
         data = self.tcpSock.recv(BUFSIZE) 
         dataLen = len(data) 

         if dataLen <= msgLength: 
          msg += data 
         else: 
          remLen = msgLength-len(data) #we just need to retrieve first bit of data to complete msg 
          msg += data[:remLen] 
          self.recvBuff.append(data[remLen:]) 

        print msg  
      # echo back the same data you just recieved 
      #clientsock.sendall(data) 
        self.send(TRON_CHAT,msg,-1) #send to client 0 


     for k in self.allClients.keys(): 
      if self.allClients[k] == clientsock: 
       self.removeClient(k,addr) 
       print 'deleted after hard exit from clientID ', k 
       #self.cliThreads[k].join() 
       #del self.cliThreads[k] 
       # then tell controller to delete player with k 
       break 

    def send(self,msgType,msg,clientID=-1): 
     header = pack("hhh",msgType,len(msg),clientID) 
     msg = header+msg 

     if clientID in self.allClients: 
      self.allClients[clientID].send(msg) 
     elif clientID==ALL_PLAYERS: 
      for k in self.allClients.keys(): 
       self.allClients[k].send(msg) 


    def mainLoop(self): 
     global nclntlock 

     try: 
      while self.controller != None and self.controller.state == WAITING: 
       print 'awaiting connections' 
       clientsock, caddy = self.servSock.accept() 

       nclntlock.acquire()       
       self.clientsConnected += 1 
       nclntlock.release() 
       print 'Client ',self.clientUID,' connected from:',caddy 
       clientsock.setblocking(0) 
       clientsock.send(str(self.clientUID)) 
       self.allClients[self.clientUID] = clientsock 
       t = Thread(target = self.recieve, args = [clientsock,caddy]) 
       t.setName('recieve-' + str(self.clientUID)) 
       self.cliThreads[self.clientUID] = t 
       self.clientUID += 1 
       # t.setDaemon(1) 
       t.start() 
     finally: 
      self.servSock.close() 

if __name__ == "__main__": 
    serv = TronServer(control = LocalController(nPlayers = 3, fWidth = 70, fHeight = 10)) 
    t = Thread(target = serv.mainLoop()) 
    t.setName('mainLoop') 
# t.setDaemon(1) 
    t.start() 

我想你想尝试,并设置套接字非阻塞模式:

http://docs.python.org/library/socket.html#socket.socket.setblocking

设置阻塞或非阻塞模式为 套接字:如果标记为0,则套接字 设置为非阻塞,否则设置为 阻塞模式。最初,所有插座 处于阻塞模式。在非阻塞 模式下,如果recv()调用未找到 任何数据,或者send()调用不能立即处理该数据,则会引发 错误异常;在阻止 模式下,呼叫阻塞,直到他们可以继续执行 。 s.setblocking(0)是 等价于s.settimeout(0); (1)相当于 s.settimeout(None)。

另外,不是使用原始套接字,而是使用multiprocessing模块进行了考虑。这是进行网络IO的更高级别的抽象。关于Pipes & Queues的部分特定于在客户端/服务器之间发送和接收数据。

+0

感谢您的回复。在recv调用之前,我尝试了self.tcpSock.setblocking(0),但似乎没有任何区别,并将它保存在服务器代码中(现在)。 我一直在看看多处理模块,你碰巧知道如果将它与我已经有的,因为我刚刚学习python很容易?我不确定我会如何使用这些方法之一。 在此先感谢您的帮助。 – Devin 2010-02-13 02:43:15