Re: [GENERAL-BG] Socekt Server Problem

[email protected] (Andrey Hristov) Sun, 13 Feb 2005 19:26:16 +0100
Newsgroups php.general.bg
Message-ID <[email protected]>
   Здравей,
този скрипт под уиндоус ли го пускаш? Ако не е под уиндоус, може да опиташ
с ext/pcntl, и да правиш pcntl_fork(), вместо да стартираш външен процес. Само
мога да ти кажа, че имаш race condition, като извикваш нов интерпретатор, защото
при високо натоварване в главният ти сървър свободният порт може да се bind-не
отново докато се стартира "детският"(child) ти сървър.
   Доколкото съм си играл, тази грешка, че другата страна е затворила връзката,
идва когато се разпадне връзката. Например ако клиента ти затвори връзката. Грешката,
която получаваш, при повторно стартиране е защото сървърският ти порт вече е закачен.
Beej's Guide To Network Progamming казва следното :
------------------
"Sometimes, you might notice, you try to rerun a server and bind() fails, claiming 
"Address already in use." What does that mean?
Well, a bit a of socket that was connected is still hanging around in the kernel, and it s 
hogging the port. You can either wait for it to clear (a minute or so), or add code to 
your program allowing it to reuse the port, like this:

int yes=1; //char yes= 1 ; // Solaris people use this
// lose the pesky "Address already in use" error message
if (setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1) {
   perror("setsockopt");
   exit(1);
}
------------------

setsockopt() в PHP е socket_set_option(). Има един коментар, който казва следното:
------------------------
I am not sure if the fourth argument (optval) is used in SO_REUSEADDR ... I don't think it 
is...  In that case, this is how you set your socket re-usable:
socket_set_option($socket,SOL_SOCKET,SO_REUSEADDR,1);
------------------------
  Като допълнения искам да те посъветвам, че може да използваш __LINE__ и __FILE__, 
константите, в съобщенията ти за грешка. Например :
   printf("Bad error [%s::%d]", __FILE__, __LINE__)
и така няма да ти се налага да сменяш всяко съобщения, като му промениш мястото във
файла.

Ако използваш PHP5, тогава може да пробваш stream_socket_server() и компания вместо
ext/sockets, който все още си е експериментален модул (след 4 години мисля) и си няма
програмист, който да го поддържа активно. Докато потоците се поддържат от Уез Фърлонг.
За съжаление понякога са по-малко гъвкави. Например не можеш да сменяш беклога на
сървърското гнездо (сокет). Мисля, че в сорса на PHP е настроен да е 5, но това е 
поправима грешка, като се хакне сорса :)

Преди няколко месеца се занимавах да пиша една чудесия, която позволява отдалечено
извикване на процедури (RPC) с PHP5 и написана на ПХП. Т.е :
<?php
$a = new RPCClient("some_obj.hristov.com");
$a->testMethod();

?>
some_obj.hristov.com e име на обект, който е регистриран в сървър за имена. Цялата
идеология е като при Corba и подобни Джава технологии. А отдолу лежи върху сокети
или върху опашки от съобщения (SysV message queue). Само, че не знам вече докъде го
докарах :), защото междувременно се занимавах с дипломната си работа основно.

Надявам се, че поне малко съм ти помогнал,
Андрей

[email protected] wrote:
> Imam problem sys sledniq socket server:
> 
> <?php
> $start = date("Y-m-d H:i:s");
> echo "Skriptyt e startiran: $start\n";
> include("../includes/config.inc.php");
> 
> //Iztrivame wsichki online users - initialisation
> //$db->query("TRUNCATE online_users");
> 
> // VKLIUCHVAME FUNKCIITE ZA OBRABOTKA NA XML.
> include ("xml_parser_functions.php");
> 
> $file = "xml.log";
> 
> // VZEMAME ID-to NA USER-a.
> function get_uid_by_username($username) {
>     global $db1;
>     $query = "SELECT id FROM users WHERE username = '$username'";
>     $db1->query($query);
>     if (!$db1->nf())    {
>         $uid = 0;
>     } else {
>         $db1->next_record();
>         $uid = $db1->f("id");
>     }
>     return $uid;
> }
> 
> // VZEMAME ACCESS-a NA USER-a.
> function get_uaccess_by_username($username) {
>     global $db1;
>     $query = "SELECT access FROM users WHERE username = '$username'";
>     $db1->query($query);
>     if (!$db1->nf())    {
>         $uaccess = 0;
>     } else {
>         $db1->next_record();
>         $uaccess = $db1->f("access");
>     }
>     return $uaccess;
> }
> 
> 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();
> 
> 
> if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) < 0) {
>    echo "socket_server.php line 52; socket_create() failed: reason: " . 
> socket_strerror($sock) . "\n";
> }
> 
> if (($ret = socket_bind($sock, $net_address, $portnr)) < 0) {
>    echo "socket_server.php line 56; socket_bind() failed: reason: " . 
> socket_strerror($ret) . "\n";
>      socket_close($sock);
> }
> 
> if (($ret = socket_listen($sock, 5)) < 0) {
>    echo "socket_server.php line 61; socket_listen() failed: reason: " . 
> socket_strerror($ret) . "\n";
>      socket_close($sock);
> }
> // Iztrivame vsichki zapisi ot tablica "online_users" ako ima takiva.
> $q = "DELETE FROM online_users";
> $db->query($q);
> 
> // Inicializirame broqcha, chrez koito tyrsim svobodni portove.
> $i = $min_sock;
> do {
>     if (($msgsock = socket_accept($sock)) < 0) {
>         echo "socket_server.php line 72; socket_accept() failed: reason: 
> " . socket_strerror($msgsock) . "\n";
>         socket_close($sock);
>       break;
>     }
>     /* Send instructions. */
>     echo "New user connected!\n";
>     $msg = "\nWelcome to the PHP Test Server. \n" .
>         "To quit, type 'quit'. To shut down the server type 'exit'.\n";
>     socket_write($msgsock, $msg, strlen($msg));
> 
>     $msg_u_p = "\nEnter user and pass :";
>     socket_write($msgsock, $msg_u_p, strlen($msg_u_p));
>     do {
>         if (false === ($buf = @socket_read($msgsock, 2048, 
> PHP_NORMAL_READ))) {
>             echo "socket_server.php line 86; socket_read() failed: 
> reason: " . socket_strerror($ret) . "\n";
>             socket_close($msgsock);
>             break 2;
>         }
>         if (!($buf = trim($buf))) {
>             continue;
>         }
>     } while (!$buf);
> 
>     // VZIMAME IP-to NA KLIENTA.
>     socket_getpeername ($msgsock, $rip);
>     $rip = trim($rip);
> 
>     // ZAPISVAME VHODQSHTATA INFORMACIQ V LOG FILE.
>     $f = fopen($file, "a");
>     $message = "INPUT :\n";
>     $message .= $buf;
>     $message .= "\n\n";
>     fwrite($f, $message, strlen($message));
>     fclose($f);
> 
>     // ZAPOCHVAME PARSVANETO NA XML-A, KOITO SE NAMIRA V BUFERA.
>     include("xml_parse.php");
>     // KRAI NA PARSVANETO NA XML-A, KOITO SE NAMIRA V BUFERA.
> 
>     $username = $els["login"]["username"];
>     $pass = $els["login"]["password"];
>     $pass = md5($pass);
> 
>     $q = "SELECT * FROM users WHERE username = '$username' AND password 
> = '$pass' AND active = '1'";
>     $db->query($q);
>     if (!$db->nf()) {
>         echo "Greshen Username ili Parola!\n";
>         $out = "GRESHEN USERNAME OR PASSWORD !";
>         socket_write($msgsock, $out, strlen($out));
>         $msg_u_p = "\nCONNECTION IS CLOSED !";
>         socket_write($msgsock, $msg_u_p, strlen($msg_u_p));
>         continue;
>     } else    {
>         echo "Uspeshen whod!\n";
>         $uid = get_uid_by_username($username);
>         $uaccess = get_uaccess_by_username($username);
>         if (($sock1 = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) < 0) {
>             echo "socket_server.php line 130; socket_create() failed: 
> reason: " . socket_strerror($sock1) . "\n";
>         }
>         if (($sock2 = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) < 0) {
>             echo "socket_server.php line 133; socket_create() failed: 
> reason: " . socket_strerror($sock2) . "\n";
>         }
> 
>         // NAMIRAME PYRVITE 2 SVOBODNI PORT-a.
>         do {
>             if ($i > $max_sock) $i = $min_sock;
>             if (($ret = socket_bind($sock1, $net_address, $i)) < 0) {
>                 echo "socket_server.php line 140; socket_bind() failed: 
> reason: " . socket_strerror($ret) . "\n";
>                 $i++;
>             } else {
>                 socket_close($sock1);
>                 // PORT, NA KOITO CLIENTA SHTE PRASHTA ZAQVKI NA 
> SERVER-a I SHTE POLUCHAVA OTGOVOR.
>                 $port1 = $i;
>                 $j = $i + 1;
>                 do {
>                     if ($j > $max_sock)$j = $min_sock;
>                     if (($ret = socket_bind($sock2, $net_address, $j)) < 
> 0) {
>                         echo "socket_server.php line 150; socket_bind() 
> failed: reason: " . socket_strerror($ret) . "\n";
>                         $j++;
>                     } else {
>                         socket_close($sock2);
>                         // PORT, NA KOITO CLIENTA SHTE POLUCHAVA DANNI 
> NA VSEKI 20 SEC.
>                         $port2 = $j;
>                         // STARTIRANE NA SCRIPTA, KOITO SHTE RABOTI S 
> CLIENT-a.
>                         $q = "DELETE FROM online_users WHERE uid = '$uid'";
>                         $db->query($q);
>                         $q = "INSERT INTO online_users (uid, ip, port1, 
> port2, connected) VALUES ('$uid', '$rip', '$port1', '$port2', 'false')";
>                         $db->query($q);
>                         echo "Startirane na market_child_socket.php 
> $port1 $rip $uid $uaccess >> log &\n";
>                         exec ("php market_child_socket.php $port1 $rip 
> $uid $uaccess >> log &");
>                         echo "Startirane na market_xml_send.php $uid >> 
> /var/log/steinx/market_xml_send.log &\n";
>                         exec ("php send/market_xml_send.php $uid $port2 
>  >> /var/log/steinx/market_xml_send.log &");
>                         break;
>                     }
>                 } while (true);
>                 $i = $j + 1;
>                 break;
>             }
>         } while (true);
> 
>         // PRASHTAME XML NA KLIENTA I ZAPISVAME IZHODQSHTATA INFORMACIQ 
> V LOG FILE.
>         $xml_out = 
> "<ports><port1>$port1</port1><port2>$port2</port2></ports>";
>         socket_write($msgsock, $xml_out, strlen($xml_out));
>         $msg_u_p = "\nCONNECTION IS CLOSED !";
>         socket_write($msgsock, $msg_u_p, strlen($msg_u_p));
>         $f = fopen($file, "a");
>          $message = "OUTPUT :\n";
>          $message .= $xml_out;
>          $message .= "\n\n";
>          fwrite($f, $message, strlen($message));
>          fclose($f);
>          $xml_out = "";
>         unset ($els);
>     }
>     socket_close($msgsock);
> } while (true);
> 
> socket_close($sock);
> ?>
> 
> Ot wreme na wreme serwyra se restartira sys slednoto syobstenie:
> Warning: socket_read(): unable to read from socket [104]: Connection 
> reset by peer in /home/pub/market/xml_files/market_socket_server.php on 
> line 86
> socket_server.php line 86; socket_read() failed: reason: Operation not 
> permitted
> 
> Sled restarta izkarwa slednoo syobstenie:
> Warning: socket_bind(): unable to bind address [98]: Address already in 
> use in /home/pub/market/xml_files/market_socket_server.php on line 56
> 
> i se nalaga da spra wsichki klientiand i da restartiram servera...
> 
> Nakoj move li da mi pomogne ... da go naprawq po ustojchiw po nqkakyw 
> nachin... ako ne da se restartira to pone sled restart da ne se nalaga 
> da spiram wkliuchenite do sega klienti...
>