Maintaing socket connections across page requests
Moderator: General Moderators
Re: Maintaing socket connections across page requests
bump... anyone else have thoughts on this? I'm stumped. 
Re: Maintaing socket connections across page requests
I tried this out, and what fixed the problem was to terminate every command sent to the server with a newline.
I had a look at tcpdump while this was going on, and if I haven't made a mistake or misread the output, the line buffering is actually on the server side: the data is sent from the client and acknowledged by the server, but evidently not passed up to the PHP server process, which is still hanging on the socket_read.
Also, I don't know if you've tried the other two things I recommended, but they're still the best way of ensuring that the legs don't get cut from the tcp connection while the server is trying to read from it.
* Get the server to send back an acknowledgement of each command received and processed, and get the client to read that acknowledgement line before continuing.
* Use socket_close to close the connection. Otherwise PHP closes the connection abruptly by sending a reset packet.
I had a look at tcpdump while this was going on, and if I haven't made a mistake or misread the output, the line buffering is actually on the server side: the data is sent from the client and acknowledged by the server, but evidently not passed up to the PHP server process, which is still hanging on the socket_read.
Also, I don't know if you've tried the other two things I recommended, but they're still the best way of ensuring that the legs don't get cut from the tcp connection while the server is trying to read from it.
* Get the server to send back an acknowledgement of each command received and processed, and get the client to read that acknowledgement line before continuing.
* Use socket_close to close the connection. Otherwise PHP closes the connection abruptly by sending a reset packet.
Re: Maintaing socket connections across page requests
dml,
Thanks heaps for the advice and for checking out the code for me! I completely forgot about your suggestion on sending the Ok/n on each command.
Also, what is tcpdump? Is that something in PHP? I'm going to google it right after I finish this post, but feel free to expand on that and explain to me how you troubleshot this. Like I think I said in an earlier post. This is my very first attempt at any socket programming in any language, so I really, really appreciate all the help that I've gotten from the board (both you and Garcia).
I'm going to try these things first thing in the morning. I'll report back and let you know how it goes.
Thanks again!
Chris
Thanks heaps for the advice and for checking out the code for me! I completely forgot about your suggestion on sending the Ok/n on each command.
Also, what is tcpdump? Is that something in PHP? I'm going to google it right after I finish this post, but feel free to expand on that and explain to me how you troubleshot this. Like I think I said in an earlier post. This is my very first attempt at any socket programming in any language, so I really, really appreciate all the help that I've gotten from the board (both you and Garcia).
I'm going to try these things first thing in the morning. I'll report back and let you know how it goes.
Thanks again!
Chris
Re: Maintaing socket connections across page requests
Okay, I had a look at what tcpdump is, but when I run it I've got little clue what I'm looking at.
I'm running all of this on a windows machine, so I downloaded WinDump (and of course WinPcap), but according to the docs since WinDump is a port of tcpdump the command line switches are the same. Can you tell me what switches you're using to look at all this?
I've tried:
windump -i 2
windump -i 2 -A
windump -i 2 -q
I should say probably that at this moment, I'm doing all this from my laptop on the localhost. I've got PHP and Apache 2.2.x (whatever the latest is 2.2.9?) running on my local machine. The actual environment this will be running in is PHP running on a windows server 2003 box running IIS6. My client web interface will be on a remote machine and the server will be on a machine with a publicly accessible address. I don't know if any of this makes a difference in the end, but I thought it was worth mentioning.
I've modified my client code to look like this: (note the comments highlighted in green)
I've modified my server code to look like this: (note the comments highlighted in green)
You can see that I decided to leave the 'talkback' functionality in my little socket server. I figured that the best thing for me to do was to try and get the talkback part of this working (not that this is the end goal, just that it seems that it would be easy to get working as a test).
Things still aren't working though. Have I missed something in what you're trying to tell me? Sorry if I'm being dense about this. I really appreciate your help and patience.
Also, I'll make these changes in my real environment tomorrow to see if I get a different outcome.
Thanks again,
Chris
I'm running all of this on a windows machine, so I downloaded WinDump (and of course WinPcap), but according to the docs since WinDump is a port of tcpdump the command line switches are the same. Can you tell me what switches you're using to look at all this?
I've tried:
windump -i 2
windump -i 2 -A
windump -i 2 -q
I should say probably that at this moment, I'm doing all this from my laptop on the localhost. I've got PHP and Apache 2.2.x (whatever the latest is 2.2.9?) running on my local machine. The actual environment this will be running in is PHP running on a windows server 2003 box running IIS6. My client web interface will be on a remote machine and the server will be on a machine with a publicly accessible address. I don't know if any of this makes a difference in the end, but I thought it was worth mentioning.
I've modified my client code to look like this: (note the comments highlighted in green)
Code: Select all
<?php
if(sizeof($_POST) > 0){
$ip = '192.168.1.100';
$port = 11111;
if(($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false){
echo "<br>Problem creating socket: " . socket_strerror(socket_last_error()) . "<br>";
}
echo "Attempting to connect to '$ip' on port '$port'...";
if(($client = socket_connect($sock, $ip, $port)) === false){
echo "<br>Problem connecting to socket: " . socket_strerror(socket_last_error()) . "<br>";
}
else{
[color=#00FF00] // notice I'm tacking on the '\n' to the string being sent to the server.[/color]
[color=#00FF00] // is this what you were talking about, by terminate every command sent to the[/color]
[color=#00FF00] // server with a newline?[/color]
socket_write($sock, $_POST['strText'] . '\n', strlen($_POST['strText']));
[color=#00FF00] // immediately check for the response from the server.[/color]
if (($buf = socket_read($sock, 2048, PHP_NORMAL_READ)) === false) {
echo "socket_read() failed: reason: " . socket_strerror(socket_last_error($sock)) . "<br>";
}
else{
[color=#40FF00]//after having read the response from the server looking for 'Ok/n'[/color]
$buf = trim($buf);
echo $buf . "<br>";
if($buf == "Ok\n"){
socket_close($sock);
}
}
}
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Gridborg Client</title>
</head>
<body>
<form name="f" method="post" action="GridborgClient.php">
<input type="text" name="strText" value=""><br />
<input type="submit" value="submit">
</form>
</body>
</html>Code: Select all
<?
error_reporting(E_ALL);
/* Allow the script to hang around waiting for connections. */
set_time_limit(0);
/* Turn on implicit output flushing so we see what we're getting
* as it comes in. */
ob_implicit_flush();
$address = '192.168.1.100';
$port = 11111;
if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) {
echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n";
}
if (socket_bind($sock, $address, $port) === false) {
echo "socket_bind() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
}
if (socket_listen($sock, 5) === false) {
echo "socket_listen() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
}
print("Waiting for connection...");
while (true){
if (($msgsock = socket_accept($sock)) === false) {
echo "socket_accept() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
break;
}
/* Send instructions. */
$msg = "\n\rWelcome to the PHP Test Server.\n\r" . "To quit, type 'quit'. To shut down the server type 'shutdown'.\n\r";
socket_write($msgsock, $msg, strlen($msg));
while(true) {
if (($buf = socket_read($msgsock, 2048, PHP_NORMAL_READ)) === false) {
echo "socket_read() failed: reason: " . socket_strerror(socket_last_error($msgsock)) . "\n\r";
break 2;
}
if (!$buf = trim($buf)) {
continue;
}
switch($buf){
case 'quit':
break 2;
case 'shutdown':
socket_close($msgsock);
break 3;
case 'openGridSock':
// code to open the socket
$ip = 'xxx.xxx.xxx.xxx'; // hidden for security
$port = 1234;
if(($GridSock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false){
$errmsg = "Problem creating socket: " . socket_strerror(socket_last_error());
socket_write($msgsock, $errmsg, strlen($errmsg));
}
if(($client = socket_connect($GridSock, $ip, $port)) === false){
$errmsg = "Problem connecting to socket: " . socket_strerror(socket_last_error());
socket_write($msgsock, $errmsg, strlen($errmsg));
}
else{
$connectmsg = "Connected.\n\r";
socket_write($msgsock, $connectmsg, strlen($connectmsg));
$GridResponse = socket_read($GridSock, 1024);
socket_write($msgsock, $GridResponse, strlen($GridResponse));
}
break;
case 'closeGridSock':
$closemsg = "Connection closed.";
socket_close($GridSock);
socket_write($msgsock, $closemsg, strlen($closemsg));
break;
default:
/* check for talkback command */
if(substr($buf,0,3) == 'tb:'){
$talkback = "You said: " . substr($buf,3,strlen($buf));
socket_write($msgsock, $talkback, strlen($talkback));
[color=#40FF00]//sending back an 'Ok/n' as dml suggested[/color]
socket_write($msgsock,"Ok/n",strlen("Ok/n"));
}
else{
// we did not get a specific daemon command so assume what was typed was a gridborg command and write it to the GridSock socket.
socket_write($GridSock, $buf, strlen($buf));
// imeadately read the gridborg's response and write that message back out to the client
$GridResponse = socket_read($GridSock, 1024);
socket_write($msgsock, $GridResponse, strlen($GridResponse));
}
break;
}
}
socket_close($msgsock);
}
?>Things still aren't working though. Have I missed something in what you're trying to tell me? Sorry if I'm being dense about this. I really appreciate your help and patience.
Also, I'll make these changes in my real environment tomorrow to see if I get a different outcome.
Thanks again,
Chris
Re: Maintaing socket connections across page requests
First, try leaving out the third (length) parameter of socket_write, and let PHP send the whole string: it looks like it might be truncating before the newline. Also, there's a variety of newline formats in the code "\n", "/n", "\r\n", "\n\r": try to use the same one consistently. If it's Windows, you might try using "\r\n" consistently as the newline terminator, both from client->server and server->client. Get the client and server to log before and after any blocking calls (socket_accept, socket_read, etc) to establish where they're getting stuck. Also, the first thing waiting for the client in the socket read buffer will be the welcome message from the server, so read that out before sending the first command.
Tcpdump and equivalents print out the tcp packets going across a network interface. It can give you very specific knowledge that's invaluable for debugging, for example whether when you call socket_write, the bytes are actually transmitted or are buffered. You do have to understand the TCP protocol to understand the output, which is worth knowing because it gives you such specific information about what's going on, but taking the time out to learn it may not be practical now.
Tcpdump and equivalents print out the tcp packets going across a network interface. It can give you very specific knowledge that's invaluable for debugging, for example whether when you call socket_write, the bytes are actually transmitted or are buffered. You do have to understand the TCP protocol to understand the output, which is worth knowing because it gives you such specific information about what's going on, but taking the time out to learn it may not be practical now.
Re: Maintaing socket connections across page requests
dml,
Thanks again for the advice. I'll try these things again very soon, and report back (in case you -- or anyone else -- are interested).
Cheers!
Chris
Thanks again for the advice. I'll try these things again very soon, and report back (in case you -- or anyone else -- are interested).
Cheers!
Chris
Re: Maintaing socket connections across page requests
YAY! I've got it working!!
This is SOoo friggin' cool! I just had the breakthrough before lunch. It was a combination of sending \r\n at the end of each command to the server as well as when I was closing the socket on the server side.
If anyone is interested in the code, I'll post it when I've cleaned it up a bit. I'm running into a small problem right now where the server that I'm talking to via my little "proxy" is sending back two lines of response to each command. Each of those lines is terminated with a \r\n so I'm only getting one of the lines since socket_read() reads either until it reaches the bite limit set, or until it gets a \r\n.
I'm going to experiment with using socket_recvfrom() which *I think* will just read until there's nothing left. At which point I could send my own terminator like dml suggested originally: Ok\r\n or something like that which I could check for in my code.
Thanks heaps dml and Garcia!
I'll report back my progress (for posterity if nothing else).
This is SOoo friggin' cool! I just had the breakthrough before lunch. It was a combination of sending \r\n at the end of each command to the server as well as when I was closing the socket on the server side.
If anyone is interested in the code, I'll post it when I've cleaned it up a bit. I'm running into a small problem right now where the server that I'm talking to via my little "proxy" is sending back two lines of response to each command. Each of those lines is terminated with a \r\n so I'm only getting one of the lines since socket_read() reads either until it reaches the bite limit set, or until it gets a \r\n.
I'm going to experiment with using socket_recvfrom() which *I think* will just read until there's nothing left. At which point I could send my own terminator like dml suggested originally: Ok\r\n or something like that which I could check for in my code.
Thanks heaps dml and Garcia!
I'll report back my progress (for posterity if nothing else).
Re: Maintaing socket connections across page requests
Yep... I'm beating my head up against a wall again... The thing is almost working perfectly, but then my final destination server thows me for a loop. Each time I send it a command, it can respond with one or more lines of text!
a typical command might be:
login username password
the response would be:
ROK 6
ESessionCreated 6
and that's a crlf (\r\n) after each one of those responses. So I realize what I need to do is to read from the server in a loop until it's got nothing left for me and then send my cool little control command back to my web app that says, okay the server's given all it's going to give (dml's suggested 'Ok\r\n' is working nicely for that).
Here's the problem I've tried several things to read this in a loop and none of them are working! Grr...
I've tried:
Code: Select all
while(($GridResponse = trim(socket_read($GridSock, 2048, PHP_BINARY_READ))) <> ''){
echo "GridResponse is: " . $GridResponse . "\r\n";
echo "writing back Gridborg Response...\r\n";
$numbytes = socket_write($msgsock, $GridResponse);
echo "number of bytes written: " . $numbytes . "\r\n";
echo "Will I even see this the second time?\r\n";
}
... and it hangs right there... it never drops out of the loop.GridResponse is: ROK 75
writing back Gridborg Response...
number of bytes written: 6
Will I even see this the second time?
GridResponse is: ESessionCreated 75
writing back Gridborg Response...
number of bytes written: 18
Now, if I put a counter in there and tell the loop to exit when the counter reaches 2 (the number of lines I'm expecting in response from the server *this* time), it exits the loop and everything works well, but I cannot be guaranteed that the server will always respond with two lines. Sometimes it's just one and sometimes it could be more.
So while this works:
Code: Select all
$i = 0;
while(true){
$GridResponse = trim(socket_read($GridSock, 2048, PHP_BINARY_READ));
echo "i is: " . $i . "\r\n";
echo "GridResponse is: " . $GridResponse . "\r\n";
echo "writing back Gridborg Response...\r\n";
$numbytes = socket_write($msgsock, $GridResponse);
echo "number of bytes written: " . $numbytes . "\r\n";
$i++;
if($i == 2){
break;
[color=#800000]// i've even tried replacing 'break;' with 'continue;' to see if I could get the loop to go one more time![/color]
}
echo "Will I even see this the second time?\r\n";
}
I've even tried this:
Code: Select all
while(true){
$GridResponse = trim(socket_read($GridSock, 2048, PHP_BINARY_READ));
echo "GridResponse is: " . $GridResponse . "\r\n";
if(strlen($GridResponse) == 0){
break;
}
else{
echo "writing back Gridborg Response...\r\n";
$numbytes = socket_write($msgsock, $GridResponse);
echo "number of bytes written: " . $numbytes . "\r\n";
}
}
I'm having a really hard time understanding why this loop is behaving the way it is. I've even tryed re-reading from the socket at the end of the loop and checking it's length to see if it's equal to zero... but it never seems to get there. I always get some variation of:
I'm so close I can taste it! I hope someone can help me out hereGridResponse is: ROK nn
writing back Gridborg Response...
number of bytes written: 6
Will I even see this the second time?
GridResponse is: ESessionCreated nn
writing back Gridborg Response...
number of bytes written: 18