В настоящее время я имею дело с сокетами и пакетами в PHP и не знаю, с чего начать с получения длины пакета. Я попробовал это из репозитория GitHub (не помню какой):
private function get_packet_length($socket) {
$a = 0;
$b = 0;
while(true) {
$c = socket_read($socket, 1);
if (!$c) {
return 0;
}
$c = ord($c);
$a |= ($c & 0x7F) << $b++ * 7;
if ($b > 5) {
return false;
}
if (($c & 0x80) != 128) {
break;
}
}
return $a;
}
Кто-нибудь знает, почему это не сработает? я получил int(1)
,
РЕДАКТИРОВАТЬ
private function get_packet_length($socket) {
$a = 0;
$b = 0;
while(true) {
$c = socket_read($socket, 10240);
if (!$c) {
return 0;
}
$c = ord($c);
$a .= strlen($c);
if ($b > 5) {
return false;
}
if (($c & 0x80) != 128) {
break;
}
}
return $a;
}
Выход: string(2) "01"
Пакеты не имеют внутренней длины, и даже получение этой информации из C на самом деле будет относительно трудным. Это связано с тем, что на уровне пользователя данные сокетов не представлены в виде пакетов. (UDP представлен в терминах полных полезных нагрузок, но вполне возможно, что отдельная полезная нагрузка UDP, полученная в пользовательском пространстве, все еще может представлять несколько пакетов.) Следовательно, вопрос не совсем правильный, и вам действительно следует спросить, как определить, как много байтов доступно для чтения на сокете.
Так почему код, который вы вставили, не говорит вам об этом? Вы должны действительно понимать, что делает этот код, потому что это интересно. Однако, поскольку это как бы касается информации, которая вам, вероятно, действительно интересна, я оставлю это на потом.
К сожалению, PHP не предоставляет средства для вызова ioctl(2)
с необработанным гнездом FD. Это позволит вам сделать что-то вроде ioctl(sock, FIONREAD, &n)
в PHP-земле, чтобы определить количество байтов, доступных для чтения. (На самом деле, по-видимому, вы можете сделать это, если вы использовали fopen
или же fsockopen
Но я думаю, что вы этого не сделали. Увы, это не сработает.
К счастью, есть два варианта для вас:
Используйте неблокирующие розетки. Ты можешь позвонить socket_set_nonblock в вашем потоке сокета. Как только вы это сделаете, любой вызов socket_read
, socket_write
и т. д. будут работать в неблокирующем режиме. Это означает, что если вы делаете, например, $data = socket_read($socket, 1024)
, а также меньше доступно 1024 байта, возвращены доступные байты. (N.B. число возвращаемых байтов может быть 0, если нет доступных данных.)
использование socket_select сделать то же самое для ряда розеток. Эта функция уведомляет вас о том, какие сокеты имеют читаемые данные / находятся в состоянии записи / имеют ошибки, требующие обработки.
Какую бы версию вы не использовали, общий способ обработки случаев, когда сокет не получает достаточного количества данных, заключается в реализации тайм-аута. socket_select
предоставляет собственный интерфейс для этого; Выполнение этого вручную с неблокирующими сокетами требует, чтобы вы помнили, как долго вы ждали, и внедрили спящие режимы между вызовами socket_read
, Если вы не получили достаточно данных в течение некоторого периода времени (скажем, 10 секунд), закройте сокет и забудьте об этом.
Если вы получаете больше данных, чем ожидаете, это ошибка, и вы закрываете сокет и забываете об этом.
Протокол, с которым вы работаете, также имеет значение. Поскольку вы не сказали, с каким протоколом вы работаете, я не могу помочь с указателями, чтобы сказать вам, сколько данных ожидать. Но, возможно, вы реализуете свой собственный протокол для удовольствия.
В этом случае вам необходимо определить методологию кодирования количества данных на линии. Поскольку метод, который вы сказали в своем вопросе, выполняет несколько бинарных трюков, я сделаю то же самое. Вы, вероятно, захотите пак 32-битное значение в начале строки. Когда вы получаете соединение, вы ждете первых 4 байтов. Как только вы прочитаете эти 4 байта, вы можете распаковывать эти данные, чтобы определить, сколько вам нужно прочитать.
<?php
$payload = "Have a nice day!\n";
$len = strlen($payload) + 1; // + 1 for terminating NUL byte
$packet = pack("Na", $len, $payload);
socket_send($sock, $packet, $len + 4); // + 4 is length
...
Затем на сервере
<?php
$r = socket_read($sock, 4);
$la = unpack("N", $r);
// Because we don't know how much to read until we get the first 4 bytes.
// Obviously this is a DoS vector for someone to hold the connection open,
// so you will likely want to use socket_select to get that first bit of
// data. That's an exercise for you.
socket_set_nonblock($sock);
$len = $la[1];
$time = 0;
$payload = "";
while ($len > 0 && $time < 10) {
$data = socket_read($sock, $la[1]);
$tlen = strlen($data);
$payload .= $data;
$len -= $tlen;
if ($len == 0) {
break;
}
sleep(1); // Feel free to usleep.
$time++;
}
Нотабене Я не проверял этот код, убедившись, что правильно закодировал упакованные / распакованные данные, поэтому я не уверен, что вы можете использовать его дословно. Считайте это архитектурным псевдокодом.
Другие протоколы имеют другие средства кодирования длины. HTTP, например, использует Content-Length в общем случае.
В предыдущем редактировании я просто отклонил это как просмотр первого байта, чтобы получить длину. Я вернулась к этому вопросу, потому что я видела повышенный голос и потому, что некоторые из моих формулировок о пакетах беспокоили меня. И я также понял, что был очень неправ в своем выводе из моего быстрого взгляда на этот код.
Код считывает отдельные байты из сокета, чтобы попытаться получить оставшуюся полезную нагрузку переменной длины. (Интересно, это код из репозитория для ZeroMQ или push-уведомлений Apple или чего-то подобного.) В любом случае, похоже, что код делает какие-то странные вещи, но что на самом деле происходит?
private function get_packet_length($socket) {
$a = 0;
$b = 0;
while(true) {
/* Read next single byte off of the socket */
$c = socket_read($socket, 1);
if (!$c) {
return 0;
}
/* Use integer value of the byte instead of the character value */
$c = ord($c);
/*
* Get the first 7 bits of $c. Since $c represents an integer value
* of a single byte, its maximum range is [0, 2^8). When we use only
* 7 bits, the range is constrained to [0, 2^7), or 0 - 127. This
* means we are using the 8th bit as a flag of some kind -- more on
* this momentarily.
*
* The next bit executed is ($b++ * 7), since multiplication has
* higher precedence than a left shift. On the first iteration, we
* shift by 0 bits, the second we shift 7 bits, the third we shift
* 14 bits, etc. This means that we're incrementally building an
* integer value byte by byte. We'll take a look at how this works
* out on real byte sequences in a sec.
*/
$a |= ($c & 0x7F) << $b++ * 7;
/*
* If we've tried to handle more than 5 bytes, this encoding doesn't
* make sense.
*/
if ($b > 5) {
return false;
}
/*
* If the top bit was 1, then we still have more bytes to read to
* represent this number. Otherwise, we are done reading this
* number.
*/
if (($c & 0x80) != 128) {
break;
}
}
return $a;
}
Итак, давайте рассмотрим, что это значит с несколькими различными потоками байтов:
$stream = "\x01"; /* This is pretty obviously 1 */
$stream = "\x81\x80\x80\x00";
/* This is also 1, can you figure out why? */
$stream = "\xff\x01"; /* You might think it 256, but this is 255 */
$stream = "\x80\x82"; /* This is 256. */
$stream = "\xff\xff\x01"; /* 32767 */
$stream = "\x80\x80\x02"; /* 32768 */
$stream = "\x0c\x00\x48\x65\x6c\x6c\x6f\x2c\x20\x77\x6f\x72\x6c\x64\x21"/*
* A static value 13, a length terminator, and 13 bytes that happen
* to represent "Hello, world!".
* This shows how such an encoding would be used in practice
*/
Если вы знакомы с порядком байтов, вы можете заметить, что эти значения передаются в кодировке с прямым порядком байтов. Обычно это было бы странно (порядок байтов в сети имеет порядок с прямым порядком байтов), но мы на самом деле не посылаем целые целые значения: мы посылаем байтовые потоки переменной длины. Кодировка каждого байта помогает нам выяснить, какова длина. Однако, не зная, какой протокол реализует этот код, на самом деле это может быть ошибкой, которая не позволяет этому коду работать переносимо на компьютерах с различным порядком байтов.
Важно отметить, что это является частью протокола bytestream и не является стандартным способом получения длины чего-либо. Фактически, многие двоичные протоколы не определены как протоколы потокового потока. Это связано с тем, что протоколы bytestream часто вообще не определяют порядка байтов (поскольку это не обязательно для потока). Поэтому, если вы запускаете этот код на PPC или некоторых процессорах ARM, он не будет работать. Поэтому мы говорим, что этот код не является переносимым.
При работе с байтовыми потоками или отправке необработанных двоичных данных по сети всегда проверяйте порядок данных. Если вы этого не сделаете, ваш протокол создаст непереносимые реализации. Смотрите также этот великий пост Робом Пайком (Rob Pike) для получения дополнительной информации о порядке байтов и о том, почему в любое время возникает проблема, вы либо растерялись, либо кто-то сделал что-то не так (например, определите протокол bytestream без полного определения кодировки чисел).
socket_read возвращает строку так просто
$packet_length=strlen($c);
должно сработать
Кроме того, причина, по которой вы получаете int (1), заключается в том, что вы устанавливаете параметр длины в socket_read.
Удаление его, вероятно, избавит вас от проблем
это
$c = socket_read($socket, 1);
должно быть
$c = socket_read($socket);
использование
$c = socket_read($socket, 1024); //1kb read.
if ($c === "") //from documentation - no data is empty string.
break;
Я не знаю точно, что вы пытаетесь получить, но если строки вы можете использовать
$str = '';
while(true) {
$tmp = socket_read($socket, 1024);
if ($tmp === '')
break;
$str .= $tmp;
}
return $str;
}