I have a device I need to talk to via UDP from within PHP (although it feels like my problem here is a Linux one not a PHP one as everything is being done at the socket level anyway).
I am sending a request to an Ethernet module in a PLC, and if I get the addressing wrong in my request then I get an error response back from the PLC as expected (by which I mean a 22 byte message sent via UDP containing the relevant error code) - I see this in my PHP code and in a Windows (Winsock-based) app I have that lets me send test queries and see the responses.
However, when I get my addressing correct, whilst the Windows app gives me the response associated with the request I made, in my PHP/Linux code I just get no response (my code hangs up waiting).
The PHP code I'm struggling with is: // Create a socket $socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); // Build the message $msg = hex2str('80 00 02 00 01 00 00 11 00 00 05 01'); // Send the message socket_sendto($socket, $msg, strlen($msg), 0, '192.168.251.132', '9600'); // Get the response - this line never returns socket_recvfrom($socket, &$buf, /*Max response length*/250, /*Flags*/0, &$ip, &$port);
I would guess that the Ethernet module in the PLC either responds immediately with a response to tell me I have the address wrong, or else has to go off and collect some data before sending it back, although the time this takes is milliseconds.
If I set Flags to MSG_DONTWAIT the code no longer hangs; it just returns no characters, even if I put a delay of a couple of seconds before the function call. I'm pretty sure that the key to the solution lies in getting the flags right but there I'm stumped due to lack of knowledge (and everywhere I can find sample code the above works fine). The list of possible flags is in the PHP docs[1] but I think a fuller list would be those listed in "man 2 recv"
Any suggestions?
[1]http://www.php.net/manual/en/function.socket-recvfrom.php
On 16/03/12 12:22, Mark Rogers wrote:
I have a device I need to talk to via UDP from within PHP (although it feels like my problem here is a Linux one not a PHP one as everything is being done at the socket level anyway).
I just tried this on PHP on Windows (albeit under VirtualBox on Linux) and it fails the same way. So it's not a Linux issue.
Anyone care to help me "convert" the PHP code to another language that I can test under Linux? It would be nice to see whether it's a PHP issue. (Can I do something similar from within bash?)
I have a suspicion that the Winsock DLLs under Windows are doing something that I'm not doing, but I have no idea what that might be.
The PHP code I'm struggling with is: // Create a socket $socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); // Build the message $msg = hex2str('80 00 02 00 01 00 00 11 00 00 05 01'); // Send the message socket_sendto($socket, $msg, strlen($msg), 0, '192.168.251.132', '9600'); // Get the response - this line never returns socket_recvfrom($socket, &$buf, /*Max response length*/250, /*Flags*/0, &$ip, &$port);
Scratch this thread.
I got the code translated to Python (never used Python before but it wasn't hard once I found some UDP sample code). Same problem.
Finally tracked it down: I needed to bind to port 9600 at my end to receive the data.
On 16 Mar 13:00, Mark Rogers wrote:
On 16/03/12 12:22, Mark Rogers wrote:
I have a device I need to talk to via UDP from within PHP (although it feels like my problem here is a Linux one not a PHP one as everything is being done at the socket level anyway).
I just tried this on PHP on Windows (albeit under VirtualBox on Linux) and it fails the same way. So it's not a Linux issue.
Anyone care to help me "convert" the PHP code to another language that I can test under Linux? It would be nice to see whether it's a PHP issue. (Can I do something similar from within bash?)
I have a suspicion that the Winsock DLLs under Windows are doing something that I'm not doing, but I have no idea what that might be.
The PHP code I'm struggling with is: // Create a socket $socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); // Build the message $msg = hex2str('80 00 02 00 01 00 00 11 00 00 05 01'); // Send the message socket_sendto($socket, $msg, strlen($msg), 0, '192.168.251.132', '9600'); // Get the response - this line never returns socket_recvfrom($socket, &$buf, /*Max response length*/250, /*Flags*/0, &$ip, &$port);
Are you *sure* that there is going to be a return packet, and that your send packet *definitely* arrived at the other end?
The equiv in python is something like:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.settimeout(4)
msg_string = '80 00 02 00 01 00 00 11 00 00 05 01' msg = "".join([chr(int(b,16)) for b in msg_string.split(' ')]
s.connect(('192.168.251.132', 9600)) s.send(chars) data,addr = s.recvfrom(250)
Hope that helps,
On 16/03/12 13:40, Brett Parker wrote:
Are you *sure* that there is going to be a return packet, and that your send packet *definitely* arrived at the other end?
I'm as sure as I can be that it gets there, and it should definitely illicit a response. However, see later follow-up: I needed to bind to port 9600 to receive it.
I'd like to understand why this is though, given that it wasn't necessary for receiving the error messages.
The equiv in python is something like:
Thanks, it's more elegant than the code I hacked together!
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.settimeout(4)
These look like configuration steps I should look into though.
Thanks for your help.
M
On 16 Mar 14:20, Mark Rogers wrote:
On 16/03/12 13:40, Brett Parker wrote:
Are you *sure* that there is going to be a return packet, and that your send packet *definitely* arrived at the other end?
I'm as sure as I can be that it gets there, and it should definitely illicit a response. However, see later follow-up: I needed to bind to port 9600 to receive it.
I'd like to understand why this is though, given that it wasn't necessary for receiving the error messages.
The equiv in python is something like:
Thanks, it's more elegant than the code I hacked together!
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.settimeout(4)
These look like configuration steps I should look into though.
I should probably admit to the code having mostly been stolen from when I wrote some discovery tools having wiresharked the traffic sent from the windows apps, and so I had that code kicking about on the laptop already - and in that, too, you had to be bound to the right port else you didn't get a response :)
(The code in question will, mostly, just about, detect cheap managed netgear switches and cheap ip power devices... I never did get the time to finish it and make it neat, or debug why it didn't work for someone else... broadcast UDP packets are fun though, and getting responses back from more than one place is also fun :)