I'm working on a project, where I have to connect Python and C# in a rather flexible way, with information being send from one to the other frequently. Most of the time, the data goes back and forth (A works with data, sends result to B, B works with result, answers A) but not necessary always.
Named pipes seemed like the way to go but I'm struggling with keeping it all synchronous.
Currently I'm just starting a server on one of them and let it wait for a client connection, while the other creates a server as well, and connects as a client when the server is ready.
It's a lot of creating servers and clients and trying to not break it, which happens as soon as a client tries to connect to an not existing server..
I'd like to do it with a pipe that runs in a separate thread, but couldn't find a way to keep it alive, it simply closes, when the client is done reading.
Are named pipes just too simplistic for such a task, or is there a way to make this work robustly?
Update:
For reference, how my code looks like (reduced to the minimum here):
First the NamedPipe Server and Client in C#
public static void SendDataToPython(string payload)
{
using (var server = new NamedPipeServerStream("Test"))
{
server.WaitForConnection();
using (var stream = new MemoryStream())
using (var writer = new BinaryWriter(stream))
{
writer.Write(payload);
server.Write(stream.ToArray(), 0, stream.ToArray().Length);
}
server.Disconnect();
}
}
public static string WaitForMessage()
{
string message = "";
string servername = ".";
string pipeName = "CSServer";
using (var pipeClient = new NamedPipeClientStream(servername, pipeName, PipeDirection.In))
{
pipeClient.Connect();
And in Python:
class PipeServer():
def __init__(self, pipeName):
self.pipe = win32pipe.CreateNamedPipe(
r'\\.\pipe\\' + pipeName,
win32pipe.PIPE_ACCESS_DUPLEX,
win32pipe.PIPE_TYPE_MESSAGE | win32pipe.PIPE_READMODE_MESSAGE | win32pipe.PIPE_WAIT,
1, 65536, 65536,
0,
None)
def connect(self):
win32pipe.ConnectNamedPipe(self.pipe, None)
def write(self, message):
win32file.WriteFile(self.pipe, message.encode() + b'\n')
def close(self):
win32file.CloseHandle(self.pipe)
using (StreamReader sr = new StreamReader(pipeClient))
{
string temp;
while ((temp = sr.ReadLine()) != null)
{
message += temp;
}
}
}
return message;
}
class PipeClient:
def __init__(self, pipeName):
self.pipeName = pipeName
def receive(self):
file_handle = win32file.CreateFile(
f"\\\\.\\pipe\\{self.pipeName}",
win32file.GENERIC_READ | win32file.GENERIC_WRITE,
0,
None,
win32file.OPEN_EXISTING,
0,
None)
left, data = win32file.ReadFile(file_handle, 4096)
print(data.decode("utf-8")[1:])
Related
I am trying to send a file from python client to a c# server and present it on screen by saving it first and then showing it on my MainWindow.
I came across a couple of problems I can't figure out why happen (I'm new to C#)
I followed this guide : http://snippetbank.blogspot.com/2014/04/csharp-client-server-file-transfer-example-1.html
The problems were :
1. The file was not being saved to my folder.
2. when I used message box to try and detect if it passes all the info it looks like it gets stuck in the middle .
I've been stuck on this for quite some time now but can't figure out what I'm missing
Python code :
def send_file(conn, name):
try:
full_path = "Output/"+name
file_to_send = open(full_path, "rb")
size = os.path.getsize(full_path)
file_name = name + "\n"
size_to_send = str(size) + "\n"
conn.send(size_to_send.encode())
conn.send(file_name.encode())
while size > 0:
data = file_to_send.read(1024)
conn.send(data)
size -= len(data)
file_to_send.close()
return conn
except IOError:
print("FILE IS ALREADY OPEN")
C# CODE :
public static string ReceiveFile(StreamReader reader, TcpClient tcpClient)
{
string folder = #"C:\Users\Roy\Desktop\GUI243\GUI243\";
// The first message from the client is the file size
string cmdFileSize = reader.ReadLine();
MessageBox.Show(cmdFileSize);
// The first message from the client is the filename
string cmdFileName = reader.ReadLine();
MessageBox.Show(cmdFileName);
string full_path = folder + cmdFileName;
int length = Convert.ToInt32(cmdFileSize);
byte[] buffer = new byte[length];
int received = 0;
int read = 0;
int size = 1024;
int remaining = 0;
// Read bytes from the client using the length sent from the client
while (received < length)
{
remaining = length - received;
if (remaining < size)
{
size = remaining;
}
read = tcpClient.GetStream().Read(buffer, received, size);
if (read == 0)
{
break;
}
received += read;
}
// Save the file using the filename sent by the client
using (FileStream fStream = new FileStream(Path.GetFileName(cmdFileName), FileMode.Create))
{
fStream.Write(buffer, 0, buffer.Length);
fStream.Flush();
fStream.Close();
}
return full_path;
}
In your C# code looks like a while cycle is missing, unless you call the function ReceiveFile() iterating somewhere else.
The class StreamReader needs to constantly check the tcp client socket to see if new data has been received because this is how a TCP stream works.
You don't know when the client will connect and send the data so you can't just call the ReceiveFile() function once.
In the example there is a perpetual while (true) cycle that makes that reader.Readline() work:
while (true)
{
// Accept a TcpClient
TcpClient tcpClient = tcpListener.AcceptTcpClient();
Console.WriteLine("Connected to client");
StreamReader reader = new StreamReader(tcpClient.GetStream());
// The first message from the client is the file size
string cmdFileSize = reader.ReadLine();
...
}
Do you have an equivalent in your code?
Also, is not pratical to use MessageBox for debugging purposes.
Try:
Debug.WriteLine ("Expected size is: " + cmdFileSize);
Note you need System.Diagnostics to use that.
My (Python) publisher:
import zmq
import time
context = zmq.Context()
socket = context.socket(zmq.PUB)
connectStr = "tcp://*:%d" % 5563
socket.bind(connectStr)
messageNum = 0
while True:
++messageNum
message = "Testing %d"%messageNum
print("Sending.. '%s'"%message)
socket.send_string(message)
time.sleep(1)
messageNum += 1
My (C++) subscriber (running in GTest):
TEST(ZeroMqPubSubTest, SubscribeGetsData)
{
// Set up the subscriber we'll use to receive the message.
zmq::context_t context;
zmq::socket_t subscriber(context, ZMQ_SUB);
// Connect to the publisher
subscriber.connect("tcp://127.0.0.1:5563");
subscriber.setsockopt(ZMQ_SUBSCRIBE, ""); // Set the filter blank so we receive everything
zmq::message_t response(0);
EXPECT_TRUE(subscriber.recv(&response));
}
I start up the publisher then start up the subscriber. The latter never returns though.
If I run a Python subscriber doing (I thought) exactly the same thing..
import zmq
context = zmq.Context()
socket = context.socket(zmq.SUB)
socket.connect ("tcp://127.0.0.1:5563")
socket.setsockopt_string(zmq.SUBSCRIBE, "")
print ("Waiting for data...")
while True:
message = socket.recv()
print ("Got some data:",message)
..it works fine:
Waiting for data...
Got some data: b'Testing 8'
Got some data: b'Testing 9'
There are two overloads of setsockopt defined in zmq.hpp:
template<typename T> void setsockopt(int option_, T const& optval)
{
setsockopt(option_, &optval, sizeof(T) );
}
inline void setsockopt (int option_, const void *optval_, size_t optvallen_)
{
int rc = zmq_setsockopt (ptr, option_, optval_, optvallen_);
if (rc != 0)
throw error_t ();
}
By providing only two arguments you implicity used the first overload, which assumes a value length of sizeof(T). This resolves to one, because "" is a zero-terminated character array. To pass in an empty string you need to use the second overload and specify a length of 0:
subscriber.setsockopt(ZMQ_SUBSCRIBE, "", 0);
Alternatively, use a zero size data type:
char none[0];
subscriber.setsockopt(ZMQ_SUBSCRIBE, none);
I am beginner in python written first program two days ago. I am having connection problem in python client and C server for AF_UNIX. I have C socket Server with AF_LOCAL.
#define NAME "#/tmp/kvsd"
int
main()
{
int sock, msgsock, rval;
struct sockaddr_un server;
char buf[1024];
unlink(NAME);
printf("before socket \n");
sock = socket(AF_LOCAL, SOCK_STREAM, 0);
if (sock < 0) {
perror("opening stream socket");
exit(1);
}
memset(&server, 0, (sizeof (server)));
server.sun_family = AF_LOCAL;
memcpy(server.sun_path, NAME, strlen(NAME));
server.sun_path[0] = 0;
printf("before bind \n");
int len = strlen(server.sun_path) + sizeof(server.sun_family);
if (bind(sock, (struct sockaddr *) &server, len)) {
perror("binding stream socket");
exit(1);
}
printf("before listen \n");
if (listen(sock, 5) == -1) {
perror("listen");
exit(1);
}
printf("before accept \n");
msgsock = accept(sock, 0, 0);
printf("accepted \n");
if (msgsock == -1)
perror("accept");
else do {
bzero(buf, sizeof(buf));
printf("before read \n");
if ((rval = read(msgsock, buf, 1024)) < 0)
perror("reading stream message");
else if (rval == 0)
printf("Ending connection\n");
else
printf("-->%s\n", buf);
} while (rval > 0);
close(msgsock);
close(sock);
unlink(NAME);
}
And Python AF_UNIX client.py:-
####### CLIENT CODE #######
from socket import *
# Create an unbond and not-connected socket.
sock = socket(AF_UNIX, SOCK_STREAM)
# Connect to the peer registered as "MyBindName" in the abstract namespace. Note the '\0'.
str = "\0/tmp/kvsd\0"
print "len ", len (str)
sock.connect("\0/tmp/kvsd")
# Wait for message
msg = sock.recv(100)
print msg
# Send reply
sock.send("Hi there!\n")
# Block until new message arrives
msg = sock.recv(100)
# When the socket is closed cleanly, recv unblocks and returns ""
if not msg:
print "It seems the other side has closed its connection"
# Close it
sock.close()
But When I run the client I'm getting following error:
[root#mat afunix]# python ./client.py len 11 Traceback (most recent call last): File "./client.py", line 13, in sock.connect("\0/tmp/kvsd") File "", line 1, in connect socket.error: [Errno 111] Connection refused [root#mat afunix]#
I am trying to use the abstract namespaces for UNIX socket but my python client is not able to connect to c server.
I tried without abstract namespaces it works. (changed NAME macro in server.c to "/tmp/kvsd" and argument to sock.connect to "/tmp/kvsd").
Can someone help me to figure out what may be the exact issue ?
Thanks in advance.
Following line has a problem.
int len = strlen(server.sun_path) + sizeof(server.sun_family);
server.sun_path has now leading null character. So strlen(server.sun_path) is 0. You need change above line as follow:
#include <stddef.h>
....
int len = offsetof(struct sockaddr_un, sun_path) + strlen(NAME);
Then, it will work.
EDIT: updated the code to use offsetof to avoid padding issue. (Thank you, alk)
PS: I assume that both server, client use name without trailing null byte. If you use name with trailing null byte, add 1 to len.
I would like to set up a serial communication between a Python daemon and an Arduino.
At first, the Python daemon sets up a serial connection that will last for the whole lifetime of the daemon. Through this connection, I would like to send data to the Arduino and receive back data in the acks variable every time the Python daemon receives commands.
The problem is that while the first time the communication goes well, nothing is sent through serial afterwards. If I make the a new connection for every request it works, but it makes the program very slow, which I'd like to avoid.
edit: the real issue is when send a correct string to the arduio evrything goes well but when i send a wrong one the serial port block and it will never reconize corrct strings again( the problem is in the arduino code)
Python code:
import serial
import time
import sys
from socket import *
import threading
import thread
def handler(clientsock,addr):
while 1:
#arduino.flush()
data = clientsock.recv(BUFSIZ)
if not data:
break
print data
print data
#time.sleep(3)
arduino.write(data)
#time.sleep(3)
ack = arduino.readline(1)
arduino.flush()
clientsock.send(ack+"\n")
clientsock.close()
if __name__=='__main__':
HOST = '0.0.0.0'
PORT = 21567
BUFSIZ = 1024
ADDR = (HOST, PORT)
arduino = serial.Serial('/dev/ttyACM0',9600,timeout=6)
serversock = socket(AF_INET, SOCK_STREAM)
serversock.bind(ADDR)
serversock.listen(2)
while 1:
print 'waiting for connection...'
clientsock, addr = serversock.accept()
print '...connected from:', addr
thread.start_new_thread(handler, (clientsock, addr))
Arduino code:
int relayPinCH1 = 7; // pin de commande du relais 1
char inData[20]; // Allocate some space for the string
char inChar=-1; // Where to store the character read
byte index = 0; // Index into array; where to store the character
void setup()
{
pinMode(relayPinCH1, OUTPUT);
Serial.begin(9600);
}
char Comp(char* This) {
while (Serial.available() > 0) // Don't read unless
// there you know there is data
{
if(index < 19) // One less than the size of the array
{
inChar = Serial.read(); // Read a character
inData[index] = inChar; // Store it
index++; // Increment where to write next
inData[index] = '\0'; // Null terminate the string
}
}
Serial.flush();
if (strcmp(inData,This) == 0) {
for (int i=0;i<19;i++) {
inData[i]=0;
}
index=0;
return(0);
}
else {
return(1);
}
}
void loop()
{
//Serial.println("Hello Pi");
if (Comp("l11\n")==0)
{
Serial.flush();
digitalWrite(relayPinCH1, HIGH);
Serial.println("y");
}
if (Comp("l10\n")==0)
{
Serial.flush();
digitalWrite(relayPinCH1, LOW);
Serial.println("n");
}
delay(1000);
}
In your Arduino code, your logic is kind of funky - so, I'm not sure, but are you clearing index to 0 before you start the loop again? It looks like once index == 19, it may or may not get reset to 0 depending upon later logic. If you enter Comp() a second time and index >= 19 then you'll never read the serial port again.
I think #Zeus is entirely right (and hence I upvoted that answer), but there are also other problems. To reiterate what #Zeus is saying:
index is only reset to 0 if the comparison succeeds. So your buffer is full, the string you are looking for isn't there, and index never goes back to 0 again.
Once index reaches 19, no more reading is done. As a result, whatever is in inData stays in inData and all the future comparisons will fail, meaning index will never get reset to 0.
There are a number of other problems in the code, but the main issue is that the design is very fragile, and prone to exactly the sort of error you are experiencing. For instance if the newlinews your Python script is sending are CR+LF for newlines, but you are expecting CR only, you'll have the same sort of failure you have now: first time communications work, but never again.
I would suggest reorganizing your code like this:
Your function for reading serial port reads a line from a serial port and returns that to the caller (without the newlines), regardless of the content of the communications.
The caller compares the line received from the serial port with the list of known commands and executes them accordingly.
This might look rougly as follows
char strCommand[0xFF];
int idxCommandChar;
// Read a command from serial, returning the command size
// This function BLOCKS, i.e., doesn't return until a command is available
int readSerialCommand() {
// We reset the index to zero on every read: the command is overwritten every time
idxCommandChar = 0;
// Read serial characters and store them in strCommand
// until we get a newline
int in = Serial.read();
while (in!='\n') {
strCommand[idxCommandChar++] = in;
in = Serial.read();
}
// Add the string terminator
strCommand[idxCommandChar++] = '\0';
// Return command size
return idxCommandChar;
}
// Get command from serial, and process it.
void processCommand() {
readSerialCommand();
if (strcmp(strCommand, "CMD1")==0) {
// do something
} else if (strcmp(strCommand, "CMD2")==0) {
// do something else
} else {
// Unknown command
Serial.println("Unknown command");
}
}
void loop() {
processCommand();
delay(1000);
}
This code blocks on serial, i.e. doesn't return until a newline is detected. You could easily modify the code to be non-blocking, perhaps like this:
/* Read serial characters, if available and store them in strCommand
until we get a newline
Returns 0 if no command is available */
int readSerialCommand() {
idxCommandChar = 0;
while (Serial.available()) {
int in = Serial.read();
while (in!='\n') {
strCommand[idxCommandChar++] = in;
in = Serial.read();
}
strCommand[idxCommandChar++] = '\0';
return idxCommandChar;
}
return 0;
}
// Get command from serial (if available), and process it.
void processCommand() {
if (readSerialCommand()) {
....
In either case you might loose serial characters while you are waiting, so you may want to rethink that strategy.
I need to receive data from device connected via Ethernet (modbus/TCP) and send it to webpage (maybe using web sockets).
I can't find good examples. Now I can connect with driver and print values using ModbusClientProtocol.read_input_registers() but I had to create own factory and protocol class. I am using autobahn, twisted, pymodbus.
I've no familiarity with modbus or pymodbus, so I'm guessing and leaving a lot of blanks for you to fill in.
This is hacked out of something I recently put together to receive snmptraps and redistribute the information to connected websockets.
Hopefully this is enough to get you going:
#!/usr/bin/python
from twisted.internet import protocol, reactor, utils, defer
from twisted.web.server import Site
from twisted.web.static import File
from autobahn.websocket import WebSocketServerFactory, WebSocketServerProtocol
from autobahn.util import newid
from autobahn.resource import WebSocketResource
class ModbusThing(object):
def __init__(self,clientAddress):
self.clientAddress = clientAddress
self.client = None
def start(self):
pass
## Create client connection to modbus server
## Start Looping Call of pollForData with suitable interval
def pollForData(self):
pass
## Call read methods on ModbusClient object, add call backs to process the results
## Add errorBacks to notify of errors
def resultCallback(self,result):
pass
## Process the data from a read request
## Assumes that your websocket clients expect json like {"event":"update","data":[0,1,2]}
message = dict(event="update",data=processedResults)
self.broadcast(json.dumps(message))
def broadcast(self,msg):
"""Override me"""
pass
class TrackingWebSocketProtocol(WebSocketServerProtocol):
def onOpen(self):
self.session_id = newid()
self.factory._addSession(self,self.session_id)
print "Socket Open %s" % (self.peerstr,)
def onMessage(self,payload,isBinary):
print "Message received from %s\n\t: %r" % (self.peerstr,payload)
def onClose(self,wasClean,code,reason):
self.factory._removeSession(self)
print "Socket Closed %s" % (self.peerstr,)
class TrackingWebSocketFactory(WebSocketServerFactory):
def __init__(self,*args,**kwargs):
WebSocketServerFactory.__init__(self,*args,**kwargs)
self.proto2session = {}
self.session2proto = {}
def _addSession(self,proto,session_id):
if not self.proto2session.has_key(proto):
self.proto2session[proto] = session_id
else:
raise Exception("logic error - dublicate _addSession for protoToSessions")
if not self.session2proto.has_key(session_id):
self.session2proto[session_id] = proto
else:
raise Exception("logic error - dublicate _addSession for sessionsToProto")
def _removeSession(self,proto):
if proto in self.proto2session:
session_id = self.proto2session[proto]
del self.proto2session[proto]
if session_id in self.session2proto:
del self.session2proto[session_id]
def sendToAll(self,message,binary=False):
prepped = self.prepareMessage(message,binary)
for proto in self.proto2session.keys():
proto.sendPreparedMessage(prepped)
def run():
## WebSocket Factory
wsfactory = TrackingWebSocketFactory('ws://yourhostname:80')
wsfactory.protocol = TrackingWebSocketProtocol
wsresource = WebSocketResource(wsfactory)
## Modbus handler
modbus_thing = ModbusThing((addressofserver,portofserver))
modbus_thing.broadcast = wsfactory.sendToAll
modbus_thing.start()
## WebServer Site
# "static" subdirectory, containing http served resources, e.g. index.html, javascript and css
root = File("static")
# Your websocket service as 'ws://yourhostname/ws'
root.putChild("ws", wsresource)
site = Site(root)
reactor.listenTCP(80,site)
def main():
reactor.callWhenRunning(run)
reactor.run()
if __name__=='__main__':
main()
On the browser side of things. A little module for interacting with websockets is handy:
var FancyWebSocket = function(url){
var conn = null;
var fws = this;
if ("WebSocket" in window) {
conn = new WebSocket(url);
} else if ("MozWebSocket" in window) {
conn = new MozWebSocket(url);
} else {
console.log("Error Websockets not supported in browser");
return;
}
var callbacks = {};
var debug = true;
this.bind = function(event_name, callback){
callbacks[event_name] = callbacks[event_name] || [];
callbacks[event_name].push(callback);
return this;// chainable
};
this.send = function(event_name, event_data){
var payload = JSON.stringify({event:event_name, data: event_data});
conn.send( payload ); // <= send JSON data to socket server
return this;
};
this.close = function(){ conn.close(); return this;}
// dispatch to the right handlers
conn.onmessage = function(evt){
if (debug) console.log("Websocket(" + conn.URL + ") Message: " + evt.data)
var json = JSON.parse(evt.data)
dispatch(json.event, json.data)
};
conn.onclose = function(){
if (debug) console.log("Websocket(" + conn.URL + ") Closed");
dispatch('close',fws);
}
conn.onopen = function(){
if (debug) console.log("Websocket(" + conn.URL + ") Open");
dispatch('open',fws);
}
conn.onerror = function(e){
if (debug) console.log("Websocket(" + conn.URL + ") Error: " + error);
dispatch('error',fws,e);
}
this.setdebug = function(v) { debug=v; return this; }
var dispatch = function(event_name, message){
var chain = callbacks[event_name];
if(typeof chain == 'undefined') return; // no callbacks for this event
for(var i = 0; i < chain.length; i++){
chain[i]( message )
}
}
};
Then in your browser console:
conn = new FancyWebSocket("ws://yourhostname/ws");