Название: 2 пакета сливаются в один
Отправлено: Firefox от Декабря 19, 2013, 14:15
Здравствуйте. у меня есть 2 программы клиент и сервер TCP. по сети общаются. пакет от сервера к клиету передается такой: длинна пакета, разделитель(|), заголовок пакета (KS), команда из 4-х цифр.так вот если пакеты передаются раз в пол секунды то иногда на стороне клиента приходит строка состоящая из 2-х последовательно записанных пакетов. если увеличить время например раз в 7 секунд то все в порядке. выводила пакеты на сервере перед самой функцией write и сразу после read на клиенте. запись на сервере void ClientSocket::slotSendData_pod(QByteArray arr) { int sz=0; if(socket_out->state()!=QAbstractSocket::ConnectedState) return;
sz=socket_out->write(arr);
if(sz!=arr.size()) { qDebug()<<"SV_P: not all information writes to protocol"; } } чтение на клиенте { QTcpSocket *socket_in=(QTcpSocket *)sender(); QByteArray arrData; arrData.resize(0); QDataStream in(socket_in); in.setVersion(QDataStream::Qt_4_2); quint64 NextBlockSize=0; for(;;) { if(!NextBlockSize) { if((unsigned)socket_in->bytesAvailable()<sizeof(quint16)) break; NextBlockSize=socket_in->bytesAvailable(); } if((unsigned)socket_in->bytesAvailable()<=NextBlockSize) break; } arrData= socket_in->read(NextBlockSize);
if(arrData.size()>1) { QString pak=(QString)(arrData); QStringList Lst=pak.split(packetSeparator); qDebug()<<"IN"<<Lst; .... }
Название: Re: 2 пакета сливаются в один
Отправлено: Old от Декабря 19, 2013, 14:19
Это нормально, так и должно быть. Можно воспользоваться флушем, но лучше сразу правильно разбирать несколько пакетов на принимающей стороне.
Название: Re: 2 пакета сливаются в один
Отправлено: Firefox от Декабря 19, 2013, 15:22
flush не помог. честно говоря мне по сути алгоритма это не сильно хорошо :-[. я надеялась принять пакет и отправить далее без всяких наворотов. потому как в есть необходимость соблюдать временные рамки для каждого пакета.
Название: Re: 2 пакета сливаются в один
Отправлено: Old от Декабря 19, 2013, 15:27
flush не помог. честно говоря мне по сути алгоритма это не сильно хорошо :-[. я надеялась принять пакет и отправить далее без всяких наворотов. потому как в есть необходимость соблюдать временные рамки для каждого пакета.
Пакеты объединяет сетевой стек. Это специальная мера, что бы уменьшить бесполезный трафик. Не стоит пытаться это обойти. Вы настроите отправляющую машину на отправку каждого пакета отдельно, но ваши пакеты могут сливаться и на транзитных узлах. Кстати, может быть и обратное, пакет может приходить по частям. Делайте сразу правильно, поверьте, будет меньше проблем в дальнейшем. :) P.S. Кстати, у вас ошибки на принимающей стороне. Это будет работать в синтетических тестах и может (скорее всего будет) не работать на реальной сети.
Название: Re: 2 пакета сливаются в один
Отправлено: ploop от Декабря 19, 2013, 15:53
Вот, кстати, пример принимающего кода, ещё не отлаженный, но корректно работающий, если в сеть выплюнуть подряд несколько разнообразных пакетов от нескольких байт до сотен мегабайт. Заголовок имеет такую структуру: 4 байта - размер блока 32 байта - хэш этого числа (размера), чтобы отсеять мусор. Иначе любой залётный байт на этот порт начнёт приниматься и разбираться. Я взял MD5, но можно что-нибудь попроще. Далее - сам блок данных. C++ (Qt) void tfrclient::slotReadyRead() { // Чтение сокета QTcpSocket* pClientSocket = (QTcpSocket*)sender(); QDataStream in(pClientSocket); in.setVersion(QDataStream::Qt_4_2); // Ждём приёма всего сообщения while(pClientSocket->bytesAvailable() > 0) { // Пока в буфере что-то есть, крутимся в цикле if ((pClientSocket->bytesAvailable() > sizeof(quint32)) && (block_size == 0)) { // Если количество байт в буфере больше 4х, и мы ещё не // забрали размер посылки, заберём этот размер QString md5; quint32 size; quint32 p1, p2; in >> size; p1 = pClientSocket->bytesAvailable(); in >> md5; p2 = pClientSocket->bytesAvailable(); if(md5 == getMd5(size)) { block_size = size-(p1-p2); qDebug() << "size=" << block_size; } } if (block_size > 0) { // Если размер у нас есть, складываем всё в буфер // до тех пор, пока не примем всю посылку while((input_buf.size() < (int)block_size) && (pClientSocket->bytesAvailable() > 0)) { // Забираем только ту часть пакета, которая указана в заголовке QByteArray tmp; quint32 size = (block_size > pClientSocket->bytesAvailable()) ? pClientSocket->bytesAvailable() : block_size; tmp.resize(size); in.readRawData(tmp.data(),size); input_buf.append(tmp); } qDebug() << "input_buf="<<input_buf.size()<<"NextBlock="<<block_size; if (input_buf.size() > (int)block_size) { qDebug() << "Error! Clean socket"; pClientSocket->readAll(); pClientSocket->reset(); block_size = 0; input_buf.clear(); return; } if (input_buf.size() == (int)block_size) { // Наш буфер заполнен, обрабатываем parse_buf(&input_buf); block_size = 0; input_buf.clear(); } else { qDebug() << "Next loop!"; return; } } } }
Название: Re: 2 пакета сливаются в один
Отправлено: Fregloin от Декабря 21, 2013, 14:22
ужасный код, не в обиду. во первых. во вторых вычислять хеш каждый раз на прием и отправку думаю будет накладновато, если конечно скорость не важна. в третьих статически преобразовывать sender к QTcpSocket можно, но лучще делать через qobject_cast. ну и главное ваш код не будет работать с не Qt приложениями.
Название: Re: 2 пакета сливаются в один
Отправлено: GPPsoft от Декабря 24, 2013, 11:47
Проблема еще актуальна?
Название: Re: 2 пакета сливаются в один
Отправлено: ploop от Января 14, 2014, 12:34
ужасный код, не в обиду. Писался на коленке, сейчас переписан полностью. Да и опыта у меня очень мало. во вторых вычислять хеш каждый раз на прием и отправку думаю будет накладновато, если конечно скорость не важна Не важна, у меня пакет раз в несколько минут. ваш код не будет работать с не Qt приложениями. Не актуально, но хотелось бы знать почему? Из-за сериализации через QDataStream? Так это и ежу понятно. Или Вы что-то ещё имели ввиду?
Название: Re: 2 пакета сливаются в один
Отправлено: LisandreL от Января 15, 2014, 09:14
Из-за сериализации через QDataStream? Так это и ежу понятно. Да это не такая уж проблема. Эта сериализация нормально документирована. Повторить её на другом языке в нужном объёме не такая уж и проблема.
Название: Re: 2 пакета сливаются в один
Отправлено: Firefox от Января 15, 2014, 13:17
не хочу создавать новую тему, потому спрошу в этой. мне кажется что обмен, который я написала не очень хорош. потому хочу переписать. и хочу спросить про само построение логики, чтоб обмен был логически лучше. вот старый пример сервера: tripserver.h
#ifndef TRIPSERVER_H #define TRIPSERVER_H
#include <QTcpServer> #include <QTcpSocket> #include <QApplication>
#include "clientsocket.h" #include "common_gs.h"
#define port_pod 1111 //#define port_out_pod 1112
class QTcpServer; class QTcpSocket;
class TripServer : public QTcpServer
{ Q_OBJECT
public: TripServer(QObject *parent = 0 ); QTcpSocket *socket_pod; QTcpSocket *socket_2d_8d; QTcpSocket *socket_sord_16KP; QTcpSocket *socket_sord_toACHVO; QTcpSocket *socket_sord_fromACHVO; ClientSocket *soc;
signals: void sendData_pod(); void sendData_sord(); void sendData_2d_8d(); void sendData_16KP(QString); void OTMData(int,int); void SignalRezim16KP(unsigned short,unsigned short); void BufferForSend(QByteArray); void IzzlOn(QByteArray); public slots: void slotDisconnected_pod(); void slotDisconnected_2d_8d(); void slotDisconnected_16KP(); void slotDisconnected_toACHVO(); void slotDisconnected_fromACHVO();
private: void incomingConnection(int socketId);
tripserver.cpp
#include "tripserver.h" TripServer::TripServer(QObject *parent) : QTcpServer(parent) { soc=new ClientSocket(this); socket_pod=new QTcpSocket(this); socket_2d_8d= new QTcpSocket(this); socket_sord_16KP= new QTcpSocket(this); socket_sord_toACHVO= new QTcpSocket(this); socket_sord_fromACHVO= new QTcpSocket(this); }
void TripServer::incomingConnection(int socketId) { qDebug()<<this->serverPort(); if( this->serverPort()==port_pod)// обмен с "Подыгрышем Связи" { socket_pod->setSocketDescriptor(socketId); if(socket_pod) { soc->socket_out=socket_pod; connect(socket_pod,SIGNAL(readyRead()),soc,SLOT(readClient())); connect(socket_pod,SIGNAL(disconnected()),this,SLOT(slotDisconnected_pod())); } }
if(this->serverPort()==_PORT_2D_8D)//обмен с СОРД по протоколу 2Д-8Д приборов { socket_2d_8d->setSocketDescriptor(socketId); if(socket_2d_8d) { soc->socket_out=socket_2d_8d; connect(socket_2d_8d,SIGNAL(disconnected()),this,SLOT(slotDisconnected_2d_8d())); connect(this,SIGNAL(sendData_2d_8d()),soc,SLOT(slotsendData_2d_8d())); connect(socket_2d_8d,SIGNAL(readyRead()),soc,SLOT(readClientSord_2d_8d())); connect(soc,SIGNAL(SendData_2d_8d()),soc,SLOT(slotsendData_2d_8d())); connect(soc,SIGNAL(Radiation_on(QByteArray)),this,SIGNAL(IzzlOn(QByteArray))); } } if(this->serverPort()==_PORT_16KP) //обмен с СОРД по протоколу 16КП прибора { socket_sord_16KP->setSocketDescriptor(socketId); if(socket_sord_16KP) { soc->socket_out=socket_sord_16KP; connect(socket_sord_16KP,SIGNAL(disconnected()),this,SLOT(slotDisconnected_16KP())); connect(socket_sord_16KP,SIGNAL(readyRead()),soc,SLOT(readClientSord_16KP())); connect(soc,SIGNAL(SignalRezim(unsigned short,unsigned short)),this,SIGNAL(SignalRezim16KP(unsigned short,unsigned short))); connect(soc,SIGNAL(SignBufferForSend(QByteArray)),this,SIGNAL(BufferForSend(QByteArray))); } } if(this->serverPort()==_PORT_FROM_ACHVO) // обмен с СОРД по протоколу АЧВО(отправка данных в АЧВО) { socket_sord_toACHVO->setSocketDescriptor(socketId); if(socket_sord_toACHVO) { soc->socket_out=socket_sord_toACHVO; connect(socket_sord_toACHVO,SIGNAL(disconnected()),this,SLOT(slotDisconnected_toACHVO())); } } if(this->serverPort()==_PORT_TO_ACHVO) // обмен с СОРД по протоколу АЧВО(прием данных от АЧВО) { socket_sord_fromACHVO->setSocketDescriptor(socketId); if(socket_sord_fromACHVO) { soc->socket_out=socket_sord_fromACHVO; connect(socket_sord_fromACHVO,SIGNAL(disconnected()),this,SLOT(slotDisconnected_fromACHVO())); bool nn=connect(socket_sord_fromACHVO,SIGNAL(readyRead()),soc,SLOT(readClientSord_Achvo())); qDebug()<<"nn"<<nn; } }
}
void TripServer::slotDisconnected_pod() { if(socket_pod) { socket_pod->disconnectFromHost(); disconnect(socket_pod,SIGNAL(readyRead()),soc,SLOT(readClient())); disconnect(socket_pod,SIGNAL(disconnected()),this,SLOT(slotDisconnected_pod())); //disconnect(this,SIGNAL(sendData_pod()),soc,SLOT(slotsendData_pod())); } } void TripServer::slotDisconnected_2d_8d() { if(socket_2d_8d) { socket_2d_8d->disconnectFromHost(); disconnect(this,SIGNAL(sendData_2d_8d()),soc,SLOT(slotsendData_2d_8d())); disconnect(socket_2d_8d,SIGNAL(disconnected()),this,SLOT(slotDisconnected_2d_8d())); disconnect(socket_2d_8d,SIGNAL(readyRead()),soc,SLOT(readClientSord_2d_8d())); disconnect(soc,SIGNAL(SendData_2d_8d()),soc,SLOT(slotsendData_2d_8d())); } } void TripServer::slotDisconnected_16KP() { if(socket_sord_16KP) { disconnect(socket_sord_16KP,SIGNAL(disconnected()),this,SLOT(slotDisconnected_16KP())); disconnect(socket_sord_16KP,SIGNAL(readyRead()),soc,SLOT(readClientSord_16KP())); } } void TripServer::slotDisconnected_toACHVO() { if(socket_sord_toACHVO) { disconnect(socket_sord_toACHVO,SIGNAL(disconnected()),this,SLOT(slotDisconnected_toACHVO())); } } void TripServer::slotDisconnected_fromACHVO() { if(socket_sord_fromACHVO) { disconnect(socket_sord_fromACHVO,SIGNAL(disconnected()),this,SLOT(slotDisconnected_fromACHVO())); disconnect(socket_sord_fromACHVO,SIGNAL(readyRead()),soc,SLOT(readClientSord_Achvo())); } }
вызов класса в главном потоке serv=new TripServer(this); servSord_2d_8d=new TripServer(this); serverSord_16KP= new TripServer(this); serverSord_toAchvo=new TripServer(this); serverSord_fromAchvo=new TripServer(this); qDebug()<<"PORT_client_16KP"<<PORT_client_16KP; serv->listen(QHostAddress::Any,PORT_client_16KP); servSord_2d_8d->listen(QHostAddress::Any,_PORT_2D_8D); serverSord_16KP->listen(QHostAddress::Any,_PORT_16KP); serverSord_toAchvo->listen(QHostAddress::Any,_PORT_TO_ACHVO); serverSord_fromAchvo->listen(QHostAddress::Any,_PORT_FROM_ACHVO);
//связь сигналов по пришедшим из теста данным с обработкой инф-и connect(serv->soc,SIGNAL(Sign_Sinhr(QString,QString,QString,QString,QString)),this,SLOT(slotATGS_Sinhr(QString,QString,QString,QString,QString))); connect(serv->soc,SIGNAL(Sign_NT()),this,SLOT(slotATGS_NT())); connect(serv->soc,SIGNAL(Sign_TLG(QString)),this,SLOT(slotATGS_TLG(QString))); connect(serv->soc,SIGNAL(Sign_KT()),this,SLOT(slotATGS_KT())); connect(serv->soc,SIGNAL(Sign_Pilot(QString,QString,QString)),this,SLOT(slotPilot(QString,QString,QString))); connect(serv->soc,SIGNAL(Sign_KK(QString)),this,SLOT(slotKK(QString))); connect(serv->soc,SIGNAL(Sign_SR()),this,SLOT(slotSR())); ............
сокет #include <QtNetwork> #include "clientsocket.h"
QString packetSeparator="|"; unsigned char numb_packet=0; float oldPeriod=0.f;
QFile fp("1.txt"); QByteArray str_send; ClientSocket::ClientSocket(QObject *parent) : QTcpSocket(parent) { flag_write_data=true; nextBlockSize = 0; socket_out=new QTcpSocket(this); head.resize(4); head[0]=0xe0; head[1]=0xe3; head[2]=0xe5; head[3]=0xe7; count_packet_to16KP=0; }
void ClientSocket::readClient() { QTcpSocket *socket_in=(QTcpSocket *)sender(); QByteArray arrData; arrData.resize(0); QDataStream in(socket_in); in.setVersion(QDataStream::Qt_4_2); quint64 NextBlockSize=0; for(;;) { if(!NextBlockSize) { if((unsigned)socket_in->bytesAvailable()<sizeof(quint16)) break; NextBlockSize=socket_in->bytesAvailable(); } if((unsigned)socket_in->bytesAvailable()<=NextBlockSize) break; } arrData= socket_in->read(NextBlockSize); qDebug()<<"arrData"<<arrData; if(arrData.size()>=1) { QString pak=(QString)(arrData); qDebug()<<"arrData"<<arrData; QStringList List=pak.split(packetSeparator); ...... } void ClientSocket::slotSendData_pod(QByteArray arr) { int sz=0; if(socket_out->state()!=QAbstractSocket::ConnectedState) return;
sz=socket_out->write(arr); socket_out->flush();
if(sz!=arr.size()) { qDebug()<<"SV_P: not all information writes to protocol"; } }
void ClientSocket::readClientSord_2d_8d() {
QTcpSocket *socket_in=(QTcpSocket *)sender(); QByteArray arrData; QDataStream in(socket_in); in.setVersion(QDataStream::Qt_4_2); quint64 NextBlockSize=0; NextBlockSize=socket_in->bytesAvailable(); while(NextBlockSize>0) { arrData.append(socket_in->readAll()); NextBlockSize=socket_in->bytesAvailable(); } qDebug()<<"IN_Data_2D_8D"<<arrData; QString pak=(QString)(arrData); QStringList lst=pak.split('*'); if(lst.size()>0) packetFrom2d_8d(lst); }
хочу сделать такую модель, и спросить насколько она будет не ужасной. Будет класс ExchangeConnection()- в него будет передаваться порт и IP адрес, внутри класса будет только подключение нового соединения по сигналу и слот по сигналу readyRead(). Потом несколько классов для каждого клиента, которые будут наследоваться от ExchangeConnection(), в этих классах будут задаваться порт и IP, передаваемые в ExchangeConnection() в конструкторе этих классов. а в самих классах уже будет идти разбор пакета от нужного клиента. например ExchangeConnection::ExchangeConnection(QObject *parent) : QObject(parent) { tcpServer = new QTcpServer(this); connect(tcpServer,SIGNAL(newConnection()),this,SLOT(slot_new_connection())); } void ExchangeConnection::slot_new_connection() { tcpClient = tcpServer->nextPendingConnection(); connect(tcpClient,SIGNAL(disconnected()),this,SLOT(slot_disconnect())); connect(tcpClient,SIGNAL(readyRead()),this,SLOT(on_readyRead())); } void ExchangeConnection::slot_disconnect() { tcpClient->deleteLater(); } void ExchangeConnection::on_readyRead() { if(!tcpClient) return; QByteArray arrData; QDataStream in(tcpClient); in.setVersion(QDataStream::Qt_4_2); quint64 NextBlockSize=0; NextBlockSize=socket_in->bytesAvailable(); // вопрос про правильность организации чтения???????? while(NextBlockSize>0) { arrData.append(tcpClient->readAll()); NextBlockSize=tcpClient>bytesAvailable(); }
}
Подскажите как такая структура сервера?
|