Use RFC2217 network serial ports with Twisted Python? - python

Is there a way to connect to an RFC2217 networked serial port with Twisted Python?
Pyserial seems to support it via the serial.serial_for_url("rfc2217://...") function. And they indicate that twisted uses pyserial for managing serial connections, however twisted.internet.serialport.SerialPort seems to expect a port name or number which suggests it is just passing this to the serial.Serial constructor.
I can use socat to create a PTY externally and pass the dev name to twisted which works fine, but I was wondering if I can bypass this step by using the pyserial support directly.
socat PTY,link=/dev/myport TCP:192.168.1.222:9001
Edit: The pyserial faq suggests this modification for instantiating serial objects:
try:
s = serial.serial_for_url(...)
except AttributeError:
s = serial.Serial(...)
Not sure if this helps though...

I have come to the conclusion that using Pyserial's RFC2217 support with Twisted Python is non-trivial. Pyserial's implementation of RFC2217, besides being currently experimental, uses threads to manage the socket connection which they state as being a problem for select based applications:
The current implementation starts a thread that keeps reading from the (internal) socket. The thread is managed automatically by the rfc2217.Serial port object on open()/close(). However it may be a problem for user applications that like to use select instead of threads.
It is fairly straight forward to subclass t.i.serialport.SerialPort and overwrite the _serialFactory method (which creates the pyserial object to be used for accessing the serial port)
class SerialPort(serialport.SerialPort):
def _serialFactory(self, dev, *args, **kwargs):
" pyserial recommends the following for supporting serial urls "
try:
return serial.serial_for_url(dev)
except AttributeError:
return serial.Serial(dev, *args, **kwargs)
However, the resulting object lacks a file descriptor and so the fileno() method (used internally by t.i._posixserialport) throws an exception.
--- <exception caught here> ---
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/twisted/internet/base.py", line 1204, in mainLoop
self.doIteration(t)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/twisted/internet/selectreactor.py", line 105, in doSelect
[], timeout)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/twisted/internet/_posixserialport.py", line 48, in fileno
return self._serial.fd
exceptions.AttributeError: 'Serial' object has no attribute 'fd'
The current workarounds are to either use socat as described in the question, or for the network serial server I'm using (Brainboxes ES-842) you can configure it in "Raw TCP" mode instead of "Telnet/RFC2217" mode and just use your existing protocol over a TCP connection (as long as you're not depending on flow control or other serial control lines and can use a predefined fixed baud rate).

Related

Blocking an ip from joining a socket server [duplicate]

I have a python socket server that listens on a port, and accepts all incoming connections using:
(conn, address) = socket.accept()
However, I wish to accept connections only from certain ip address.
Currently, I close the connection if the address isn't registered, to accomplish this.
But is there a better way to do this, by directly rejecting connections from unregistered addresses, instead of accepting connections and then closing them?
It's not possible to indicate Connection refused to clients from some IP addresses, and to establish the connection to clients from other IP addresses. This is not a Python limitation, but a lower-level, BSD socket layer limitation. You can't do it even from C.
The closest behavior in general you can do in Python is closing the connection quickly after it has been accepted:
sock, addr = server_socket.accept()
if addr[0] != '12.34.56.78':
sock.close()
return
...
Then the client would see the connection being accepted, and very shortly after that the client would see EOF when reading from it, and it wouldn't be able to write to it.
However it's possible to limit by interface (i.e. network card) at bind time, by using one of:
server_socket.bind(('', 65432)) # Bind on any interface.
server_socket.bind(('127.0.0.1', 65432)) # Bind on loopback (localhost clients only).
server_socket.bind(('34.56.78.91', 65432))
So in the 127.0.0.1 version, telnet 127.0.0.1 65432 (as a client) would work, but telnet myhostname 65432 would yield Connection refused (and the server_socket.accept() call won't get this connection).
If you read the docs you can find the BaseServer.verify_request(request, client_address) which tells you this:
Must return a Boolean value; if the value is True, the request will be processed, and if it’s False, the request will be denied. This function can be overridden to implement access controls for a server. The default implementation always returns True.
Microsoft appears to support this functionality via the SO_CONDITIONAL_ACCEPT socket option
This appears to require usage of WSAAccept to accept connections
This constant does not appear in pythons socket module on my windows 8 machine. I don't think there is an option to use WSAAccept via python's builtin socket module.
If I understand correctly, this will allow your server to respond to SYN packets immediately with RST packets when configured to do so instead of finishing the handshake and exchanging FIN packets. Note that usage of this flag removes responsibility to handle connections from the operating system and places it on the application, so there is plenty of room for errors and performance hits to occur. If a performance boost was the goal, it might not be not worth pursuing
It is possible to do at the C level on windows. Pythons ctypes module allows interfacing with C code, so it is technically possible to do via a python interface. But it likely requires a non trivial amount of effort. If you are certain you require this feature, it may be less effort to find a C socket library that supports this out of the box, then you could make a ctypes wrapper for that.

How to open a socket after closing it?

I tried with opening the socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sending a message, and then closing it
sock.close()
but I don't know how to open it again. I use UDP socket, and the sock.connect() is a TCP command I think... leastways I tried it but it caused error.
You can't reopen a socket. As close explains:
The underlying system resource (e.g. a file descriptor) is also closed when all file objects from makefile() are closed. Once that happens, all future operations on the socket object will fail.
This is inherent in the BSD sockets model: closing a socket means the kernel throws away all resources related to that socket and releases the file descriptor. As POSIX defines it:
If fildes refers to a socket, close() shall cause the socket to be destroyed.
If you try to use that file descriptor again, you get an EBADFD error if you're lucky—and if you're unlucky, you get some completely different socket or file that reused the same number. In fact, this is true for all file descriptors, not just sockets. Quoting POSIX again:
Once a file is closed, the file descriptor no longer exists, since the integer corresponding to it no longer refers to a file.
What you can do, of course, is create a brand new socket, the same way you created the first one:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
You may end up with a different file descriptor, but for most sockets code, that won't make a difference.
Or, of course, you can just not close the socket. Whatever reason you had for wanting to close the socket, the fact that you want to use it again implies that you were probably wrong.

Inter-Process Communications between Python and C++ using local file sockets?

I have a c++ program which creates a local socket bound to "/tmp/.mysocket" and waits to receive data from that socket. The way it is set up, raw binary data would be sent to the socket and loaded into the following C++ structure:
struct StateVariable
{
char Name[64];
int E[8];
};
The C++ program listens for input using recvfrom:
int nBytes = recvfrom(SD,&DataReceived,sizeof(StateVariable)/sizeof(char),0,(sockaddr*)&SentFrom,&size);
The server is just using the standard AF_UNIX and SOCK_DGRAM to create the socket.
My questions is relating to python: How do I send data to the local socket bound to /tmp/.mysocket using python? I am not using AF_INET or opening a specific port for this.
I can use python's socket library to describe the socket, but I can't find any resource that discusses binding a socket to a file in python and sending data to that socket. The documentation for the socket library only discusses using AF_INET and SOCK_DGRAM for local sockets bound to a port number at 127.0.0.1, but I'm not doing that.
How do I get python to send data to a socket bound to a file? Is there an example python program that does just that (maybe a client/server pair that demonstrates this functionality)? As long as I can get python to send data to a local file socket, I can figure out the rest.
The Python socket library is a fairly thin wrapper around the socket interface you're familiar with from C++. See socket documentation.
It's going to look something like this:
import socket
import struct
s = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
s.connect('/tmp/...')
s.send(struct.pack('64s8i', ...))
You just change AF_INET to AF_UNIX and then .connect(address) and/or .bind(address) methods accept path to a file (instead of (host, port) pair). Other then that everything else works pretty much the same.

Pysnmp Command responder wrapper

I want to develop simple pysnmp command responder to listen to my test instrument device, get requests came from the device and replace them with my own command (Telnet command)in my host PC (Ubuntu) and send it to evaluation board. In the past there was also support SNMP on HOST, I want to just keep the structure of exists PLUGIN on device and change only the Host side.
Please do not ask me why I do it that way.
I have prepared MIB file that works with the device in SNMP. PLUGIN installed on device knows how to work and send the MIB OID with the OID I know and according to the request need to replace by command of my own command on host PC.
Please see the flow for example get frequency from evaluation board:
Plugin in device send command to get frequency (MIB OID):
1.3.6.1.4.1.55555.1.1.5.1.1.2.1
Listen to Device (SNMPv1 port 161), get request(1.3.6.1.4.1.55555.1.1.5.1.1.2.1) for frequency from device and replace it in local PC Host(Ubuntu): get_frequency_mycommand_telnet
How to do it in the most simple with Python 2.7?
Let me suggest you basing your script on this example.
You could implement your telnet communication inside the __call__() of your custom MIB object instance. Make sure to set proper OID to make this object SNMP-addressable.
Also, add support for SNMP SET operation and probably pass incoming value into the __call__() or other method you may want to implement.
You may want to scratch IPv6 support if you do not need it.
Keep in mind that with this implementation __call__() is blocking so no other SNMP queries would be processed until __call__() is done. You could enhance it towards higher performance and concurrency by making your telnet client asynchronous.

Python indifference to serial / Ethernet

I'm trying to communicate with a device that uses the same protocol whether you're talking to it over a TCP socket or a serial port. Either way, it's an ASCII command-based interface, in which you type your command at a prompt, followed by a newline, you get a response with a newline, and then a new prompt.
> IDENTIFY
DEVICE_TYPE_RESPONSE
> TEST POWER
OK
>
The program can and should block until it gets the new prompt, but there needs to be a timeout so you don't wait forever in case of sudden device death. That timeout should be seconds so as not to falsely flag on a network glitch, but shouldn't force me to wait seconds once I've gotten my prompt character.
I'd love some way to abstract the interface so as to not care about what the underlying communications layer is. Just pass it either an open socket or an open serial port and let everything happen. Is there some good way to do this? Preferably capable of running under Python 2.6.
You could implement the protocol in Twisted which allows you to use TCP or the serial port as a transport without changing your protocol implementation. Twisted also allows you to set timeouts/delayed callbacks.
Python's serial package provides a lot of useful stuff including some serial over TCP/IP bridges. If you want to talk to it using ASCII/Telnet then you probably want the to use the '--convert' option with the Simple Serial to Network (TCP/IP) redirector.
Also you might want to take a look at this other related question on Converting serial port data to TCP/IP in a linux environment

Categories

Resources