TCP/IP Server in Ignition - Repeated connections

Hello all,

I am having issues with closing and re-opening TCP/IP connections using Ignition Edge as a server.
The aim is to have a Python script establish a TCP/IP server when performing an action on the Ignition program (e.g. pressing a button). A separate application would act as a client to receive the data.

Here is a simple version of the code:


import socket

#Define connection port and IP
IP = "10.0.0.1"  # Enter IP Address
port = 100  # Enter port

data = "123456789"

s = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)

s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

s.bind((IP, port))

s.listen(1)

system.gui.messageBox("Server waiting for connection")
connection, address = s.accept()
system.gui.messageBox("Client connected")
try:
    while True:
        received_message = connection.recv(1024)
        if not received_message:
            break
        try:
        	connection.sendall(data)
        except:
            system.gui.messageBox("Connection exited")
finally:
	s.shutdown(socket.SHUT_RDWR)
	connection.close()

This works very well if done once, the client is able to receive the data as intended. However, the goal is to be able to repeat this transaction multiple times (either leaving the socket open, or closing it and re-opening later would be be okay). Despite closing the socket, executing the script a second time will result in a "address already in use" exception being raised.
Using netstat in the common prompt causes shows that the communication is not present at the end of the messaging. If the close/shutdown statement is removed from the code, then netstat will show the connection as "CLOSE_WAIT" which indicates to me that the connection is being closed properly when using the close/shutdown statements. If Ignition is closed and reopen, then the script can be executed again without issues. However, this is not a practical solution.

I've tried adding the socket.SO_REUSEADDR arg in setsockopt, but to my understanding this is meant to reuse sockets stuck in TIME_WAIT state (which isn't the case here) and therefore has not helped.

Does anyone know a way to fix this issue without restarting the Ignition program or knows anything I could try?

Please edit your post and format your code so we can see the indentation, etc.

Thank you for the feedback, I was not aware of the functionality, just fixed the issue! Please let me know if you have any ideas on what I could try to remedy the repeated connection problem.

Any feedback for solving a similar issue ? @G_O

Hi Marc,

I ended up being unable to do it directly in Ignition. My guess is that the interface keeps the connection in memory despite it being closed and prevents new connections from being open with the same IP/port.

However, I found a workaround. I wrote a separate, stand alone Python script turned into a .exe file with more or less the same code as my first message to handle the communication (modified to fit my exact application). Using system.util.execute, you can open the executable from Ignition and handle the communication that way. This worked without issues.

I was about to link to one of @pturmel's posts about why you shouldn't use Python sockets in Ignition (they leak and leave connections open, in my experience) but it looks like he's typing something up about it right now.

1 Like

The fundamental problem with the original code is that it doesn't call s.accept() in an outer loop. It is not safe in Ignition to do that outside of an asynchronous thread, and not safe to have no way to kill the thread when a project is edited, but it is certainly doable within Ignition.

(I wouldn't ever use jython's socket implementation, either.)

1 Like

Hi Felipe, that would make sense, this is also what I'm seeing.

Hi Phil, thanks for the feedback! I actually had already tested this code in an asynchronous thread as well since not using threading would freeze the entire program in case of any issues during the communication which isn't safe or practical. I wrapped the code in my first post in a separate function and used system.util.invokeAsynchronous, not sure if this is what you meant. The same issue occurred but as you were saying it's probably due to the socket implementation. What would you recommend instead for TCP/IP communication? For now the stand alone Python script outside of Ignition seems like a suitable solution.

I recommend java.net or java.nio. For something simple like your case, start with ServerSocket.

Use one asynchronous thread that opens and binds the socket, in a limited retry loop if necessary for a WAIT to finish. Then listen in a loop for connections. For each connection, create another asynchronous thread to handle its traffic.

Always use timeouts. Regularly check within your loops for Thread.interrupted() and bail out if true. In gateway scope, use system.util.getGlobals() or system.util.globalVarMap() to keep track of running threads so they can be interrupted/killed when a project is saved/restarted.

Search this forum for advice on managing long lived threads.

1 Like

Got it, thank you for the detailed response! Appreciate it, I'll take a look at this method once I have the chance.