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...
>