Просмотр полной версии : Jabber бот на php - проблема с сокетами
Krist_ALL
27.03.2009, 14:21
Всем привет!
Решил написать своего джаббер бота на пхп. Не нада кричать что есть готовые бибоиотеки с xmpp итд, я пишу с нуля. Проблема заключается втом, что функция socket_read читает только один пакет, их бывает приходит 2 или 3, приходится ее писать несколько раз, из-за этого (я так думаю, но не факт что это из-за функции) у меня сбивается порядок огбмена пакеами и соединение рвется что-ли... посылаю данные а ответа не вижу (еслиб я допустил ошибку в формировании данных, пришел бы пакет, говорящий об ошибке в синтаксисе) .. Помогите плз.
<?php
$JABBER_SERVER = "jabber.ru";
$PORT = 5222;
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if(!$socket) { echo 'soket open - NO'; exit; } else { echo 'socket open - OK<br>'; }
$connect = socket_connect($socket, $JABBER_SERVER, $PORT);
if(!$connect) { echo 'soket connect - NO'; exit; } else { echo 'socket connect - OK<br>'; }
$t = "<stream:stream to='jabber.ru' xmlns='jabber:client' "; $t .= "xmlns:stream='http://etherx.jabber.org/streams' xml:lang='ru' version='1.0'/>";
echo '<b>Клиент - </b>'.htmlspecialchars($t).'<br><br>';
socket_write($socket, $t);
$data = socket_read($socket, 5000);
echo '<b>Ответ от jabber - </b>'.htmlspecialchars($data)."<br><br>";
$data = socket_read($socket, 5000);
echo '<b>Ответ от jabber - </b>'.htmlspecialchars($data)."<br><br>";
$data = socket_read($socket, 5000); echo '<b>Ответ от jabber - </b>'.htmlspecialchars($data)."<br><br>";
$t1 = "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5' />";
echo '<b>Клиент - </b>'.htmlspecialchars($t1).'<br><br>';
socket_write($socket, $t1); $data = socket_read($socket, 5000);
echo '<b>Ответ от jabber - </b>'.htmlspecialchars($data)."<br><br>";
//socket_close($socket);
?>
Krist_ALL
27.03.2009, 17:01
На многих форумах по пхп мне не помогли. Не знают как сделать...
Вя надежда на вас!
Pashkela
27.03.2009, 17:32
попробуй так:
1. $t = блаблабалбала и в конце всегда ."\n";
2. Явно указать длину того, чего пишешь:
socket_write($socket, $t, strlen($t));
3. Читать можно попробовать так:
socket_read($socket, 5000, PHP_NORMAL_READ);
А вообще читай:
http://ru.php.net/socket_read
там есть примеры работы с сокетами, правильные примеры
Krist_ALL
27.03.2009, 17:49
Warning: socket_read() [function.socket-read]: unable to read from socket [104]: Connection reset by peer in ....
Сделал как сказал Pashkela но вот такая штука вылезла.....
KaZ@NoVa
27.03.2009, 18:26
ты под каким Php этот код писал. У меня ошибку пишет, что функции socket_create не существует.
И я не пойму, что означают строки вида:
<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5' />
Krist_ALL
27.03.2009, 18:34
Плд нормальным! ВОт под каким ты запускаещь скриепт я непойму
laedafess
27.03.2009, 19:10
ты под каким Php этот код писал. У меня ошибку пишет, что функции socket_create не существует.
в пхп надо php_sockets.dll добавить
Krist_ALL Примерный план подключение к джаббер серверу:
1) Создаем сокет (socket_create)
2) Подключаемся к серверу(socket_connect)
3) Посылаем инициализирующий запрос (<?xml version='1.0'?><stream:stream to=' и т.д.)
4) А вот дальше нужен цикл обработки и собственно ответов. Приблизительного вида:
// $sock - первоначальный сокет от socket_create
while (1)
{
$read=array($sock);
$count=socket_select($read,$write=null,$exception= null,15);
// Если ничего не пришло пропускаем
// Или если нужно что то послать по собственному желанию - отправляем
if ($count<1) continue;
foreach ($read as $one)
{
$input=socket_read($one,4096);
// Тут обработка принятого пакета
// например если пришел пакет stream features ответить пакетом начала SASL авторизации
}
}
Сам сейчас осваиваю питон и тоже пишу свой клиент джаббера
З.Ы. Ох и намучаешься ты с DIGEST-MD5 ))
Pashkela Не вводите людей в заблуждение, если вы не разбираетесь. XMPP не предусматривает символ перевода строк, чтобы можно было читать NORMAL_READ и для socket_write НЕ требуется указывать длину буфера, если конечно не хочется передавать обрезанные данные
Krist_ALL
27.03.2009, 19:43
Gifts, сделал я как ты написал, но чет скрипт зависв браузере - полаеися бесконечный цикл же вайл
<?php
$JABBER_SERVER = "jabber.ru";
$PORT = 5222;
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if(!$socket) { echo 'soket open - NO'; exit; } else { echo 'socket open - OK<br>'; }
$connect = socket_connect($socket, $JABBER_SERVER, $PORT);
if(!$connect) { echo 'soket connect - NO'; exit; } else { echo 'socket connect - OK<br>'; }
$t = "<stream:stream to='jabber.ru' xmlns='jabber:client' ";
$t .= "xmlns:stream='http://etherx.jabber.org/streams' xml:lang='ru' version='1.0'/> ";
echo '<b>Клиент - </b>'.htmlspecialchars($t).'<br><br>';
socket_write($socket, $t);
while (1) {
$read=array($socket);
$count=socket_select($read,$write=null,$exception= null,15);
// Если ничего не пришло пропускаем
// Или если нужно что то послать по собственному желанию - отправляем
if ($count<1) continue;
foreach ($read as $one)
{
$input=socket_read($one,4096);
echo "htmlspecialchars($input)";
// Тут обработка принятого пакета
// например если пришел пакет stream features ответить пакетом начала SASL авторизации //
echo '<b>Ответ от jabber - </b>'.htmlspecialchars($input)."<br><br>";
}
}
?>
НА счет типа авторизации - эт канешно сложно, но я выдерну какнить из класса готового.. ПРосто я хочу нааписать своего бота а не готового юзать.
Krist_ALL Естественно "завис". Клиент принял пакет и в бесконечном цикле ждет, когда придет следующий, а в браузер ничего не выводится из-за буферизации вывода.
Сделай так: <?php
$JABBER_SERVER = "jabber.ru";
$PORT = 5222;
$i=0;
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if(!$socket) { echo 'soket open - NO'; exit; } else { echo 'socket open - OK<br>'; }
$connect = socket_connect($socket, $JABBER_SERVER, $PORT);
if(!$connect) { echo 'soket connect - NO'; exit; } else { echo 'socket connect - OK<br>'; }
$t = "<stream:stream to='jabber.ru' xmlns='jabber:client' ";
$t .= "xmlns:stream='http://etherx.jabber.org/streams' xml:lang='ru' version='1.0'/> ";
echo '<b>Клиент - </b>'.htmlspecialchars($t).'<br><br>';
socket_write($socket, $t);
while (1) {
$read=array($socket);
$count=socket_select($read,$write=null,$exception= null,1);
// Если ничего не пришло пропускаем
// Или если нужно что то послать по собственному желанию - отправляем
if ($i++ > 5) break; // Соединение оборвется через 5 секунд или 5 принятых пакетов
if ($count<1) continue;
foreach ($read as $one)
{
$input=socket_read($one,4096);
echo "htmlspecialchars($input)";
// Тут обработка принятого пакета
// например если пришел пакет stream features ответить пакетом начала SASL авторизации //
echo '<b>Ответ от jabber - </b>'.htmlspecialchars($input)."<br><br>";
}
}
?>
Krist_ALL
27.03.2009, 20:03
Ну вот опять тажа проблема, что и вначале - немогу послать второй пакет, тоестья его ослал но ответа не вижу
<?php
$JABBER_SERVER = "jabber.ru";
$PORT = 5222;
$i=0;
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if(!$socket) { echo 'soket open - NO'; exit; } else { echo 'socket open - OK<br>'; }
$connect = socket_connect($socket, $JABBER_SERVER, $PORT);
if(!$connect) { echo 'soket connect - NO'; exit; } else { echo 'socket connect - OK<br>'; }
$t = "<stream:stream to='jabber.ru' xmlns='jabber:client' ";
$t .= "xmlns:stream='http://etherx.jabber.org/streams' xml:lang='ru' version='1.0'/> ";
echo '<b>Клиент - </b>'.htmlspecialchars($t).'<br><br>';
socket_write($socket, $t);
while (1) {
$read=array($socket);
$count=socket_select($read,$write=null,$exception= null,1);
// Если ничего не пришло пропускаем
// Или если нужно что то послать по собственному желанию - отправляем
if ($i++ > 5) break; // Соединение оборвется через 5 секунд или 5 принятых пакетов
if ($count<1) continue;
foreach ($read as $one) { $input=socket_read($one,4096);
// Тут обработка принятого пакета
// например если пришел пакет stream features ответить пакетом начала SASL авторизации
echo '<b>Ответ от jabber - </b>'.htmlspecialchars($input)."<br><br>";
}
}
$t2 = "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5' />";
echo '<b>Клиент - </b>'.htmlspecialchars($t2).'<br><br>';
socket_write($socket, $t2);
while (1) { $read=array($socket); $count=socket_select($read,$write=null,$exception= null,1);
// Если ничего не пришло пропускаем
// Или если нужно что то послать по собственному желанию - отправляем
if ($i++ > 5) break; // Соединение оборвется через 5 секунд или 5 принятых пакетов
if ($count<1) continue; foreach ($read as $one) { $input=socket_read($one,4096);
// Тут обработка принятого пакета
// например если пришел пакет stream features ответить пакетом начала SASL авторизации
echo '<b>Ответ от jabber - </b>'.htmlspecialchars($input)."<br><br>";
}
}
?>
Вот что выводтся в браузере
socket open - OK
socket connect - OK
Клиент - <stream:stream to='jabber.ru' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' xml:lang='ru' version='1.0'/>
Ответ от jabber - <?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' id='2125564970' from='jabber.ru' version='1.0' xml:lang='en'><stream:features><starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/><compression xmlns='http://jabber.org/features/compress'><method>zlib</method></compression><mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><mechanism>DIGEST-MD5</mechanism><mechanism>PLAIN</mechanism></mechanisms><register xmlns='http://jabber.org/features/iq-register'/></stream:features></stream:stream>
Ответ от jabber -
Ответ от jabber -
Ответ от jabber -
Ответ от jabber -
Ответ от jabber -
Клиент - <auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5' />
Pashkela
27.03.2009, 20:18
/* 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();
Krist_ALL
27.03.2009, 20:38
ПРичем тут ob_implicit_flush();...
В холостую идет чтение опять......
Krist_ALL Господи, что Вы делаете. Цикл обработки будет один! не надо его копипастить сто тыщ раз. Я специально комментарий оставил где должна быть обработка. Объясняю, на том месте должно быть что-то вроде: if (strpos($input,'stream:features')!==false)
{
отправить пакет на запрос SASL авторизации
}
И второе - почитайте спецификацию. НЕЛЬЗЯ закрывать тэг <stream:stream> если вы не хотите закрыть соединение. Ака: $t = "<stream:stream to='jabber.ru' xmlns='jabber:client' ";
$t .= "xmlns:stream='http://etherx.jabber.org/streams' xml:lang='ru' version='1.0'/> ";
"/>" Считается закрытием тэга. Уберите слеш
Pashkela
27.03.2009, 20:51
<?php
/* Allow the script to hang around waiting for connections. */
set_time_limit(0);
@ini_set("display_errors","1");
/* Turn on implicit output flushing so we see what we're getting
* as it comes in. */
ob_implicit_flush();
$JABBER_SERVER = "jabber.ru";
$PORT = 5222;
$i=0;
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if(!$socket) { echo 'soket open - NO'; exit; } else { echo 'socket open - OK<br>'; }
$connect = socket_connect($socket, $JABBER_SERVER, $PORT);
if(!$connect) { echo 'soket connect - NO'; exit; } else { echo 'socket connect - OK<br>'; }
$t = "<stream:stream to='jabber.ru' xmlns='jabber:client' ";
$t .= "xmlns:stream='http://etherx.jabber.org/streams' xml:lang='ru' version='1.0'/> ";
echo '<b>Клиент - </b>'.htmlspecialchars($t).'<br><br>';
socket_write($socket, $t);
while (1) {
$read=array($socket);
$count=socket_select($read,$write=null,$exception= null,1);
// Если ничего не пришло пропускаем
// Или если нужно что то послать по собственному желанию - отправляем
if ($i++ > 5) break; // Соединение оборвется через 5 секунд или 5 принятых пакетов
if ($count<1) continue;
foreach ($read as $one) {
$input=socket_read($one,4096);
if (!empty($input)) {
// Тут обработка принятого пакета
// например если пришел пакет stream features ответить пакетом начала SASL авторизации
echo '<b>Ответ от jabber - </b>'.htmlspecialchars($input)."<br><br>";
}
}
}
$t2 = "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5' />";
echo '<b>Клиент - </b>'.htmlspecialchars($t2).'<br><br>';
socket_write($socket, $t2);
while (1) { $read=array($socket); $count=socket_select($read,$write=null,$exception= null,1);
// Если ничего не пришло пропускаем
// Или если нужно что то послать по собственному желанию - отправляем
if ($i++ > 5) break; // Соединение оборвется через 5 секунд или 5 принятых пакетов
if ($count<1) continue; foreach ($read as $one) { $input=socket_read($one,4096);
if (!empty($input)) {
// Тут обработка принятого пакета
// например если пришел пакет stream features ответить пакетом начала SASL авторизации
echo '<b>Ответ от jabber - </b>'.htmlspecialchars($input)."<br><br>";
}
}
}
?>
http://s44.radikal.ru/i103/0903/b6/9f5ae48fd66d.jpg
if (!empty($input)) {
тогда выводим ответ
}
Pashkela Он будет всегда не empty, если соединение есть (связано с обработкой socket_select). Скорее надо проверять обратное: if(empty($input)) die('Соединение разорвано');
Pashkela
27.03.2009, 21:07
не всегда, как видно, я потестил у себя, ответов иногда разное кол-во, в зависимости от того, какое соединение, банально F5 несколько раз и сразу видно результат
В общем, должно выглядеть почти так. <?
$JABBER_SERVER = "jabber.ru";
$PORT = 5222;
$i=0;
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if(!$socket) { echo 'soket open - NO'; exit; } else { echo 'socket open - OK<br>'; }
$connect = socket_connect($socket, $JABBER_SERVER, $PORT);
if(!$connect) { echo 'soket connect - NO'; exit; } else { echo 'socket connect - OK<br>'; }
$t = "<stream:stream to='jabber.ru' xmlns='jabber:client' ";
$t .= "xmlns:stream='http://etherx.jabber.org/streams' xml:lang='ru' version='1.0'>";
echo '<b>Клиент - </b>'.htmlspecialchars($t).'<br><br>';
socket_write($socket, $t);
while (1) {
$read=array($socket);
$count=socket_select($read,$write=null,$exception= null,1);
// Если ничего не пришло пропускаем
// Или если нужно что то послать по собственному желанию - отправляем
if ($i++ > 10) break; // Соединение оборвется через 5 секунд или 5 принятых пакетов
if ($count<1) continue;
foreach ($read as $one) {
$input=socket_read($one,4096);
if (empty($input)) die('Соединение разорвано');
echo '<b>Ответ от jabber - </b>'.htmlspecialchars($input)."<br><br>";
if (stripos($input,'stream:feature')!==false)
{
$t="<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5' />";
socket_write($one,$t);echo '<b>Клиент - </b>'.htmlspecialchars($t).'<br><br>';
}
if (stripos($input,'challenge')!==false)
{
echo '<br/><br/> А теперь разгадывай челленджы сасла: '.htmlspecialchars(base64_decode(strip_tags($input )));
}
}
}
Krist_ALL
27.03.2009, 21:40
спаСИБО ОГРОМНОЕЙ !
Krist_ALL
27.03.2009, 21:40
спаСИБО ОГРОМНОЕЙ !но на этом я не закончу-завтра продолжу. Всем спасибо!
Krist_ALL
28.03.2009, 20:18
Итак продолжим!
После отпраки серверу пакета, который содержит метод авторизации (дигест мд5), мне приходит ответ в басе64, раскодировываю и получаю значение nonce. Я теперь должен отправить пакет который будет содержать:
username='jid',
realm = 'jabber.ru',
nonce=123123123132',
cnonce='????????',
nc=0000001,
qop=auth,
digest-uri='xmpp/jabber.ru', charset=utf-8,response=!!!!!!!!!
Итак, что жея хотел спросить. nonce Эт понятно, оно у нас уже есть,а вот что писать в поле cnonce? В документации сказано что этоуникальный код ответной клинтской сессии, сгенерированный клиентом.... Как понимать не знаю.
Про response молчу.
И еще, мне кажется вайл тут неочень.. может фор? Потомут что я хочу сделать отдельно авторизацию и отделюно прослушку ответов...
А вообше мне кажется моя идея очень замечательная т.к. бот будет на пхп а не на питоне, то даст возможность запускать его на хостингах а не на серваках. Потому что иметь сервак и знать как с ним управится не каждый может, а вот оплатить хостинг и залить на него бота - почти каждый)
Krist_ALL Читайте RFC2831. Например тут: http://www.faqs.org/rfcs/rfc2831.html
И собственно rfc3920bis. Все вопросы отпадут сами собой
cnonce - это любая строка, например можно использовать MD5 от текущего времени. А response - функция от всех этих параметров
З.Ы, если вы думаете, что PHP класс для джаббера никто до этого не придумал - вы ошибаетесь (например XMPPHP, jabberPHP)
З.Ы.Ы, Зачем выдумывать лисапед (это к вашей фразе о циклах)? Вы как раз вернетесь к тому же, с чего начали - почему вам не приходят все ответы
Krist_ALL
28.03.2009, 20:57
Я знаю про php класс. Я об этом писал в начале темы.
Я пишу простой код - простой всмысле понятный. и признаться не знаю классы и хочу написть не исползуя классы.
Пасибо за ссылку на ртфм.
Krist_ALL
29.03.2009, 21:03
Хотелось бы попдробнее чтоб кто-нибудь прокоментировал данный кусок кода
while (1)
{
$read=array($socket);
$count=socket_select($read,$write=null,$exception= null,1);
// Если ничего не пришло пропускаем
// Или если нужно что то послать по собственному желанию - отправляем
if ($i++ > 10) break; // Соединение оборвется через 5 секунд или 5 принятых пакетов
if ($count<1) continue;
Почему вместо if ($i++ > 10) break; не сделать фор и=0; и<10?
Почему через 5 пакетов а не 10? Ведь if ($i++ > 10)
Krist_ALL Я просто не правил комментарий, да, там 10 пакетов будет. Этот бряк по сути нужен, только для отладки приложения. Потому что еще нет условий, по которому надо выходить из цикла. Когда скрипт дорастет до того, что будет проверять большую часть исключений (ака соединение разорвано, пришел пакет '</stream:stream>' и прочие), тогда от этой строки можно будет избавиться.
Krist_ALL
29.03.2009, 22:36
Если не бреак то тада цикл будет бесконечным и зависнет)
Как же хочется фор для авторизации а потом вайл для приема сообщений.
Krist_ALL Еще раз вопрос - зачем? У тебя получится два АБСОЛЮТНО одинаковых цикла, просто в одном будешь обрабатывать одно, во втором - другое.
Если не бреак то тада цикл будет бесконечным и зависнет)
Еще раз - когда цикл будет обрабатывать все исключения - цикл будут прерывать эти самые обработчики исключений.
vBulletin® v3.8.14, Copyright ©2000-2026, vBulletin Solutions, Inc. Перевод: zCarot