Hi I am having some issues wrapping some ZMQ pull clients in Python classes. These classes are instanced and called in a Subprocess via the multiprocessing module. When the clients are functions all works but when they are classes the poller.poll() hangs.
The code bellow has both versions: One works, the other doesn't. Why?
import zmq
import time
import sys
import random
from multiprocessing import Process
def server_push(port="5556"):
context = zmq.Context()
socket = context.socket(zmq.PUSH)
socket.bind("tcp://*:%s" % port)
print "Running server on port: ", port
# serves only 5 request and dies
for reqnum in range(10):
if reqnum < 6:
socket.send("Continue")
else:
socket.send("Exit")
break
time.sleep (1)
def server_pub(port="5558"):
context = zmq.Context()
socket = context.socket(zmq.PUB)
socket.bind("tcp://*:%s" % port)
publisher_id = random.randrange(0,9999)
print "Running server on port: ", port
# serves only 5 request and dies
for reqnum in range(10):
# Wait for next request from client
topic = random.randrange(8,10)
messagedata = "server#%s" % publisher_id
print "%s %s" % (topic, messagedata)
socket.send("%d %s" % (topic, messagedata))
time.sleep(1)
class Client:
def __init__(self,port_push, port_sub):
context = zmq.Context()
self.socket_pull = context.socket(zmq.PULL)
self.socket_pull.connect ("tcp://localhost:%s" % port_push)
print "Connected to server with port %s" % port_push
self.socket_sub = context.socket(zmq.SUB)
self.socket_sub.connect ("tcp://localhost:%s" % port_sub)
self.socket_sub.setsockopt(zmq.SUBSCRIBE, "9")
print "Connected to publisher with port %s" % port_sub
# Initialize poll set
def __call__(self):
poller = zmq.Poller()
poller.register(self.socket_pull, zmq.POLLIN)
poller.register(self.socket_sub, zmq.POLLIN)
# Work on requests from both server and publisher
should_continue = True
print "listening"
while should_continue:
print "hello"
socks = dict(poller.poll())
print poller
if self.socket_pull in socks and socks[self.socket_pull] == zmq.POLLIN:
message = self.socket_pull.recv()
print "Recieved control command: %s" % message
if message == "Exit":
print "Recieved exit command, client will stop recieving messages"
should_continue = False
if self.socket_sub in socks and socks[self.socket_sub] == zmq.POLLIN:
string = self.socket_sub.recv()
topic, messagedata = string.split()
print "Processing ... ", topic, messagedata
def client(port_push, port_sub):
context = zmq.Context()
socket_pull = context.socket(zmq.PULL)
socket_pull.connect ("tcp://localhost:%s" % port_push)
print "Connected to server with port %s" % port_push
socket_sub = context.socket(zmq.SUB)
socket_sub.connect ("tcp://localhost:%s" % port_sub)
socket_sub.setsockopt(zmq.SUBSCRIBE, "9")
print "Connected to publisher with port %s" % port_sub
# Initialize poll set
poller = zmq.Poller()
poller.register(socket_pull, zmq.POLLIN)
poller.register(socket_sub, zmq.POLLIN)
# Work on requests from both server and publisher
should_continue = True
while should_continue:
socks = dict(poller.poll())
if socket_pull in socks and socks[socket_pull] == zmq.POLLIN:
message = socket_pull.recv()
print "Recieved control command: %s" % message
if message == "Exit":
print "Recieved exit command, client will stop recieving messages"
should_continue = False
if socket_sub in socks and socks[socket_sub] == zmq.POLLIN:
string = socket_sub.recv()
topic, messagedata = string.split()
print "Processing ... ", topic, messagedata
if __name__ == "__main__":
# Now we can run a few servers
server_push_port = "5556"
server_pub_port = "5558"
Process(target=server_push, args=(server_push_port,)).start()
Process(target=server_pub, args=(server_pub_port,)).start()
#~ Process(target=client,args=(server_push_port,server_pub_port)).start()
Process(target=Client(server_push_port,server_pub_port)).start()
Edit1: this is not quite correct...give me a few moments to get it right...
I think you may be invoking the Client class the wrong way. I'm not an expert with this, but I think your client should be subclassed from Process, and then run using the .start() function. So, define your Client class like this:
class Client(Process):
def __init__(self, port_push, port_sub):
(...) # your class init code here...make sure indentation is correct
Then at the end where you run the servers, create an instance of your Client class and start it like so:
client_class = Client(port_push, port_sub)
client_class.start()
Edit2: Here's an edited version of fccoelho's code that works for me.
The biggest problem appears to be that the ZMQ initialization stuff needs to be done in the __call__ method, not in __init__. I suspect this is due to how memory is allocated in multiprocessing, in that the __init__ function will be done in the parent process, while the __call__ function is done in the child process with a separate memory space. Apparently ZMQ doesn't like this. I've also added some wait times to prevent the client from connecting to the server before the server is ready, and to prevent the server from sending messages before the client subscribes. Also using 127.0.0.1 instead of localhost (my computer doesn't like localhost for some reason). Also removed the annoying print messages around the poll call in the client, and fixed the indentation problem where the client checks the poll results on the pubsub socket.
import zmq
import time
import sys
import random
from multiprocessing import Process
def server_push(port="5556"):
context = zmq.Context()
socket = context.socket(zmq.PUSH)
socket.bind("tcp://127.0.0.1:%s" % port)
print "Running server on port: ", port
time.sleep(1.0)
# serves only 5 request and dies
for reqnum in range(10):
if reqnum < 6:
socket.send("Continue")
else:
socket.send("Exit")
print 'Push server sent "Exit" signal'
break
time.sleep(0.4)
def server_pub(port="5558"):
context = zmq.Context()
socket = context.socket(zmq.PUB)
socket.bind("tcp://127.0.0.1:%s" % port)
socket.setsockopt(zmq.HWM, 1000)
publisher_id = random.randrange(0,9999)
print "Running server on port: ", port
time.sleep(1.0)
# serves only 5 request and dies
for reqnum in range(10):
# Wait for next request from client
topic = random.randrange(8,10)
messagedata = "server#%s" % publisher_id
print "%s %s" % (topic, messagedata)
socket.send("%d %s" % (topic, messagedata))
time.sleep(0.4)
class Client:
def __init__(self,port_push, port_sub):
self.port_push = port_push
self.port_sub = port_sub
# Initialize poll set
def __call__(self):
time.sleep(0.5)
print 'hello from class client!'
context = zmq.Context()
self.socket_pull = context.socket(zmq.PULL)
self.socket_pull.connect ("tcp://127.0.0.1:%s" % self.port_push)
print "Connected to server with port %s" % self.port_push
self.socket_sub = context.socket(zmq.SUB)
self.socket_sub.connect ("tcp://127.0.0.1:%s" % self.port_sub)
self.socket_sub.setsockopt(zmq.SUBSCRIBE, "9")
print "Connected to publisher with port %s" % self.port_sub
poller = zmq.Poller()
poller.register(self.socket_pull, zmq.POLLIN)
poller.register(self.socket_sub, zmq.POLLIN)
# Work on requests from both server and publisher
should_continue = True
print "listening"
while should_continue:
# print "hello"
socks = dict(poller.poll())
# print poller
if self.socket_pull in socks and socks[self.socket_pull] == zmq.POLLIN:
message = self.socket_pull.recv()
print "Recieved control command: %s" % message
if message == "Exit":
print "Recieved exit command, client will stop recieving messages"
should_continue = False
if self.socket_sub in socks and socks[self.socket_sub] == zmq.POLLIN:
string = self.socket_sub.recv()
topic, messagedata = string.split()
print "Processing ... ", topic, messagedata
def client(port_push, port_sub):
print 'hello from function client!'
context = zmq.Context()
socket_pull = context.socket(zmq.PULL)
socket_pull.connect ("tcp://127.0.0.1:%s" % port_push)
print "Connected to server with port %s" % port_push
socket_sub = context.socket(zmq.SUB)
socket_sub.connect ("tcp://127.0.0.1:%s" % port_sub)
socket_sub.setsockopt(zmq.SUBSCRIBE, "9")
print "Connected to publisher with port %s" % port_sub
# Initialize poll set
poller = zmq.Poller()
poller.register(socket_pull, zmq.POLLIN)
poller.register(socket_sub, zmq.POLLIN)
# Work on requests from both server and publisher
should_continue = True
while should_continue:
socks = dict(poller.poll(1000))
if socket_pull in socks and socks[socket_pull] == zmq.POLLIN:
message = socket_pull.recv()
print "Recieved control command: %s" % message
if message == "Exit":
print "Recieved exit command, client will stop recieving messages"
should_continue = False
if socket_sub in socks and socks[socket_sub] == zmq.POLLIN:
string = socket_sub.recv()
topic, messagedata = string.split()
print "Processing ... ", topic, messagedata
if __name__ == "__main__":
# Now we can run a few servers
server_push_port = "5556"
server_pub_port = "5558"
Process(target=server_push, args=(server_push_port,)).start()
Process(target=server_pub, args=(server_pub_port,)).start()
# Process(target=client,args=(server_push_port,server_pub_port)).start()
Process(target=Client(server_push_port,server_pub_port)).start()
Finally, here's a cleaner implementation of multi-process pubsub that's very bare-bones, but demonstrates things more clearly:
import zmq
from multiprocessing import Process
import time
class ServerPubSub(Process):
def __init__(self, port, n):
Process.__init__(self)
self.port = port
self.n = n
def run(self):
self.context = zmq.Context()
self.pub = self.context.socket(zmq.PUB)
self.pub.bind('tcp://127.0.0.1:%d' % self.port)
self.pub.setsockopt(zmq.HWM, 1000)
time.sleep(1)
end = False
for i in range(self.n):
print 'SRV: sending message %d' % i
self.pub.send('Message %d' % i)
print 'SRV: message %d sent' % i
time.sleep(0.2)
self.pub.close()
class ClientPubSub(Process):
def __init__(self, port, n):
Process.__init__(self)
self.port = port
self.n = n
def run(self):
self.context = zmq.Context()
self.sub = self.context.socket(zmq.SUB)
self.sub.connect('tcp://127.0.0.1:%d' % self.port)
self.sub.setsockopt(zmq.SUBSCRIBE, '')
self.poller = zmq.Poller()
self.poller.register(self.sub, zmq.POLLIN)
end = False
count = 0
while count < self.n:
ready = dict(self.poller.poll(0))
if self.sub in ready and ready[self.sub] == zmq.POLLIN:
msg = self.sub.recv()
print 'CLI: received message "%s"' % msg
count += 1
self.sub.close()
if __name__ == "__main__":
port = 5000
n = 10
server = ServerPubSub(port, n)
client = ClientPubSub(port, n)
server.start()
client.start()
server.join()
client.join()
Related
I want to create a TCP server which can send specific messages to specific clients. In my example, I have two clients Iconet and Robot. I want to send specific messages to each of these clients once they are connected. I wish to send VITAi and VITAr to the clients respectively. Once i receive the response from the two clients i wish to jump to def chat() The aim is to get the response from clients and then jump to def chat() which acts like a chat room and displays the messages the clients have sent. How can i achieve this?
server
import threading
import socket
PORT = 1026
SERVER = socket.gethostbyname(socket.gethostname())
ADDR = (SERVER,PORT)
FORMAT = "utf-8"
HEADER = 1024
DISCONNECT_MESSAGE = "END_CYCLE"
VITA_R = "yes"
VITA_I = "yes"
robot_flag = False
iconet_flag = False
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(ADDR)
clients = []
aliases = []
alias_dictionary_iter = zip(aliases,clients)
alias_dictionary = dict(alias_dictionary_iter)
def broadcast(broadcast_message):
for client in clients:
client.send(broadcast_message)
def handle_client(client,addr):
print(f"[NEW CONNECTION] {addr} connected.")
connected = True
while connected:
for client in clients:
if client == clients[0]:
robot_message = 'VITAr'
client.send(robot_message.encode(FORMAT))
robot_response = client.recv(2048).decode(FORMAT)
print(robot_response)
if robot_response == VITA_R:
robot_flag == True
else:
robot_flag == False
elif client == clients[1]:
iconet_message = 'VITAi'
client.send(iconet_message.encode(FORMAT))
iconet_response = client.recv(2048).decode(FORMAT)
print(iconet_response)
if iconet_response == VITA_I:
iconet_flag == True
else:
iconet_flag == False
def chat(client):
while robot_flag & iconet_flag == True:
try:
message = client.recv(1024)
broadcast(message)
print(message)
except:
index = clients.index(client)
clients.remove(client)
client.close()
alias = aliases[index]
broadcast(f'{alias} has left the chat room!'.encode('utf-8'))
aliases.remove(alias)
break
def start():
server.listen()
print(f"[LISTENING] Server is listening on {SERVER}")
while True:
client, addr = server.accept()
print(f"[NEW CONNECTION] {addr} connected.")
client.send('NAME?'.encode(FORMAT))
alias = client.recv(1024)
aliases.append(alias)
clients.append(client)
print(f'The clients is {alias}'.encode(FORMAT))
thread = threading.Thread(target= handle_client, args=(client, addr))
thread.start()
print ('[STARTING] server is starting')
start()
client
import threading
import socket
name = input('NAME? ')
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('0.0.0.0', 1026))
def client_receive():
while True:
try:
message = client.recv(1024).decode('utf-8')
if message == "NAME?":
client.send(name.encode('utf-8'))
else:
print(message)
except:
print('Error!')
client.close()
break
def client_send():
while True:
message = f'{name}: {input("")}'
client.send(message.encode('utf-8'))
receive_thread = threading.Thread(target=client_receive)
receive_thread.start()
send_thread = threading.Thread(target=client_send)
send_thread.start()
Output at console server
I am trying to write bi-directional UDP communication using multithread but it crashes after sending two messages. Also i am new to threading so please post your solution on this.
Thanks
Server side:
import threading
from threading import Thread
import socket
from socket import *
import time
import pymongo
from datetime import datetime
from time import ctime
#broadcast works for this program
import netifaces
import os
import re
import struct
class cont():
def get_msg(self):
UDP = "192.168.1.27"
port = 4343
address = UDP, port
self.sock = socket(AF_INET, SOCK_DGRAM)
self.sock.bind(address)
while True:
r = self.sock.recvfrom(1000)
print("controller1: %s" % (r[0]))
reply = input('Main controller : ')
client_address = r[1]
self.sock.sendto(bytearray(reply, "utf-8"), client_address)
t2 = threading.Thread(target=self.get_msg, args=(reply,))
t2.start()
if __name__=='__main__':
c=cont()
#c.broad(msg="")
c.get_msg()
Client side:
UDP=""
port=4343
address=UDP,port
client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
while(True):
msg=input("Controller1")
client.sendto(bytearray(msg,"utf-8"),address)
reply=client.recvfrom(1000)
recved=str(reply)
print("Main Controller:% s" % recved))
Output required :
Server Console:
Client:b'hello'
Server:b'hi
Client Console:
Client: b'hello'
Server : (b'hi',('ip',port)
Here is a TCP class I made for communicating with my robots, can be easily modified for UDP. Might seem like a lot of code, but it's what it takes for "reliable" "two way" communication, without blocking your main program. I use processes instead of threads because threads in python aren't "real" threads due to the global interpreter lock.
import socket
from multiprocessing import Process, Queue, Event, Value
import traceback
class SocketComm(object):
def __init__(self,port):
self.address = ""
self.otherAddress = object
self.port = port
self.finished = Value("b", True)
self.inbox = Queue()
self.connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.getMessagesProcess = Process(target=self.getMessages)
self.getMessagesProcess._stop_event = Event()
self.getMessagesProcess.daemon = True
self.connected = False
self.timeout = 3
return
def setupLine(self, addr):
self.address = addr
if self.address is "": #i.e. server on raspberry pi
try:
self.connection.settimeout(self.timeout)
self.connection.bind((self.address, self.port))
print("binding with port: " + str(self.port))
self.connection.listen(1)
self.connection, self.otherAddress = self.connection.accept()
print("connected to client at: " + self.otherAddress[0])
except socket.error as e:
print(str(e))
return False
else:
try:
#print("connecting to port: " + str(self.port))
self.connection.connect((self.address, self.port)) # i.e. client
print("connected to server")
except socket.error as e:
#print(str(e))
return False
self.getMessagesProcess.start()
self.connected = True
self.finished.value = False
print("inbox at: " + str(id(self.inbox)))
return True
def sendMessage(self, msg):
try:
self.connection.send(str.encode(msg))
#print("sent: " + str(msg))
except Exception as e:
pass
#print(str(e))
#traceback.print_exc()
#print("exception caught.")
return
def getMessages(self):
#print("getting messages now")
self.connection.settimeout(1)
while(not self.finished.value):
#print("checking inbox")
#print("inbox length: " + str(len(self.inbox)))
try:
received = self.connection.recv(1024)
decoded = received.decode('utf-8')
if len(decoded) > 0:
if(decoded == "end"):
self.finished.value = True
else:
self.inbox.put(decoded)
print("received: " + str(decoded))
except socket.error as e:
if(type(e).__name__ == "timeout"):
pass
else:
print("endpoint closed.")
self.finished.value = True
return
def closeConnection(self):
if(self.connected):
self.finished.value = True
self.getMessagesProcess._stop_event.set()
self.sendMessage("end")
try:
self.getMessagesProcess.join()
except:
print("process already finished.")
self.connection.close()
return
##
##if(__name__ == "__main__"):
## robotClient = SocketComm(5555)
## robotClient.setupLine("127.0.0.1")
## while(robotClient.finished.value == False):
## val = input("enter something: ")
## if(len(val) > 0):
## robotClient.sendMessage(val)
##
##
##if(__name__ == "__main__"):
## try:
## robotServer = SocketComm(5555)
## print("waiting for client to connect...")
## robotServer.setupLine("")
## print("connected!")
## while(robotServer.finished.value == False):
## val = input("enter something: ")
## if(len(val) > 0):
## robotServer.sendMessage(val)
## except:
## pass
## finally:
## robotServer.closeConnection()
## sys.exit(0)
I am trying to run a thread more than once and keep getting an error:
RuntimeError: threads can only be started once
I have tried reading up multithreading and implementing it in my code without any luck.
Here is the function I am threading:
def receive(q):
host = ""
port = 13000
buf = 1024
addr = (host,port)
Sock = socket(AF_INET, SOCK_DGRAM)
Sock.bind(addr)
(data, addr) = Sock.recvfrom(buf)
q.put(data)
Here is the code I want to run:
q = Queue.Queue()
r = threading.Thread(target=receive, args=(q,))
while True:
r.start()
if q.get() == "stop":
print "Stopped"
break
print "Running program"
When the stop message gets sent, the program should break out of the while loop, but it does not run due to multithreading. The while loop should constantly print out Running program, until the stop message is sent.
The queue is used to receive the variable data from the receive function (which is the stop).
Here is a working example (for python 2.7).
The program has two modes of operation:
with no arguments it runs the receive loop
with arguments it sends a datagram
Note how r.start() and r.terminate() are called outside of the while loop in client.
Also, receive has a while True loop.
import sys
import socket
from multiprocessing import Process, Queue
UDP_ADDR = ("", 13000)
def send(m):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(m, UDP_ADDR)
def receive(q):
buf = 1024
Sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
Sock.bind(UDP_ADDR)
while True:
(data, addr) = Sock.recvfrom(buf)
q.put(data)
def client():
q = Queue()
r = Process(target = receive, args=(q,))
r.start()
print "client loop started"
while True:
m = q.get()
print "got:", m
if m == "stop":
break
print "loop ended"
r.terminate()
if __name__ == '__main__':
args = sys.argv
if len(args) > 1:
send(args[1])
else:
client()
I think the problem is once the thread is started, calling thread.start() again throws the error.
Using a try block would might work as a simple fix:
while True:
try:
r.start()
except Exception:
#or except RunTimeError:
pass
if q.get() == "stop":
print "Stopped"
break
print "Running program"
I have this code which listens/sends from/to a Scratch program with remote sensor connections enabled (e.g communicates by Port 42001 on 127.0.0.1)
# This code is copyright Simon Walters under GPL v2
# This code is derived from scratch_handler by Thomas Preston
# Version 5dev 11Aug08 Much better looping supplied by Stein #soilandreyes
# and someone else #MCrRaspJam who've name I've forgotton!
# Version 6dev - Moved Allon/AllOff to be processed before single pins :)
# Vesion 7dev - start to tidy up changes
# Vesion 8dev - use gpio-output system and broadcast allon, 1on system
# V0.1 - change to 6 out 2 in and sanitise the code
# V0.2 -
from array import *
import threading
import socket
import time
import sys
import struct
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BOARD)
GPIO.setup(11,GPIO.OUT)
GPIO.setup(12,GPIO.OUT)
GPIO.setup(13,GPIO.OUT)
GPIO.setup(15,GPIO.OUT)
GPIO.setup(16,GPIO.OUT)
GPIO.setup(18,GPIO.OUT)
GPIO.setup(22,GPIO.IN,pull_up_down=GPIO.PUD_UP)
GPIO.setup(7,GPIO.IN,pull_up_down=GPIO.PUD_UP)
'''
from Tkinter import Tk
from tkSimpleDialog import askstring
root = Tk()
root.withdraw()
'''
PORT = 42001
DEFAULT_HOST = '127.0.0.1'
#HOST = askstring('Scratch Connector', 'IP:')
BUFFER_SIZE = 240 #used to be 100
SOCKET_TIMEOUT = 1
SCRATCH_SENSOR_NAME_INPUT = (
'gpio-input0',
'gpio-input1'
)
SCRATCH_SENSOR_NAME_OUTPUT = (
'gpio-output0',
'gpio-output1',
'gpio-output2',
'gpio-output3',
'gpio-output4',
'gpio-output5'
)
SCRATCH_BROADCAST_NAME_OUTPUT = (
'1on','1off','2on','2off','3on','3off','4on','4off','5on','5off','6on','6off'
)
#Map gpio to real connector P1 Pins
GPIO_PINS = array('i',[11,12,13,15,16,18,22,7])
GPIO_PIN_OUTPUT = array('i')
GPIO_PIN_INPUT = array('i')
print "Output Pins are:"
for i in range(0,len(SCRATCH_SENSOR_NAME_OUTPUT)):
print GPIO_PINS[i]
GPIO_PIN_OUTPUT.append(GPIO_PINS[i])
print "Input Pins are:"
for i in range(len(SCRATCH_SENSOR_NAME_OUTPUT),8):
print GPIO_PINS[i]
GPIO_PIN_INPUT.append(GPIO_PINS[i])
class ScratchSender(threading.Thread):
#Not needed as its a Listening issue
...
class ScratchListener(threading.Thread):
def __init__(self, socket):
threading.Thread.__init__(self)
self.scratch_socket = socket
self._stop = threading.Event()
def stop(self):
self._stop.set()
def stopped(self):
return self._stop.isSet()
def physical_pin_update(self, pin_index, value):
physical_pin = GPIO_PIN_OUTPUT[pin_index]
print 'setting GPIO %d (physical pin %d) to %d' % (pin_index,physical_pin,value)
GPIO.output(physical_pin, value)
def run(self):
#This is main listening routine
while not self.stopped():
#time.sleep(0.1) # be kind to cpu
try:
data = self.scratch_socket.recv(BUFFER_SIZE)
dataraw = data[4:].lower()
print 'Length: %d, Data: %s' % (len(dataraw), dataraw)
if len(dataraw) == 0:
#This is probably due to client disconnecting
#I'd like the program to retry connecting to the client
time.sleep(2)
except socket.timeout:
print "sockect timeout"
time.sleep(1)
continue
except:
break
if 'sensor-update' in dataraw:
#gloablly set all ports
if 'gpio-outputall' in dataraw:
outputall_pos = dataraw.find('gpio-outputall')
sensor_value = dataraw[(outputall_pos+16):].split()
#print sensor_value[0]
for i in range(len(SCRATCH_SENSOR_NAME_OUTPUT)):
self.physical_pin_update(i,int(sensor_value[0]))
#check for individual port commands
for i in range(len(SCRATCH_SENSOR_NAME_OUTPUT)):
if 'gpio-output'+str(i) in dataraw:
#print 'Found '+ 'gpio-output'+str(i)
outputall_pos = dataraw.find('gpio-output'+str(i))
sensor_value = dataraw[(outputall_pos+14):].split()
#print sensor_value[0]
self.physical_pin_update(i,int(sensor_value[0]))
#Use bit pattern to control ports
if 'gpio-pattern' in dataraw:
#print 'Found gpio-outputall'
num_of_bits = len(SCRATCH_SENSOR_NAME_OUTPUT)
outputall_pos = dataraw.find('gpio-pattern')
sensor_value = dataraw[(outputall_pos+14):].split()
#print sensor_value[0]
bit_pattern = ('0000000000000000'+sensor_value[0])[-num_of_bits:]
#print 'bit_pattern %s' % bit_pattern
for i in range(len(SCRATCH_SENSOR_NAME_OUTPUT)):
#bit_state = ((2**i) & sensor_value) >> i
#print 'dummy gpio %d state %d' % (i, bit_state)
physical_pin = GPIO_PIN_OUTPUT[i]
if bit_pattern[-(i+1)] == '0':
print 'setting GPIO %d (physical pin %d) low' % (i,physical_pin)
GPIO.output(physical_pin, 0)
else:
print 'setting GPIO %d (physical pin %d) high' % (i,physical_pin)
GPIO.output(physical_pin, 1)
elif 'broadcast' in dataraw:
#print 'received broadcast: %s' % data
if 'allon' in dataraw:
for i in range(len(SCRATCH_SENSOR_NAME_OUTPUT)):
self.physical_pin_update(i,1)
if 'alloff' in dataraw:
for i in range(len(SCRATCH_SENSOR_NAME_OUTPUT)):
self.physical_pin_update(i,0)
for i in range(len(SCRATCH_SENSOR_NAME_OUTPUT)):
#check_broadcast = str(i) + 'on'
#print check_broadcast
if str(i+1)+'on' in dataraw:
self.physical_pin_update(i,1)
if str(i+1)+'off' in dataraw:
self.physical_pin_update(i,0)
if 'pin' + str(GPIO_PIN_OUTPUT[i])+'on' in dataraw:
GPIO.output(physical_pin, 1)
if 'pin' + str(GPIO_PIN_OUTPUT[i])+'off' in dataraw:
GPIO.output(physical_pin, 0)
elif 'stop handler' in dataraw:
cleanup_threads((listener, sender))
sys.exit()
else:
print 'received something: %s' % dataraw
def create_socket(host, port):
while True:
try:
print 'Trying'
scratch_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
scratch_sock.connect((host, port))
break
except socket.error:
print "There was an error connecting to Scratch!"
print "I couldn't find a Mesh session at host: %s, port: %s" % (host, port)
time.sleep(3)
#sys.exit(1)
return scratch_sock
def cleanup_threads(threads):
for thread in threads:
thread.stop()
for thread in threads:
thread.join()
if __name__ == '__main__':
if len(sys.argv) > 1:
host = sys.argv[1]
else:
host = DEFAULT_HOST
# open the socket
print 'Connecting...' ,
the_socket = create_socket(host, PORT)
print 'Connected!'
the_socket.settimeout(SOCKET_TIMEOUT)
listener = ScratchListener(the_socket)
sender = ScratchSender(the_socket)
listener.start()
sender.start()
# wait for ctrl+c
try:
while True:
time.sleep(0.5)
except KeyboardInterrupt:
cleanup_threads((listener, sender))
sys.exit()
The issue I'm having is in this section of code
def run(self):
#This is main listening routine
while not self.stopped():
#time.sleep(0.1) # be kind to cpu
try:
data = self.scratch_socket.recv(BUFFER_SIZE)
dataraw = data[4:].lower()
print 'Length: %d, Data: %s' % (len(dataraw), dataraw)
if len(dataraw) == 0:
#This is probably due to client disconnecting
#I'd like the program to retry connecting to the client
time.sleep(2)
except socket.timeout:
print "sockect timeout"
time.sleep(1)
continue
except:
break
If the client disconnects e.g Scratch is closed, I need this program to basically restart looking for a connection again and wait for Scratch to re-connect.
Could I have some suggestions as to how to achieve this please as I am a python newbie
regards
Simon
My solution was to use a global variable and change main loop structure
if len(dataraw) == 0:
#This is probably due to client disconnecting
#I'd like the program to retry connecting to the client
#tell outer loop that Scratch has disconnected
if cycle_trace == 'running':
cycle_trace = 'disconnected'
break
is used to break out of loop and
cycle_trace = 'start'
while True:
if (cycle_trace == 'disconnected'):
print "Scratch disconnected"
cleanup_threads((listener, sender))
time.sleep(1)
cycle_trace = 'start'
if (cycle_trace == 'start'):
# open the socket
print 'Starting to connect...' ,
the_socket = create_socket(host, PORT)
print 'Connected!'
the_socket.settimeout(SOCKET_TIMEOUT)
listener = ScratchListener(the_socket)
sender = ScratchSender(the_socket)
cycle_trace = 'running'
print "Running...."
listener.start()
sender.start()
# wait for ctrl+c
try:
#just pause
time.sleep(0.5)
except KeyboardInterrupt:
cleanup_threads((listener,sender))
sys.exit()
this is now my main outer loop
Seems to work :)
I'm trying to write a handler/controller for the Minecraft server. My problem is that I can't seem get writing and reading to work properly. When a client issues a command that uses the server class's method serverCom, the Minecraft server's text/log starts to come into the Python window/Python console and the connected client hangs. Also, it seems that after I use Popen, the Minecraft server doesn't really launch until I do write to the server (aka serverCom method). In case anyone is wondering, the Popen goes to a batch file that opens the .jar file. This is on Windows XP.
import subprocess
import os
import configobj
import socket
import threading
from time import sleep
config = configobj.ConfigObj("config.ini")
cHost = config["hostip"]
cPort = int(config["hostport"])
cBuffer = int(config["serverbuffer"])
cClients = int(config["numberofclients"])
cPassword = config["password"]
class server(object):
def __init__(self):
self.process = False
self.folder = "C:\\servers\\minecraft-danny"
self.max = configobj.ConfigObj("%s\\simpleserver.properties"%self.folder)["maxPlayers"]
def serverStart(self):
if not self.process:
self.process = subprocess.Popen("java -Xmx1024m -Xms1024m -jar minecraft_server.jar nogui", cBuffer, None, subprocess.PIPE, subprocess.PIPE, subprocess.STDOUT, cwd = self.folder)
return True
return False
def serverStop(self):
if self.process:
self.serverCom("stop")
self.process = False
return True
return False
def serverCom(self, text):
if self.process:
self.process.stdout.seek(2)
self.process.stdin.write("%s\n"%text)
self.process.stdin.flush()
self.process.stdout.flush()
return (str(self.process.stdout.readline()), True)
return ("", False)
def serverPlayers(self):
if self.process:
self.serverCom("list")
x = self.serverCom(" ")[0].split(":")[3].replace("\n","").replace(" ","")
if x == "":
x = 0
else:
x = len(x.split(","))
return (x, self.max)
return (0,self.max)
serv = server()
def client(cnct, adr):
global count
try:
dat = str(cnct.recv(cBuffer)).split(" ")
ans = False
if dat[0] == "start":
print "Client %s:%s started the MC Server....."%(adr[0], adr[1])
x = serv.serverStart()
sleep(1)
serv.serverCom(" ")
serv.serverCom(" ")
sleep(5)
if x:
ans = "Server is now online."
else:
ans = "Server is already online."
elif dat[0] == "stop":
print "Client %s:%s stopped the MC Server....."%(adr[0], adr[1])
x = serv.serverStop()
sleep(6)
if x:
ans = "Server is now offline."
else:
ans = "Server is already offline."
elif dat[0] == "commun":
print "Client %s:%s executed a command on the MC Server....."%(adr[0], adr[1])
serv.serverCom(" ".join(dat[1:]))
x = serv.serverCom(" ")
if x[1]:
ans = x[0]
else:
ans = "No return text, server is offline or not responding."
elif dat[0] == "players":
print "Client %s:%s recieved the player count from the MC Server....."%(adr[0], adr[1])
pc = serv.serverPlayers()
ans = "%s/%s"%(pc[0],pc[1])
elif dat[0] == "help":
print "Client %s:%s recieved the help list....."%(adr[0], adr[1])
ans = "__________\nstart - Starts the server.\nstop - Stops the server.\ncommun <command> - Writes to server's console.\nplayers - Returns player count.\nhelp - Shows this help.\nclose - Closes client connections.\n__________"
elif dat[0] == "close":
pass
else:
ans = "Command '%s' is not valid."%dat[0]
if ans:
cnct.send("PASS")
cnct.send("%s\n"%ans)
threading.Thread(target = client, args = (cnct, adr,)).start()
else:
cnct.send("DICN")
cnct.send("Connection to server closed.\n")
cnct.close()
print "Client %s:%s disconnected....."%(adr[0], adr[1])
if count:
count -= 1
except:
cnct.close()
print "Client %s:%s disconnected..... "%(adr[0], adr[1])
if count:
count -= 1
print "-MC Server Control Server v0.0.1 BETA-"
print "Starting up server....."
print "Connecting to socket....."
count = 0
sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sck.bind((cHost, cPort))
sck.listen(5)
print "Connected and listening on %s:%s....."%(cHost, cPort)
print "Setting up client listener, allowing %s clients to connect at a time....."%cClients
while True:
for x in range(cClients):
(cnct, adr) = sck.accept()
print "Client %s:%s connected....."%(adr[0], adr[1])
cnct.send("Welcome to MineCraft Server Control.\n\nPlease enter server control password.\n")
ps = str(cnct.recv(cBuffer))
if count < cClients:
if ps == cPassword:
cnct.send("CRRT")
cnct.send("%s was correct.\nIf you need help type 'help'."%ps)
count += 1
threading.Thread(target = client, args = (cnct, adr,)).start()
else:
cnct.send("WRNG")
cnct.send("%s wasn't the correct password, please try again."%ps)
cnct.close()
print "Client %s:%s rejected....."%(adr[0], adr[1])
else:
cnct.send("WRNG")
cnct.send("Too many clients connected to MineCraft Server Control")
cnct.close()
print "Client %s:%s rejected....."%(adr[0], adr[1])
sck.close()
I have no idea how a Minecraft server works, but there are a number of problems with your code:
You are redirecting stderr to stdout from the created Java process, then expecting a line response from the server. This could be the reason that the Minecraft server is not starting, since it would block on a stderr write (depending on how Windows XP handles it). Additionally, any stderr write (e.g. log write) will destroy any structured responses you may be waiting for.
You are reading with sock.recv(N) and then assuming that you get the whole chunk (e.g. password). This is not how TCP works, you may very well get just one character back (especially true if the user types the password interactively e.g. in a Telnet prompt).
You are flushing the stdout of the subprocess, which is your input stream. You probably want to flush the stdin of the subprocess. Flushing an input stream makes no sense, it is the output stream that determines when to flush.