I am trying to write a gateway side script that will work as a TCP server.
I am having trouble trying to import the libraries I would like to use. Per the jython and python 2.1 documentation I should be able to import SocketServer and threading, but keep receiving the error “ImportError: no module named SocketServer”
“ImportError: no module named threading”.
Yes, but if the technology is already natively in Ignition why go through the trouble of trying to write a module that does the same thing? If the libraries I need for writing a gateway side TCP server are not available then I will work on writing module. My understanding is that Ignition has python 2.1 libraries available for use.
If it is something simple, the Python socket library is sufficient (i.e. sending and receiving occassional messages to printers and other devices), but if you have a need for speed, the java libraries are better. The post-V2.1 Python libraries perform much better, but you’ll find the 2.1 and earlier ones to be sort of lacking.
Someday I’ll sit down and write a module, but it works so well this way that I have bumped it down lower in the priority list.
Close the server socket that your asynchronous task is accepting on, and the blocked call to accept() will throw an IOException that should exit your function, ending the task.
I have been doing some research. What if I use a global variable and non blocking? I could then update the global variable and the thread would check the variable value in order to exit
yeah, that could work. by “non-blocking” did you mean java NIO? If so, you may want to reconsider just to avoid the deep and tangled complexity pit you’re hurling yourself into.
this is what i came up with for NIO. Does my NIO solution have any performance advantage over the IOException method? or vice versa?
def TCPServer():
from java.nio import *
from java.nio.channels import *
from java.nio.channels.spi import *
from java.net import *
import time
from java.util import *
from java.nio.charset import CharsetDecoder
from java.nio.charset import Charset
import system
global KEEP_RUNNING
#this stuff is used for reading and writing to a Java ByteBuffer
buf = ByteBuffer.allocateDirect(1024)
charset = Charset.forName("US-ASCII")
decoder = charset.newDecoder()
enc = charset.newEncoder()
#create sever socket channel
sChannel = ServerSocketChannel.open()
#bind server socket channel to ip and port
sChannel.socket().bind(InetSocketAddress("192.168.1.101", 10005))
#configure the server socket channel to not block
sChannel.configureBlocking(0)
KEEP_RUNNING = 1
#keep checking for connections as long as global variable is still true
while(KEEP_RUNNING == 1):
print "Waiting for connections"
try:
#this is non blocking
sc = sChannel.accept()
except:
print "error in ServerSocketChanel accept()"
sChannel.close()
sc = None
buf.rewind()
buf.clear()
if (sc == None): #no client has tried to connect
time.sleep(10)
else: #client has connected
try:
sc.configureBlocking(0) #makes the client connection non blocking when trying to read data
print "Incoming connection from: " + str(sc.socket().getRemoteSocketAddress())
except:
print "error in configuring non blocking"
sc = None
buf.rewind()
buf.clear()
try:
currentData = ""
buf.rewind() #makes sure bytebuffer is at the beginning
sc.write(enc.encode(CharBuffer.wrap("S\r\n"))) #write data to bytebuffer to be sent to client
numRead = sc.read(buf) #read data from bytebuffer
#keep reading as long as global variable is still true and the end of message has not been reached
while(KEEP_RUNNING == 1 and currentData.endswith("\r\n") == 0):
if(numRead != 0):
print "numRead = "+str(numRead)
time.sleep(2)
sc.write(enc.encode(CharBuffer.wrap("S\r\n")))
numRead = sc.read(buf)
bufPosition = buf.position() #get bytebuffer position
buf.rewind() #move to beginning of bytebuffer
currentData = decoder.decode(buf).toString() #get the current data from bytebuffer as string
buf.position(bufPosition) #put the bytebuffer position back to end to continue reading
print "buf position="+str(bufPosition)
currentData = currentData[:bufPosition] #gets ride of trailing white space from bytebuffer
print currentData
time.sleep(5)
decoder.reset()
bufPosition = buf.position()
buf.rewind()
print decoder.decode(buf).toString()[:bufPosition]
buf.clear()
sc.close()
except:
print "error in read"
sc = None
buf.rewind()
buf.clear()
print "Thread stopped"
sChannel.close()
system.util.invokeAsynchronous(TCPServer)
Well at first glance that code looks OK, but NIO code is notoriously complex to get right.
Any performance advantage you may gain (this is actually controversial) is totally counteracted by the way you’re using sleeps to wait for more data in the same thread that accepts new connections.
That said, I believe that you’re writing a serial-over-ethernet protocol, so I’d say that performance is not important at all.
Basically, use whatever works, but I’m warning you that if your NIO starts giving you funky behavior, the troubleshooting is hellish.
To keep track of a traditional server socket you could just make it global (just like your KEEP_RUNNING flag) in order to shut the socket down.