Вот назрел такой вопрос пишу сервер, кот. слушает по определённому порту клиента.
Работа в режиме запрос-ответ.
Иногда возникают ситуации, когда приходит запрос и готовится на него ответ, за это время клиент может ещё слать мне запросы.... Так вот после ответа приходят ко мне запросы, а вернее сказать запрос который есть склееные запросы к серверу за время пока отвечал сервер... а это не есть хорошо
Можно ли как-то это пофиксить? Чтоб они приходили в порядке очереди?
P.s.: многопоточность не подходит.
P.s.1: MS VC++ ; MFC; в основе — берклиевский классический сервер.
... << RSDN@Home 1.1.4 beta 5 rev. 395>>
24.05.05 10:46: Перенесено модератором из 'C/C++. Прикладные вопросы' — Odi$$ey
Здравствуйте, -Cheese-, Вы писали:
C>Вот назрел такой вопрос пишу сервер, кот. слушает по определённому порту клиента. C>Работа в режиме запрос-ответ. C>Иногда возникают ситуации, когда приходит запрос и готовится на него ответ, за это время клиент может ещё слать мне запросы.... Так вот после ответа приходят ко мне запросы, а вернее сказать запрос который есть склееные запросы к серверу за время пока отвечал сервер... а это не есть хорошо C>Можно ли как-то это пофиксить? Чтоб они приходили в порядке очереди?
TCP? Тогда маркер в конце каждого сообщения или размер сообщения в самом начале.
Если UDP, то проблемы нет — приходящие дейтаграммы не склеиваются в одну. Правда, есть риск их потери.
C>P.s.: многопоточность не подходит. C>P.s.1: MS VC++ ; MFC; в основе — берклиевский классический сервер.
Здравствуйте, CrystaX, Вы писали:
CX>Здравствуйте, -Cheese-, Вы писали:
C>>Вот назрел такой вопрос пишу сервер, кот. слушает по определённому порту клиента. C>>Работа в режиме запрос-ответ. C>>Иногда возникают ситуации, когда приходит запрос и готовится на него ответ, за это время клиент может ещё слать мне запросы.... Так вот после ответа приходят ко мне запросы, а вернее сказать запрос который есть склееные запросы к серверу за время пока отвечал сервер... а это не есть хорошо
Это вообще фундаментальная особенность TCP-трафика: пакеты, которые на отправляющей стороне отсылаются отдельно, поступать могут на принимающую сторону либо склеенными, либо наоборот -- разделенными на более мелкие части.
C>>Можно ли как-то это пофиксить? Чтоб они приходили в порядке очереди?
CX>TCP? Тогда маркер в конце каждого сообщения или размер сообщения в самом начале.
+1
Именно. Причем нужно будет позаботиться о буфере куда данные из канала поступают и там парсятся. Те данные, которые успешно распарсены из буфера изымаются. Остальны остаются ждать продолжения.
Если сообщения имеют регулярную текстовую структуру (например, XML), то можно обойтись без меток или размера -- сама структура сообщения позволит выделить сообщение целиком. Но это может быть не эффективно на больших сообщениях.
Лично я использую размер сообщения в его залоговке.
CX>Если UDP, то проблемы нет — приходящие дейтаграммы не склеиваются в одну. Правда, есть риск их потери.
C>>P.s.: многопоточность не подходит. C>>P.s.1: MS VC++ ; MFC; в основе — берклиевский классический сервер.
... << RSDN@Home 1.1.4 beta 6a rev. 436>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, eao197, Вы писали:
E>Если сообщения имеют регулярную текстовую структуру (например, XML), то можно обойтись без меток или размера -- сама структура сообщения позволит выделить сообщение целиком. Но это может быть не эффективно на больших сообщениях.
Пример: HTTP. Есть заголовок Content-Length, в котором указывается длина body.
E>Лично я использую размер сообщения в его залоговке.
Да, это самое эффективное, если протокол разрабатываешь сам.
Здравствуйте, eao197, Вы писали:
E>Лично я использую размер сообщения в его залоговке.
ага
мне, например, приходит сообщение в заголовке указано 503 байта
а протокол говорит, что ты мол получил, 1013 байт
т.е. мне нужно следующее начинать с 2+503 байта?
а это все данные там....?
короче возюкаться нужно... а так всё прекрасно начиналось.... (до большой частоты обращений к серверу )
Здравствуйте, -Cheese-, Вы писали:
C>Здравствуйте, eao197, Вы писали:
E>>Лично я использую размер сообщения в его залоговке. C>ага C>мне, например, приходит сообщение в заголовке указано 503 байта C>а протокол говорит, что ты мол получил, 1013 байт
C>т.е. мне нужно следующее начинать с 2+503 байта? C>а это все данные там....? C>короче возюкаться нужно... а так всё прекрасно начиналось.... (до большой частоты обращений к серверу )
Если тебе некоторый (как правило, небольшой) процент потерь некритичен, используй UDP.
Здравствуйте, -Cheese-, Вы писали:
C>Здравствуйте, CrystaX, Вы писали:
CX>>Если тебе некоторый (как правило, небольшой) процент потерь некритичен, используй UDP.
C>ещё как критичен.... по шеям можно получить
Ну тогда только с помощью буффера. TCP не гарантирует, что ты получишь весь пакет за один раз или за один раз только один пакет. Даже более того — возможна ситуация, когда при чтении из сокета ты получаешь меньшее количество байт, чем запрашивал, и это ничего не значит. При следующем чтении ты запросто можешь прочитать еще кусок (равный или меньший запрошенному количеству). Конец соединения происходит только при возврате 0 байт.
TCP — потоковый протокол. Разбить его на смысловые фрагменты — уже пользовательская (твоя) задача. Вообще говоря, ничего страшного здесь нет. Сам сейчас занимаюсь подобными вещами.
Здравствуйте, CrystaX, Вы писали:
CX>Здравствуйте, eao197, Вы писали:
E>>Если сообщения имеют регулярную текстовую структуру (например, XML), то можно обойтись без меток или размера -- сама структура сообщения позволит выделить сообщение целиком. Но это может быть не эффективно на больших сообщениях.
CX>Пример: HTTP. Есть заголовок Content-Length, в котором указывается длина body.
Я имел в виду чуть-чуть другое. Например, когда сообщения имеют вид:
Если поступают большие блоки данных, в которых могут быть множество подобных небольших данных, то их может быть удобно разбирать SAX-парсером. Выбрасывая при этом успешно пропарсенные сообщения и останавливаясь на первом сообщении, которое не поместилось в буфер полностью.
... << RSDN@Home 1.1.4 beta 6a rev. 436>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, -Cheese-, Вы писали:
C>Здравствуйте, eao197, Вы писали:
E>>Лично я использую размер сообщения в его залоговке. C>ага C>мне, например, приходит сообщение в заголовке указано 503 байта C>а протокол говорит, что ты мол получил, 1013 байт
C>т.е. мне нужно следующее начинать с 2+503 байта? C>а это все данные там....? C>короче возюкаться нужно... а так всё прекрасно начиналось.... (до большой частоты обращений к серверу )
Да, повозюкаться тебе придется, раз такие вещи тебя удивляют. Еще интереснее ситуация станет, когда клиент или сервер не будет успевать вычитывать весь трафик. Или когда потребуется использовать сокеты в неблокирующем режиме, чтобы не висеть на длительных операциях connect (что бывает при некоторых жестких сбоях в сети)...
Вот, например, как у меня идет разбор входящего трафика:
so_4::ret_code_t
client_connection_t::read()
{
// Пытаемся прочитать очередную порцию данных.unsigned int bytes_to_read = query_read_buf_size();
auto_ptr_3::vect_ptr_t< char > buf(
new char[ bytes_to_read ], bytes_to_read );
unsigned int bytes_read;
so_4::ret_code_t rc;
/* m_phys_channel_ptr -- это объект, который физически работает с сокетом. */
rc = m_phys_channel_ptr->read(
buf.query(), bytes_to_read, bytes_read );
if( rc == success )
{
// Чтение прошло без ошибок.
size_t size;
/* В моем случае данные могут быть заархивированны, метод transform_incoming распаковывает их и помещает в атрибут m_ibuf. */
rc = transform_incoming( buf.query(), bytes_read, size );
// Если что-то трансформировали, то нужно
// поместить это в промежуточный буфер и
// попробовать разобрать.if( rc == success && size )
/* parse_ibuf -- это виртуальный метод, который переопределяется для разных типов соединений. */
rc = parse_ibuf();
}
return rc;
}
/* Вот в этом производном классе разбираются данные, в которых есть специальный заголовок с длиной сообщения. */
so_4::ret_code_t
sop_client_connection_t::parse_ibuf()
{
/* Идем в начало буфера чтобы начать его новый просмотр. */
m_ibuf.set_pos( 0 );
while( so_4::sop::package_header_t::e_image_size <= m_ibuf.size() )
{
// Считываем заголовок пакета и проверяем его.
so_4::sop::package_header_t header;
m_ibuf >> header;
if( header.is_valid() )
{
// Заголовок корректен. Обрабатываем тело пакета.unsigned int body_size = header.size();
// Если все тело пакета находится в буфере, то
// изымаем его и отсылаем на обработку.if( so_4::sop::package_header_t::e_image_size +
body_size <= m_ibuf.size() )
{
/* Сюда попадаем только, если в буфере сообщение находится целиком. */
so_4::rt::msg_auto_ptr_t< msg_sop_package >
msg( new msg_sop_package() );
msg->m_channel = m_comm_channel;
msg->m_package.insert( 0, m_ibuf,
m_ibuf.pos(), body_size );
// Заблокирован ли канал?bool is_blocked = ( msg->m_is_blocked =
in_package_processed( body_size ) );
msg.send( communicator_agent_name(),
"msg_sop_package" );
// Обработанные данные из буфера нужно изъять./* Вот так данные из промежуточного буфера удаляются. После этого указатель перемещается в начало буфера. */
m_ibuf.erase( 0,
so_4::sop::package_header_t::e_image_size +
body_size );
// Если канал оказался заблокированным, то разбор
// буфера нужно прекратить.if( is_blocked )
break;
}
else// В буфере недостаточно данных.
// Просмотр буфера можно прекратить.break;
}
else
{
// Обнаружен некорректный заголовок.return so_4_make_rc( invalid_package_header,
"Invalid SOP package header" );
}
}
// Если после просмотра в буфере оказалось слишком
// много данных, то это ошибка.if( m_ibuf.size() > m_ibuf_max_size )
{
return so_4_make_rc( ibuf_too_large,
"Input buffer is too large" );
}
return so_4::ret_code_t();
}
Так что, если нет желания с такими низкоуровневыми особенностями разбираться, то возьми какую-нибудь готовую библиотеку. Тот же ACE, например. В нем и чтение/запись реализованы. И специальный класс MessageBlock. И специальный класс для перекодировок значений в BigEndian/LittleEndian.
Либо вообще откажись от собственного низкоуровневого протокола в пользу чего-нибудь готового. Например, CORBA, Ice, SOAP, XML-RPC. Или даже собственный протокол поверх HTTP: во-первых, ты получишь готовый транспортный уровень, во-вторых, request/response протоколы на основе HTTP (да и CORBA) могут хорошо маштабироваться (с балансировкой нагрузки на несколько web-серверов) + отказоустойчивость в этом случае есть.
... << RSDN@Home 1.1.4 beta 6a rev. 436>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, eao197, Вы писали:
E>Здравствуйте, CrystaX, Вы писали:
CX>>Здравствуйте, -Cheese-, Вы писали:
C>>>Вот назрел такой вопрос пишу сервер, кот. слушает по определённому порту клиента. C>>>Работа в режиме запрос-ответ. C>>>Иногда возникают ситуации, когда приходит запрос и готовится на него ответ, за это время клиент может ещё слать мне запросы.... Так вот после ответа приходят ко мне запросы, а вернее сказать запрос который есть склееные запросы к серверу за время пока отвечал сервер... а это не есть хорошо
E>Это вообще фундаментальная особенность TCP-трафика: пакеты, которые на отправляющей стороне отсылаются отдельно, поступать могут на принимающую сторону либо склеенными, либо наоборот -- разделенными на более мелкие части.
C>>>Можно ли как-то это пофиксить? Чтоб они приходили в порядке очереди?
CX>>TCP? Тогда маркер в конце каждого сообщения или размер сообщения в самом начале. E>+1
E>Именно. Причем нужно будет позаботиться о буфере куда данные из канала поступают и там парсятся. Те данные, которые успешно распарсены из буфера изымаются. Остальны остаются ждать продолжения.
E>Если сообщения имеют регулярную текстовую структуру (например, XML), то можно обойтись без меток или размера -- сама структура сообщения позволит выделить сообщение целиком. Но это может быть не эффективно на больших сообщениях.
E>Лично я использую размер сообщения в его залоговке.
Но не только, если протокол свой, и обработка ошибок висит на тебе, можно и даже желательно сделать свой подзаголовок вида 000001002003, в этом случае можно контролировать не только общую длину пакета, независимо от количества сегментов, склеиваеться или нет, но и номер субпакета который пришел и его отдельную длину, имхо в этом случае порядок точно не перепутаеться и в контрольную сумму будет легче поверить
Здравствуйте, eao197, Вы писали:
E>Здравствуйте, CrystaX, Вы писали:
CX>>Здравствуйте, eao197, Вы писали:
E>>>Если сообщения имеют регулярную текстовую структуру (например, XML), то можно обойтись без меток или размера -- сама структура сообщения позволит выделить сообщение целиком. Но это может быть не эффективно на больших сообщениях.
CX>>Пример: HTTP. Есть заголовок Content-Length, в котором указывается длина body.
E>Я имел в виду чуть-чуть другое. Например, когда сообщения имеют вид: E>
E>Если поступают большие блоки данных, в которых могут быть множество подобных небольших данных, то их может быть удобно разбирать SAX-парсером. Выбрасывая при этом успешно пропарсенные сообщения и останавливаясь на первом сообщении, которое не поместилось в буфер полностью.
Да, я понял. Но в данном случае это непринципиально. Ровно так же на этом месте может быть множество HTTP сообщений.
Здравствуйте, c0unt, Вы писали:
E>>Лично я использую размер сообщения в его залоговке.
C>Но не только, если протокол свой, и обработка ошибок висит на тебе, можно и даже желательно сделать свой подзаголовок вида 000001002003, в этом случае можно контролировать не только общую длину пакета, независимо от количества сегментов, склеиваеться или нет, но и номер субпакета который пришел и его отдельную длину, имхо в этом случае порядок точно не перепутаеться и в контрольную сумму будет легче поверить
Здравствуйте, eao197, Вы писали:
E>Либо вообще откажись от собственного низкоуровневого протокола в пользу чего-нибудь готового. Например, CORBA, Ice, SOAP, XML-RPC. Или даже собственный протокол поверх HTTP: во-первых, ты получишь готовый транспортный уровень, во-вторых, request/response протоколы на основе HTTP (да и CORBA) могут хорошо маштабироваться (с балансировкой нагрузки на несколько web-серверов) + отказоустойчивость в этом случае есть.
не получится... клиент как бы написан не мною и это есть приложение, кот. общается со многими серверами, в том числе и моим по определённому протоколу.
Здравствуйте, -Cheese-, Вы писали:
C>Здравствуйте, eao197, Вы писали:
E>>Либо вообще откажись от собственного низкоуровневого протокола в пользу чего-нибудь готового. Например, CORBA, Ice, SOAP, XML-RPC. Или даже собственный протокол поверх HTTP: во-первых, ты получишь готовый транспортный уровень, во-вторых, request/response протоколы на основе HTTP (да и CORBA) могут хорошо маштабироваться (с балансировкой нагрузки на несколько web-серверов) + отказоустойчивость в этом случае есть.
C>не получится... клиент как бы написан не мною и это есть приложение, кот. общается со многими серверами, в том числе и моим по определённому протоколу.
Тогда могу посоветовать посмотреть-таки в сторону ACE и почитать книгу: C++ Network Programming vol1: Mastering Complexity with ACE and Patterns (она была в eMule). Даже если ты ACE и не будешь использовать, то хотя бы для общего развития она будет полезна.
... << RSDN@Home 1.1.4 beta 6a rev. 436>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, CrystaX, Вы писали:
CX>Ну тогда только с помощью буффера. TCP не гарантирует, что ты получишь весь пакет за один раз или за один раз только один пакет. Даже более того — возможна ситуация, когда при чтении из сокета ты получаешь меньшее количество байт, чем запрашивал, и это ничего не значит. При следующем чтении ты запросто можешь прочитать еще кусок (равный или меньший запрошенному количеству). Конец соединения происходит только при возврате 0 байт. CX>TCP — потоковый протокол. Разбить его на смысловые фрагменты — уже пользовательская (твоя) задача. Вообще говоря, ничего страшного здесь нет. Сам сейчас занимаюсь подобными вещами.
если не сложно.. можно ссылочку (ну или вкратце обьяснить) на то, как склеиваются сообщения (по какому принципу) . Что я их правильно распарсил.
Здравствуйте, -Cheese-, Вы писали:
C>если не сложно.. можно ссылочку (ну или вкратце обьяснить) на то, как склеиваются сообщения (по какому принципу) . Что я их правильно распарсил.
А принципа никакого нет. Как придут, так и будут склеены.
Вот у меня сейчас сделано так:
Есть буфер необработанных сообщений. Это просто plain text. Я натравливаю на этот буфер парсер. Парсер возвращает три состояния.
1. ОК, сообщение прочитано. В этом случае оно удаляется из буффера (его длина известна, т.к. оно уже распарсено) и производится попытка парсинга следующего сообщения.
2. В буфере лежит корректное сообщение, но не полное (буфер закончился). В таком случае парсинг откладывается на потом, когда придут дополнительные данные. С буфером никаких действий не предпринимается.
3. Парсер выдает ошибку (некорректное сообщение). Обрабатываем ошибочную ситуацию.
В отдельном потоке при этом крутится цикл приема сообщений и помещения их в буфер.
Это одна из возможных схем реализации. Какую именно выбрать тебе — нужно решать на основании твоей задачи.
Здравствуйте, CrystaX, Вы писали:
CX>А принципа никакого нет. Как придут, так и будут склеены.
CX>Вот у меня сейчас сделано так: CX>Есть буфер необработанных сообщений. Это просто plain text. Я натравливаю на этот буфер парсер. Парсер возвращает три состояния. CX>1. ОК, сообщение прочитано. В этом случае оно удаляется из буффера (его длина известна, т.к. оно уже распарсено) и производится попытка парсинга следующего сообщения. CX>2. В буфере лежит корректное сообщение, но не полное (буфер закончился). В таком случае парсинг откладывается на потом, когда придут дополнительные данные. С буфером никаких действий не предпринимается. CX>3. Парсер выдает ошибку (некорректное сообщение). Обрабатываем ошибочную ситуацию.
CX>В отдельном потоке при этом крутится цикл приема сообщений и помещения их в буфер.
CX>Это одна из возможных схем реализации. Какую именно выбрать тебе — нужно решать на основании твоей задачи.
не совсем понял...
В основном мне приходят склееные >=2 сообщения..
вот к примеру ко мне приходят сообщения вида
(2 байта для размера сообщения) + іmessage1іmessage2
так вот что это за символ (і)? Всегда ли он отделяет сообщение одно от другого?
как мне их отделить?
Здравствуйте, -Cheese-, Вы писали:
C>не совсем понял... C>В основном мне приходят склееные >=2 сообщения.. C>вот к примеру ко мне приходят сообщения вида C>(2 байта для размера сообщения) + іmessage1іmessage2 C>так вот что это за символ (і)? Всегда ли он отделяет сообщение одно от другого? C>как мне их отделить?
Это какие-то части сообщений тоже. TCP не добавляет никаких маркеров. Это поток в чистом виде и ничего более. Ты уверен, что там именно 2, а не 4 байта, обозначающих размер? Кроме того, какое представление целочисленных величин используется (какой byte-order)?
Здравствуйте, CrystaX, Вы писали:
CX>Это какие-то части сообщений тоже. TCP не добавляет никаких маркеров. Это поток в чистом виде и ничего более. Ты уверен, что там именно 2, а не 4 байта, обозначающих размер?
когда не слеиваются, то точно 2 байта
CX>Кроме того, какое представление целочисленных величин используется (какой byte-order)?
Чего?
Здравствуйте, -Cheese-, Вы писали:
C>Здравствуйте, CrystaX, Вы писали:
CX>>Это какие-то части сообщений тоже. TCP не добавляет никаких маркеров. Это поток в чистом виде и ничего более. Ты уверен, что там именно 2, а не 4 байта, обозначающих размер? C>когда не слеиваются, то точно 2 байта
CX>>Кроме того, какое представление целочисленных величин используется (какой byte-order)? C>Чего?
Здравствуйте, CrystaX, Вы писали:
CX>Поищи тогда здесь или в MSDN-е. Ключевые слова: network byte order, host byte order.
прочитал. вроде понял.
не понял к чему это...
может я не правильно изьяснился в начале.
проблема в следующем:
пакет, который мне приходит имеет след. структуру —
2 байта (число N) + N байт
где N — длина сообщения
т.е. пакеты вида
x1mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
x2mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
x3mmmmmmmmmmmmm
но при большом потоке запросов мне приходит сообщение вида
x1mmmmmmmmmmmmmmmmmmmmmmx2mmmmmmmmmmmmmmmmmmmmx3mmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
где
x1 — длина всего сообщения
x2 — длина второго сообщения
x3 — длина третьего сообщения
Внимание, вопрос:
как мне узнать где начинается x2,x3?
Здравствуйте, -Cheese-, Вы писали:
C>Здравствуйте, CrystaX, Вы писали:
CX>>Поищи тогда здесь или в MSDN-е. Ключевые слова: network byte order, host byte order. C>прочитал. вроде понял. C>не понял к чему это...
C>может я не правильно изьяснился в начале. C>проблема в следующем: C>пакет, который мне приходит имеет след. структуру — C>2 байта (число N) + N байт C>где N — длина сообщения
C>т.е. пакеты вида C>x1mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm C>x2mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm C>x3mmmmmmmmmmmmm
C>но при большом потоке запросов мне приходит сообщение вида C>x1mmmmmmmmmmmmmmmmmmmmmmx2mmmmmmmmmmmmmmmmmmmmx3mmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
C>где C>x1 — длина всего сообщения C>x2 — длина второго сообщения C>x3 — длина третьего сообщения
C>Внимание, вопрос: C> как мне узнать где начинается x2,x3?
Ничего не понимаю. Ну прочитал ты первые два байта (x1). Теперь ты знаешь длину первого сообщения. Считываешь x1 байт. Следующие два байта — это уже число x2. Считываешь x2 байт. И т.д.
Здравствуйте, CrystaX, Вы писали:
CX>Ничего не понимаю. Ну прочитал ты первые два байта (x1). Теперь ты знаешь длину первого сообщения. CX>Считываешь x1 байт. Следующие два байта — это уже число x2. Считываешь x2 байт. И т.д.
в том то и дело, что х1 — не длина первого сообщения, а длина всего склеенного сообщения!
CX>Я, может, чего-то не понимаю?
Здравствуйте, -Cheese-, Вы писали:
C>Здравствуйте, CrystaX, Вы писали:
CX>>Ничего не понимаю. Ну прочитал ты первые два байта (x1). Теперь ты знаешь длину первого сообщения. CX>>Считываешь x1 байт. Следующие два байта — это уже число x2. Считываешь x2 байт. И т.д. C>в том то и дело, что х1 — не длина первого сообщения, а длина всего склеенного сообщения!
То есть это особенность протокола такая? Если да, то никак тебе помочь не могу, не зная протокола. Скажи хоть тогда, что за протокол, а то иначе гадание на кофейной гуще получается
Здравствуйте, CrystaX, Вы писали:
CX>То есть это особенность протокола такая? Если да, то никак тебе помочь не могу, не зная протокола. Скажи хоть тогда, что за протокол, а то иначе гадание на кофейной гуще получается
TCP\IP
самый обнаковенный... просто вот такая фигня получается со склейкой...
может это глюк?
часто повторяющийся...
Здравствуйте, -Cheese-, Вы писали:
C>Здравствуйте, CrystaX, Вы писали:
CX>>То есть это особенность протокола такая? Если да, то никак тебе помочь не могу, не зная протокола. Скажи хоть тогда, что за протокол, а то иначе гадание на кофейной гуще получается
C>TCP\IP C>самый обнаковенный... просто вот такая фигня получается со склейкой...
Ты не понял. Я имел в виду высокоуровневый протокол. TCP — это транспортный протокол.
C>может это глюк? C>часто повторяющийся...
Здравствуйте, CrystaX, Вы писали:
CX> C>>TCP\IP C>>самый обнаковенный... просто вот такая фигня получается со склейкой... CX>Ты не понял. Я имел в виду высокоуровневый протокол. TCP — это транспортный протокол.
ну я ещё не волшебник.... только учусь C>>может это глюк? C>>часто повторяющийся...
CX>Очень вряд ли. Скорее всего у тебя баг.
ну будем смотреть.
Здравствуйте, -Cheese-, Вы писали:
C>Здравствуйте, CrystaX, Вы писали:
CX>> C>>>TCP\IP C>>>самый обнаковенный... просто вот такая фигня получается со склейкой... CX>>Ты не понял. Я имел в виду высокоуровневый протокол. TCP — это транспортный протокол. C>ну я ещё не волшебник.... только учусь C>>>может это глюк? C>>>часто повторяющийся...
CX>>Очень вряд ли. Скорее всего у тебя баг. C>ну будем смотреть.
C>всё равно спасибо за потраченное время!
Приведи минимальный код, на котором проявляется ошибка. Думается, баг будет быстро найден.
Здравствуйте, CrystaX, Вы писали:
CX>Приведи минимальный код, на котором проявляется ошибка. Думается, баг будет быстро найден.
я бы не назвал это ошибкой... просто мне приходит сообщение описанного выше вида...
в основе как я говорил берклиевский классический сервер.
(взял где-то в инете и чуть модифицировал)
bool process_data(SOCKET sock)
{
/////////////////if ((received = recv(sock, buf, RECVBUFSIZ, 0)) != SOCKET_ERROR && received>2) {
// здесь вот buf может являться тем злополучным сообщением (а может и не быть им).
buf[received] = '\0';
pushToStack(buf,received); // тут просто парсером прогоняю сообщение и кладу в стек сообщенийreturn true;
}
return false;
}
/////////////////////////////////////////////////////////////////////////////
UINT ListenSockets(LPVOID lpvoid)
{
while (true) {
///////////if ((accepted = accept(ssocket, (struct sockaddr *) &ca, (socklen_t *)&cal)) != SOCKET_ERROR){
int size=sizeof(sockaddr);
sockaddr_in adr;
int ret=getpeername(accepted,(sockaddr*)&adr,&size);
if(adr.sin_addr.S_un.S_addr==IP_CLIENT){
if(clients.size()==0){
sprintf(buffer,"Client connected [%s]",inet_ntoa(adr.sin_addr));
clients.push_back(accepted);
}else{
sprintf(buffer,"Client already connected [%s]. Ignored",inet_ntoa(adr.sin_addr));
shutdown_socket(&accepted);
}
}else{
sprintf(buffer,"Illegal client want to connect [%s]",inet_ntoa(adr.sin_addr));
shutdown_socket(&accepted);
}
toLog(buffer);
}
// Preparing descriptor sets
FD_ZERO(&readfds);
FD_ZERO(&exfds);
FD_SET(ssocket, &exfds);
tv.tv_sec = 1;
tv.tv_usec = 0;
maxfd = ssocket;
for (CL::iterator ii = clients.begin(); ii != clients.end(); ++ii) {
FD_SET((SOCKET )*ii, &readfds);
FD_SET((SOCKET )*ii, &exfds);
maxfd = max(maxfd, (SOCKET )*ii);
}
// select failing breaks the workif (select(maxfd + 1, &readfds, NULL, &exfds, &tv) == -1) break;
// On exception in server socket also breaks immediatelyif(FD_ISSET(ssocket, &exfds)) break;
// Test events on client socketsfor (ii = clients.begin(); ii != clients.end(); ++ii) {
if (FD_ISSET(*ii, &exfds)) {
toLog("Client exception");
if (*ii != INVALID_SOCKET) shutdown_socket(&(*ii));
if ((ii = clients.erase(ii)) == clients.end()) break;
}
if (FD_ISSET(*ii, &readfds) && !process_data(*ii)) {
toLog("Failed to read client");
if (*ii != INVALID_SOCKET) shutdown_socket(&(*ii));
if ((ii = clients.erase(ii)) == clients.end()) break;
}
}
}
for (CL::iterator ii = clients.begin(); ii != clients.end(); ++ii) {
if (*ii != INVALID_SOCKET) shutdown_socket(&(*ii));
if ((ii = clients.erase(ii)) == clients.end()) break;
}
/////////////////
shutdown_socket(&ssocket);
/////////////////
}
отдельный процесс время от времени читает стек и отвечает на запросы.
Здравствуйте, -Cheese-, Вы писали:
C>Здравствуйте, CrystaX, Вы писали:
CX>>Приведи минимальный код, на котором проявляется ошибка. Думается, баг будет быстро найден. C>я бы не назвал это ошибкой... просто мне приходит сообщение описанного выше вида...
C>в основе как я говорил берклиевский классический сервер. C>(взял где-то в инете и чуть модифицировал)
C>
C>bool process_data(SOCKET sock)
C>{
C> /////////////////
C> if ((received = recv(sock, buf, RECVBUFSIZ, 0)) != SOCKET_ERROR && received>2) {
C> // здесь вот buf может являться тем злополучным сообщением (а может и не быть им).
C> buf[received] = '\0';
C> pushToStack(buf,received); // тут просто парсером прогоняю сообщение и кладу в стек сообщений
C> return true;
C> }
C> return false;
C>}
C>
Вот здесь неверно. Тебе никто не гарантирует, что данные будут приходить кусками, не меньшими чем 2 или еще сколько байт. Теоретически возможна ситуация, когда данные вообще могут приходить по одному байту за одно чтение. В этом случае ты их потеряешь.
Не факт, конечно, что это и единственный баг, но это точно надо исправить. Насчет же следующей функции (ListenSockets) — извини, не понял, что она должна делать. Никаких данных ты в ней не читаешь, так откуда же они берутся?
C>
C>/////////////////////////////////////////////////////////////////////////////
C>UINT ListenSockets(LPVOID lpvoid)
C>{
C> while (true) {
C> ///////////
C> if ((accepted = accept(ssocket, (struct sockaddr *) &ca, (socklen_t *)&cal)) != SOCKET_ERROR){
C> int size=sizeof(sockaddr);
C> sockaddr_in adr;
C> int ret=getpeername(accepted,(sockaddr*)&adr,&size);
C> if(adr.sin_addr.S_un.S_addr==IP_CLIENT){
C> if(clients.size()==0){
C> sprintf(buffer,"Client connected [%s]",inet_ntoa(adr.sin_addr));
C> clients.push_back(accepted);
C> }else{
C> sprintf(buffer,"Client already connected [%s]. Ignored",inet_ntoa(adr.sin_addr));
C> shutdown_socket(&accepted);
C> }
C> }else{
C> sprintf(buffer,"Illegal client want to connect [%s]",inet_ntoa(adr.sin_addr));
C> shutdown_socket(&accepted);
C> }
C> toLog(buffer);
C> }
C> // Preparing descriptor sets
C> FD_ZERO(&readfds);
C> FD_ZERO(&exfds);
C> FD_SET(ssocket, &exfds);
C> tv.tv_sec = 1;
C> tv.tv_usec = 0;
C> maxfd = ssocket;
C> for (CL::iterator ii = clients.begin(); ii != clients.end(); ++ii) {
C> FD_SET((SOCKET )*ii, &readfds);
C> FD_SET((SOCKET )*ii, &exfds);
C> maxfd = max(maxfd, (SOCKET )*ii);
C> }
C> // select failing breaks the work
C> if (select(maxfd + 1, &readfds, NULL, &exfds, &tv) == -1) break;
C> // On exception in server socket also breaks immediately
C> if(FD_ISSET(ssocket, &exfds)) break;
C> // Test events on client sockets
C> for (ii = clients.begin(); ii != clients.end(); ++ii) {
C> if (FD_ISSET(*ii, &exfds)) {
C> toLog("Client exception");
C> if (*ii != INVALID_SOCKET) shutdown_socket(&(*ii));
C> if ((ii = clients.erase(ii)) == clients.end()) break;
C> }
C> if (FD_ISSET(*ii, &readfds) && !process_data(*ii)) {
C> toLog("Failed to read client");
C> if (*ii != INVALID_SOCKET) shutdown_socket(&(*ii));
C> if ((ii = clients.erase(ii)) == clients.end()) break;
C> }
C> }
C> }
C> for (CL::iterator ii = clients.begin(); ii != clients.end(); ++ii) {
C> if (*ii != INVALID_SOCKET) shutdown_socket(&(*ii));
C> if ((ii = clients.erase(ii)) == clients.end()) break;
C> }
C> /////////////////
C> shutdown_socket(&ssocket);
C> /////////////////
C>}
C>
C>отдельный процесс время от времени читает стек и отвечает на запросы.
Здравствуйте, CrystaX, Вы писали:
CX>Вот здесь неверно. Тебе никто не гарантирует, что данные будут приходить кусками, не меньшими чем 2 или еще сколько байт. Теоретически возможна ситуация, когда данные вообще могут приходить по одному байту за одно чтение. В этом случае ты их потеряешь.
я думаю такого не случится.
сервера стоят рядом на одном хабе.
CX>Не факт, конечно, что это и единственный баг, но это точно надо исправить. Насчет же следующей функции (ListenSockets) — извини, не понял, что она должна делать. Никаких данных ты в ней не читаешь, так откуда же они берутся?
как не читаю...
if (FD_ISSET(*ii, &readfds) && !process_data(*ii)) {
toLog("Failed to read client");
if (*ii != INVALID_SOCKET) shutdown_socket(&(*ii));
if ((ii = clients.erase(ii)) == clients.end()) break;
}
Здравствуйте, -Cheese-, Вы писали:
C>Здравствуйте, CrystaX, Вы писали:
CX>>Вот здесь неверно. Тебе никто не гарантирует, что данные будут приходить кусками, не меньшими чем 2 или еще сколько байт. Теоретически возможна ситуация, когда данные вообще могут приходить по одному байту за одно чтение. В этом случае ты их потеряешь.
C>я думаю такого не случится. C>сервера стоят рядом на одном хабе.
Это ничего не значит. Если на принимающей стороне переполнится буфер TCP, то эта ситуация возможна. А ты сам говоришь, что данные идут с высокой частотой.
CX>>Не факт, конечно, что это и единственный баг, но это точно надо исправить. Насчет же следующей функции (ListenSockets) — извини, не понял, что она должна делать. Никаких данных ты в ней не читаешь, так откуда же они берутся? C>как не читаю...
C>
C> if (FD_ISSET(*ii, &readfds) && !process_data(*ii)) {
C> toLog("Failed to read client");
C> if (*ii != INVALID_SOCKET) shutdown_socket(&(*ii));
C> if ((ii = clients.erase(ii)) == clients.end()) break;
C> }
C>
Ок, понял. И где в твоей process_data проверка числа, находящегося в первых двух байтах?
Здравствуйте, CrystaX, Вы писали:
CX>Ок, понял. И где в твоей process_data проверка числа, находящегося в первых двух байтах?
так вот в этом-то и проблема.
я не знаю как обрабатывать полученное сообщение (если оно склеенное).
Делал так: повторил ситуацию, получил эту склейку (в режиме отладки исследовал её), смотрю и не знаю как её побороть..
Здравствуйте, -Cheese-, Вы писали:
C>Здравствуйте, CrystaX, Вы писали:
CX>>Ок, понял. И где в твоей process_data проверка числа, находящегося в первых двух байтах? C>так вот в этом-то и проблема. C>я не знаю как обрабатывать полученное сообщение (если оно склеенное). C>Делал так: повторил ситуацию, получил эту склейку (в режиме отладки исследовал её), смотрю и не знаю как её побороть..
Нет, не так. Вот примерный код, как это должно выглядеть:
bool process_data(SOCKET sock)
{
int received = 0, required = 2;
while((received = recv(sock, buf + received, required - received, 0)) > 0);
if(received == SOCKET_ERROR) return false;
// Тут нужно особое внимание обратить на byte order. Если у тебя архитектура little-endian,
// а значение высылается в сетевом порядке байт, то ntohs обязательна
// short здесь в предположении что sizeof(short) == 2short size = ntohs(*reinterpret_cast<short *>(buf));
required = size;
received = 0;
while((received = recv(sock, buf + received, required - received, 0)) > 0);
if(received == SOCKET_ERROR) return false;
buf[size] = '\0';
pushToStack(buf, size);
return true;
}
CX> // Тут нужно особое внимание обратить на byte order. Если у тебя архитектура little-endian, CX> // а значение высылается в сетевом порядке байт, то ntohs обязательна CX> // short здесь в предположении что sizeof(short) == 2
приложение будет запускаться на 2000 винде
компилятор VC++
Здравствуйте, -Cheese-, Вы писали:
C>Здравствуйте, CrystaX, Вы писали:
C>бррррр C>а для чего ещё раз?
Смотри:
Первые два Оставшаяся часть
байта сообщения
| |
+--------+------------------------+
| xx xx | xx xx xx xx xx xx |
+--------+------------------------+
Чтобы узнать длину сообщения, нужно сначала прочитать первые два байта. Это первое чтение. Затем, зная длину — оставшуюся часть. Это второе чтение.
А цикл — это как раз гарантия того, что данные будут получены полностью, без потерь, причем столько, сколько нужно.
C>
Здравствуйте, -Cheese-, Вы писали:
CX>> // Тут нужно особое внимание обратить на byte order. Если у тебя архитектура little-endian, CX>> // а значение высылается в сетевом порядке байт, то ntohs обязательна CX>> // short здесь в предположении что sizeof(short) == 2
C>приложение будет запускаться на 2000 винде C>компилятор VC++
Важно не это, а то, в каком виде данные передает другая сторона.
Здравствуйте, -Cheese-, Вы писали:
C>может я чего-то не понял, но по ходу функции в выделенном месте всегда получается, что received == SOCKET_ERROR
Обрати внимание на ntohs. Быть может, ее вызов здесь не нужен. Могу предположить такую ситуацию: данные передаются в little-endian виде, а ntohs их переворачивает. Впоне может получиться ситуация с отрицательным size. Тогда возможно возвращение SOCKET_ERROR. Других причин не вижу. Хотя, конечно, это может быть и наведенная ошибка. Не могу гадать.
Здравствуйте, CrystaX, Вы писали:
CX>Обрати внимание на ntohs. Быть может, ее вызов здесь не нужен. Могу предположить такую ситуацию: данные передаются в little-endian виде, а ntohs их переворачивает. Впоне может получиться ситуация с отрицательным size. Тогда возможно возвращение SOCKET_ERROR. Других причин не вижу. Хотя, конечно, это может быть и наведенная ошибка. Не могу гадать.
размер совпадает с передаваемым сообщением, смотрел..
Здравствуйте, -Cheese-, Вы писали:
C>Здравствуйте, CrystaX, Вы писали:
CX>>Обрати внимание на ntohs. Быть может, ее вызов здесь не нужен. Могу предположить такую ситуацию: данные передаются в little-endian виде, а ntohs их переворачивает. Впоне может получиться ситуация с отрицательным size. Тогда возможно возвращение SOCKET_ERROR. Других причин не вижу. Хотя, конечно, это может быть и наведенная ошибка. Не могу гадать.
C>размер совпадает с передаваемым сообщением, смотрел..
Ну тогда ищи, что еще сказать. Отладчик тебе в руки и вперед! А я не телепат. Угадать, что там происходит, не могу.