Reading Variable length messages in Qtcp readyRead() - c++

The following code is intended to display an Image sent over network. I sent a header of 16 bytes which I use to calculate the size of image that follows and then read that many bytes and display the image.
I used the concept at this link Tcp packets using QTcpSocket
void socket::readyRead()
{
while(socket->bytesAvailable() > 0) {
quint8 Data[16];
socket->read((char *)&Data,16);
img_size = (((quint8)Data[1]<<8)+ (quint8)Data[0]) * (((quint8)Data[3]<<8)+ (quint8)Data[2]) * 1;
QByteArray buffer = socket->read(img_size);
while(buffer.size() < (img_size))
{
// qDebug() << buffer.size();
socket->waitForReadyRead();
buffer.append(socket->read((img_size)-(buffer.size()) ));
}
unsigned char* imgdatara = (unsigned char*)&buffer.data()[0];
if( !image )
image = new QImage(imgdatara,32,640,QImage::Format_Grayscale8);
else
{
delete image;
image = new QImage(imgdatara,32,640,QImage::Format_Grayscale8);
}
emit msg(image);
}
}
My GUI says "not responding". How should I solve this?
Thanks

This is 100% working code from the book of Max Schlee's "Qt 4.8 Professional programming". This is not a simple question, because on the readyRead() signal you can receive:
1. A complete block
2. Only a part of block
3. Several blocks together
void MyClass::onReceive()
{
QDataStream in(m_pClient);
in.setVersion(QDataStream::Qt_4_6); // Your version. Not necessary.
for(;;)
{
if(m_nextBlockSize == 0)
{
if(m_pClient->bytesAvailable() < sizeof(m_nextBlockSize))
{
break;
}
else
{
in >> m_nextBlockSize;
}
}
if(m_pClient->bytesAvailable() < m_nextBlockSize)
{
break;
}
// Here you have each complete block
processYourBlockHere(); // <=====
m_nextBlockSize = 0;
}
}
Update: useful links for you: Serializing Qt Data Types and QDataStream

Related

LibUSB C++ Format of USB transfer differs

I've been using stack overflow for a long time now, and most of the problems a solution is already available. It is the first time that I actually couldn't figure it out with the web. I hope someone has the answer to the following problem.
Introduction
I am currently working on a project which should be capable of executing a command and act upon its response. This project runs on a debian based system in a c++ console application. In order to be able to perform such commands I tried using the LibUSB library.
The problem
Whenever packets are being sent it does not return a valid response as described in the documentation of the hardware. a default tool is available which triggers a callibration command, I sniffed these packets with Wireshark, but the structure of the OUT interrupt calls of the callibration tool differs from the LibUSB generated one, thus (I think) causing the command to not be executed.
The documentation provides one of the following commands, which should run a diagnostics check that returns 5 bytes of data.
[0] Header: 0x02
[1] Command: 0x4C
[2] Byte to send: 0x02 (N bytes to send, Argument + data size)
[3] Argument: 0x09
[4] Data: 0x00
The response should have the following format:
[0] Header: 0x02
[1] Command: 0x4C
[2] Byte to send: 0x03 (N bytes to send, Argument + data size)
[3] Argument: 0x09
[4] Processing result: D-1
[5] Diagnostic result: D-2
D-1: either 0x01: Normal or 0x00 Error D-2: either 0x00: Normal or not 0x00, linked error code.
Things tried so far
Transfer types:
Synchronous:
Libusb_bulk_transfer
Libusb_control_transfer
libusb_interrupt_transfer
Asynchronous:
Libusb_fill_bulk_transfer
Libusb_fill_control_transfer
Libusb_fill_interrupt_transfer
I tried both async as synchronous implementations for the LibUSB library. The control transfer I tried randomly switching the variables after the most logical ways of filling them had ran out, without success, as to be expected. Since the results found in the packet sniffing clearly indicated INTERRUPT calls being made.
Interfaces: The hardware has two interfaces. Interface 0 which contains OUT 0x02 and IN 0x81, and interface 1 which contains OUT 0x04 and IN 0x83. The sniffing of the USB interrupt call to the device triggered by the tooling provided that interface 1 is being used for the diagnostics command. (Also tried interface 0 with both IN and OUT, couldn't get it to work.
Packet sniffing with Wireshark
Results of the packet sniffing
Request and response generated with the tooling: IMG: Interrupt OUT (I marked the bit where to command is actually provided) IMG: Interrupt IN response This code actually works and returns the, expected, dataset in its data slot. (as described above, the return format is correct, 0x01 and 0x00).
Request and response generated with the LibUSB using code: IMG: Interrupt OUT IMG: Interrupt IN response
Yes, I also tried setting the buffer to a size of 64, the max buffer size for the hardware. Sadly didn't work. As seen clearly, both requests differ a lot, do I use the wrong transfer method? Is it another supported format in which you can send commands?
Used Code snippet:
The code snippet is a bit outdated, I tried re-writing / editing it several times, the last implementations being used from online examples.
#define USB_VENDOR_ID <VENDOR_ID>/* USB vendor ID used by the device
* 0x0483 is STMs ID
*/
#define USB_PRODUCT_ID <PRODUCT_ID> /* USB product ID used by the device */
#define USB_ENDPOINT_IN (LIBUSB_ENDPOINT_IN | 0x83) /* endpoint address */
#define USB_ENDPOINT_OUT (LIBUSB_ENDPOINT_OUT | 0x04) /* endpoint address */
#define USB_TIMEOUT 3000 /* Connection timeout (in ms) */
#define INTERFACE_NO 1
static libusb_context *ctx = NULL;
static libusb_device_handle *handle;
static uint8_t receiveBuf[64];
uint8_t transferBuf[64];
uint16_t counter=0;
int main(int argc, char **argv) {
libusb_device **devs; //pointer to pointer of device, used to retrieve a list of devices
libusb_device_handle *dev_handle; //a device handle
libusb_context *ctx = NULL; //a libusb session
int r; //for return values
ssize_t cnt; //holding number of devices in list
r = libusb_init(&ctx); //initialize the library for the session we just declared
if(r < 0) {
qDebug()<<"Init Error "<<r<<endl; //there was an error
return 1;
}
libusb_set_debug(ctx, 4); //set verbosity level to 3, as suggested in the documentation
cnt = libusb_get_device_list(ctx, &devs); //get the list of devices
if(cnt < 0) {
qDebug()<<"Get Device Error"<<endl; //there was an error
return 1;
}
qDebug()<<cnt<<" Devices in list."<<endl;
dev_handle = libusb_open_device_with_vid_pid(ctx, 0x0AFA, 0x7D3); //these are vendorID and productID I found for my usb device
if(dev_handle == NULL)
qDebug()<<"Cannot open device"<<endl;
else
qDebug()<<"Device Opened"<<endl;
libusb_free_device_list(devs, 1); //free the list, unref the devices in it
unsigned char *data = new unsigned char[5] { 0x02, 0x4C, 0x02, 0x09, 0 }; //data to write
data[0]= 0x02;data[1]= 0x4C;data[2]=0x02;data[3]=0x09; data[4]= 0; //some dummy values
int actual; //used to find out how many bytes were written
if(libusb_kernel_driver_active(dev_handle, INTERFACE_NO) == 1) { //find out if kernel driver is attached
qDebug()<<"Kernel Driver Active"<<endl;
if(libusb_detach_kernel_driver(dev_handle, INTERFACE_NO) == 0) //detach it
qDebug()<<"Kernel Driver Detached!"<<endl;
}
r = libusb_claim_interface(dev_handle, INTERFACE_NO); //claim interface 0 (the first) of device (mine had jsut 1)
if(r < 0) {
qDebug()<<"Cannot Claim Interface"<<endl;
return 1;
}
qDebug()<<"Claimed Interface"<<endl;
for(int i = 0; i != sizeof(data); i++) {
fprintf(stderr, "[%d] - %02x\n", i, data[i]);
}
qDebug()<<"Writing Data..."<<endl;
r = libusb_bulk_transfer(dev_handle, (USB_ENDPOINT_OUT | LIBUSB_ENDPOINT_OUT), data, sizeof(data), &actual, 0); //my device's out endpoint was 2, found with trial- the device had 2 endpoints: 2 and 129
if(r == 0 && actual == sizeof(data)) //we wrote the 4 bytes successfully
qDebug()<<"Writing Successful!"<<endl;
else
qDebug()<<"Write Error"<<endl;
fprintf(stderr, "Error Writing: %s", libusb_strerror(static_cast<libusb_error>(r)));
r = libusb_release_interface(dev_handle, INTERFACE_NO); //release the claimed interface
if(r!=0) {
qDebug()<<"Cannot Release Interface"<<endl;
return 1;
}
qDebug()<<"Released Interface"<<endl;
libusb_close(dev_handle); //close the device we opened
libusb_exit(ctx); //needs to be called to end the
delete[] data; //delete the allocated memory for data
return 0;
}
I hope I that there's someone out there capable and willing to help me out here, because I've been working on this for three days straight and still haven't gotten a logical solution to this problem.
Thanks in advance!
~ Mark
Thanks for your response! I currently found a solution to the problem! It had nothing to do with using both C / C++. Sorry for the code being a bit messy. I wrote it several times so tidiness wasn't my priority, though I will keep it in mind for a possible future post on StackOverflow. Even though solved I added results of sniffing both packets going IN and OUT, hoping it may help others with a possible same issue.
Well, what was the problem?
So, the capture of the tool indicated the last 64 bit being the payload of the request and its data, this is for both OUT and IN. (As to be seen in the images now actually provided) and as I said before, I tried allocating arrays with a size of 64 and setting the first few slots with the data necessary for the operation. As for the other slots, they were filled with the leftovers sitting at those allocated memory addresses.
What did I do to fix it
So, what I did was the following. After initializing an array and assigning it a size of 64 I set all of the allocated slots to 0 with the memset command, so the array would be completely cleared of left-over data. This left me with a clean array in which I could set the variables necessary for the command I wanted to send. (See the following snippet)
// Initialize array of 64 bytes.
uint8_t *data = new uint8_t[64];
memset(data, 0x00, 64);
data[0] = 0x02; data[1] = 0x4C; data[2] = 0x01; data[3] = 0x17;
I tidied up the code a bit to provide better readability, here is the code I used which works! Hopefully others find this information useful.
//*** DEPENDENCIES *************************************************************
// QT
#include <QCoreApplication>
#include <QtCore/QDebug>
// Others
#include <libusb.h>
#include <iostream>
//*** VARIABLES ****************************************************************
#define USB_VENDOR_ID <VENDOR_ID_GOES_HERE>
#define USB_PRODUCT_ID <PRODUCT_ID_GOES_HERE>
#define USB_ENDPOINT_OUT 0x04
#define USB_ENDPOINT_IN 0x83
#define INTERFACE_NO 0x01
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
libusb_device *dev;
libusb_device_handle *dev_handle;
libusb_context *ctx = NULL;
//*** INITIALIZATION *******************************************************
uint r = libusb_init(&ctx);
// Check if initiated succesfully
if ( r < 0 ) { qDebug() << "Init error."; return 1; }
libusb_set_debug(ctx, 4);
dev_handle = libusb_open_device_with_vid_pid(ctx, USB_VENDOR_ID, USB_PRODUCT_ID);
if (dev_handle == NULL) { qDebug() << "Could not open device."; return 1;}
qDebug() << "Device opened succesfully!";
// Check if kernel driver, detach
if(libusb_kernel_driver_active(dev_handle, INTERFACE_NO) == 1) {
qDebug() << "Kernel Driver Active";
if(libusb_detach_kernel_driver(dev_handle, INTERFACE_NO) == 0) {
qDebug() << "Kernel Driver Detached";
}
}
// Claim interface
r = libusb_claim_interface(dev_handle, INTERFACE_NO);
if ( r < 0 ) {
qDebug() << "Could not claim interface.";
return 1;
}
qDebug() << "Interface claimed.";
//*** EXECUTION OF USB TRANSFERS *******************************************
// Prepare command
int actual_written;
// Initialize array of 64 bytes.
uint8_t *data = new uint8_t[64];
memset(data, 0x00, 64);
data[0] = 0x02; data[1] = 0x4C; data[2] = 0x01; data[3] = 0x17;
qDebug() << "================= OUT ==============================";
//*** ATTEMPT TO WRITE COMMAND *********************************************
r = libusb_bulk_transfer(dev_handle,
USB_ENDPOINT_OUT,
data, 64,
&actual_written,
10000);
qDebug() << "OUT status: " << libusb_strerror(static_cast<libusb_error>(r));
if (r == 0 && actual_written == 64) {
qDebug() << "Succesfully written!";
} else {
qDebug() << "||" << r << "||"<< actual_written << "||"
<< "Could not write.";
}
qDebug() << "================== IN ===============================";
//*** ATTEMPT TO READ FEEDBACK *********************************************
// Initialize array of 64 bytes.
uint8_t *feedback = new uint8_t[64];
memset(feedback, 0x00, 64);
int actual_received;
r = libusb_bulk_transfer(
dev_handle,
USB_ENDPOINT_IN,
feedback,
64,
&actual_received,
0);
qDebug() << "IN status: " << libusb_strerror(static_cast<libusb_error>(r));
if(r == 0 && actual_received == 64) {
qDebug("\nRetrieval successful!");
qDebug("\nSent %d bytes with string: %s\n", actual_received, feedback);
} else {
qDebug() << actual_received << "||" <<feedback << "||"
<< "Could not read incoming data. ||";
}
for( int m = 0; m < 64; m++)
{
fprintf(stderr, "[%d] - %02x\n", m, feedback[m]);
}
if (feedback[4] != 0x01) {
qDebug() << "Unsuccesful offset adjustment.";
return -1;
}
// Further code should go here.
//*** FREEING USB **********************************************************
// Releasing interface
r = libusb_release_interface(dev_handle, INTERFACE_NO);
if ( r < 0 ) { qDebug() << "Could not release interface."; return 1; }
qDebug() << "Interface released.";
libusb_close(dev_handle);
libusb_exit(ctx);
delete[] data;
delete[] feedback;
qDebug() << "End of main";
return 0;
}
Thomas and David, thanks a lot!
~ Mark

Streaming audio with ffmpeg library in C++/CLR, avcodec_fill_audio_frame returning -22 error code

Good morning.
So I am working on a project where I need to stream realtime video & audio in C++ over an RTMP connection using FFmpeg's libraries. As far as I understand, the video & audio use two different streams so I am now trying to create an audio only stream.
MY PROBLEM: When calling FFmpeg's avcodec_fill_audio_frame() I receive an error code -22. This tells me nothing seeing as the FFmpeg library's documentation...leaves a great deal to be desired.
My MainForm.h has the following (relevant) members
private: NAudio::CoreAudioApi::MMDevice^ waveDevice_chat;
private: NAudio::Wave::WaveIn^ waveInput_chat;
private: NAudio::Wave::IWavePlayer^ waveOutput_chat;
private: NAudio::Wave::BufferedWaveProvider^ waveProvider_chat;
Fist step is to connect the mic:
System::Void MainForm::btnConnectMic_Click(System::Object^ sender, System::EventArgs^ e) {
msclr::interop::marshal_context context;
waveEnumerator_chat = gcnew NAudio::CoreAudioApi::MMDeviceEnumerator();
System::Collections::Generic::List<NAudio::CoreAudioApi::MMDevice^>^ wavePorts_chat = System::Linq::Enumerable::ToList(waveEnumerator_chat->EnumerateAudioEndPoints(DataFlow::Capture, DeviceState::Active));
if (wavePorts_chat->Count > 0) {
waveDevice_chat = (NAudio::CoreAudioApi::MMDevice^)(wavePorts_chat[0]);
waveDevice_chat->AudioEndpointVolume->Mute = false;
waveInput_chat = gcnew WaveIn();
waveInput_chat->BufferMilliseconds = 50;
waveInput_chat->DeviceNumber = 0;
waveInput_chat->WaveFormat = gcnew NAudio::Wave::WaveFormat(44100, 1);
waveInput_chat->DataAvailable += gcnew System::EventHandler<NAudio::Wave::WaveInEventArgs ^>(this, &MainForm::waveInput_data_available);
waveInput_chat->StartRecording();
waveProvider_chat = gcnew BufferedWaveProvider(gcnew NAudio::Wave::WaveFormat(44100, 1));
waveProvider_chat->DiscardOnBufferOverflow;
}
}
Here is the code for the EventHandler which is called by NAudio when data is available
void MainForm::waveInput_data_available(System::Object^ sender, WaveInEventArgs^ e) {
if (waveProvider_chat->BufferedBytes + e->BytesRecorded > waveProvider_chat->BufferLength)
waveProvider_chat->ClearBuffer();
else
waveProvider_chat->AddSamples(e->Buffer, 0, e->BytesRecorded);
}
And finally, here is the code snippet that is supposed to fill my audio frame (this is runnign in a loop in a background worker)
uint8_t* new_buffer;
int result
AVFrame* a_frame = av_frame_alloc();
AVStream* astrm;
AVCodec* acodec = avcodec_find_encoder(AVCodecID::AV_CODEC_ID_AAC);
/*
*
*
*
*/
in the loop
if (read_buffer->Length <= 0)
continue;
new_buffer = (uint8_t*)av_malloc((size_t)waveProvider_chat->BufferedBytes);
for (int i = 0; i < waveProvider_chat->BufferedBytes; i++)
new_buffer[i] = (uint8_t)read_buffer[i];
AVPacket a_pkt;
av_init_packet(&a_pkt);
a_pkt.data = nullptr;
a_pkt.size = 0;
int got_a_packet = 0;
int a_encode = avcodec_fill_audio_frame(a_frame, astrm->codec->channels, astrm->codec->sample_fmt, new_buffer, read_buffer->Length, 0);
std::cout << "[FILL] encoded response: " << a_encode << std::endl;
When you look at the source code of avcodec_fill_audio_frame, you see
if (buf_size < needed_size)
return AVERROR(EINVAL);
where EINVAL happens to be 22.
So in your case, I would guess, the buffer is not large enough.

QSerialPort - wating for whole data from sender

I'm working with a serial device.
The QSerialPort is in a separate thread.
Thread is created this way:
QThread* serialthread = new QThread;
Serial* serial = new Serial();
serial->moveToThread(serialthread);
When Data is available this signal in my thread worker is emited:
void Serial::process()
{
serialport = new QSerialPort();
connect(this->serialport,SIGNAL(readyRead()),this,SLOT(readyToRead()));
}
void Serial::readyToRead()
{
emit SIG_dataAvailable(this->read());
}
This is the function that reads the data and checks if the data is correct - the second byte on my serial device says how long the rest of the packet is...
QByteArray Serial::read() const
{
QByteArray receivedData;
int length;
receivedData = serialport->readAll();
length = receivedData[1];
if(length != receivedData.length() - 1)
{
qDebug() << "protocol error.";
return NULL;
}
return receivedData;
}
My problem is that the signal QSerialPort::readyRead is emited before the data from the serial device is complete in the buffer. Any idea how to solve this problem?
There is absolutely NO guarantee that you'll get whole data at ONCE. You can solve this problem in some ways.
1) If you have fixed size package you can do something like this:
void foo::onSerialRead()
{
//! Is there whole datagram appears?
if (m_serial->bytesAvailable() < ::package_size) {
//! If not, waiting for other bytes
return;
}
//! Read fixed size datagram.
QByteArray package = m_serial->read(::package_size);
//! And notify about it.
emit packageReady(package);
}
2) If your package size may vary. Then you have to include "hader" in to your package. This header should contain at least "start" byte and data size (Its second byte in your case). And header shuld be fixed size. Then you can do something like this:
void foo::onSerialRead()
{
static QByteArray package;
static bool isHeaderRead = false;
static quint8 startByte = 0;
static quint8 dataSize = 0;
//! Is there whole header appears?
if (m_serial->bytesAvailable() < ::header_size) {
//! If not, waiting for other bytes
return;
}
if (!isHeaderRead) {
//! Read fixed size header.
package.append(m_serial->read(::header_size));
QDataStream out(&package);
out >> startByte;
//! Check is it actually beginning of our package?
if (Q_UNLIKELY(startByte != ::protocol_start_byte)) {
return;
}
out >> dataSize;
isHeaderRead = true;
}
//! Check is there whole package available?
if (Q_LIKELY(dataSize > m_serial->bytesAvailable())) {
//! If not, waiting for other bytes.
return;
}
//! Read rest.
package.append(m_serial->read(dataSize));
//! And notify about it.
emit packageReady(package);
package.clear();
isHeaderRead = false;
}
And there is absolutely no point in putting your QSerial in to different thread.

Why thrift TBinaryProtocol read recv data more complex than just size + content

Thrift version is 0.8. I'm implementing my own thrift transport layer in client with C++, protocol use Binary, my server is use frame transport and binary protocol, and is no problem for sure. And I get "No more data to read" exception in TTransport.h readAll function. I traced the call link, find in TBinaryProtocol.tcc
template <class Transport_>
uint32_t TBinaryProtocolT<Transport_>::readMessageBegin(std::string& name,
TMessageType& messageType,
int32_t& seqid) {
uint32_t result = 0;
int32_t sz;
result += readI32(sz); **//sz should be the whole return buf len without the first 4 bytes?**
if (sz < 0) {
// Check for correct version number
int32_t version = sz & VERSION_MASK;
if (version != VERSION_1) {
throw TProtocolException(TProtocolException::BAD_VERSION, "Bad version identifier");
}
messageType = (TMessageType)(sz & 0x000000ff);
result += readString(name);
result += readI32(seqid);
} else {
if (this->strict_read_) {
throw TProtocolException(TProtocolException::BAD_VERSION, "No version identifier... old protocol client in strict mode?");
} else {
// Handle pre-versioned input
int8_t type;
result += readStringBody(name, sz);
result += readByte(type); **//No more data to read in buf, so exception here**
messageType = (TMessageType)type;
result += readI32(seqid);
}
}
return result;
}
So my quesiton is: in frame transport, the data struct, should ONLY be size + content(result, seqid, function name....), that's exactly what my server pack. Then my client read the first 4 bytes lenth, and use it to fetch the whole content, is there any other left to read now?
Here is my client code, I believe quite simple.the most import part I have emphasize that.
class CthriftCli
{
......
TMemoryBuffer write_buf_;
TMemoryBuffer read_buf_;
enum CthriftConn::State state_;
uint32_t frameSize_;
};
void CthriftCli::OnConn4SgAgent(const TcpConnectionPtr& conn)
{
if(conn->connected() ){
conn->setTcpNoDelay(true);
wp_tcp_conn_ = boost::weak_ptr<muduo::net::TcpConnection>(conn);
if(unlikely(!(sp_countdown_latch_4_conn_.get()))) {
return 0;
}
sp_countdown_latch_4_conn_->countDown();
}
}
void CthriftCli::OnMsg4SgAgent(const muduo::net::TcpConnectionPtr& conn,
muduo::net::Buffer* buffer,
muduo::Timestamp receiveTime)
{
bool more = true;
while (more)
{
if (state_ == CthriftConn::kExpectFrameSize)
{
if (buffer->readableBytes() >= 4)
{
frameSize_ = static_cast<uint32_t>(buffer->peekInt32());
state_ = CthriftConn::kExpectFrame;
}
else
{
more = false;
}
}
else if (state_ == CthriftConn::kExpectFrame)
{
if (buffer->readableBytes() >= frameSize_)
{
uint8_t* buf = reinterpret_cast<uint8_t*>((const_cast<char*>(buffer->peek())));
read_buf_.resetBuffer(buf, sizeof(int32_t) + frameSize_, TMemoryBuffer::COPY); **// all the return buf, include first size bytes**
if(unlikely(!(sp_countdown_latch_.get()))){
return;
}
sp_countdown_latch_->countDown();
buffer->retrieve(sizeof(int32_t) + frameSize_);
state_ = CthriftConn::kExpectFrameSize;
}
else
{
more = false;
}
}
}
}
uint32_t CthriftCli::read(uint8_t* buf, uint32_t len) {
if (read_buf_.available_read() == 0) {
if(unlikely(!(sp_countdown_latch_.get()))){
return 0;
}
sp_countdown_latch_->wait();
}
return read_buf_.read(buf, len);
}
void CthriftCli::readEnd(void) {
read_buf_.resetBuffer();
}
void CthriftCli::write(const uint8_t* buf, uint32_t len) {
return write_buf_.write(buf, len);
}
uint32_t CthriftCli::writeEnd(void)
{
uint8_t* buf;
uint32_t size;
write_buf_.getBuffer(&buf, &size);
if(unlikely(!(sp_countdown_latch_4_conn_.get()))) {
return 0;
}
sp_countdown_latch_4_conn_->wait();
TcpConnectionPtr sp_tcp_conn(wp_tcp_conn_.lock());
if (sp_tcp_conn && sp_tcp_conn->connected()) {
muduo::net::Buffer send_buf;
send_buf.appendInt32(size);
send_buf.append(buf, size);
sp_tcp_conn->send(&send_buf);
write_buf_.resetBuffer(true);
} else {
#ifdef MUDUO_LOG
MUDUO_LOG_ERROR << "conn error, NOT send";
#endif
}
return size;
}
So please give me some hints about this?
You seem to have mixed concepts of 'transport' and 'protocol'.
Binary Protocol describes how data should be encoded (protocol layer).
Framed Transport describes how encoded data should be delivered (forwarded by message length) - transport layer.
Important part - Binary Protocol is not (and should not) be aware of any transport issues. So if you add frame size while encoding on transport level, you should also interpret incoming size before passing read bytes to Binary Protocol for decoding. You can (for example) use it to read all required bytes at once etc.
After quick looking trough you code: try reading 4 bytes of frame size instead of peeking it. Those bytes should not be visible outside transport layer.

Network send problem

I try to send data over the network, but the server I've programmed doesen't get the data.
This code worked befor:
void MainWindow::send()
{
QByteArray qbarr;
QDataStream qdstrm(&qbarr, QIODevice::WriteOnly);
int iCount = qlist->count();
QProgressDialog qprogrsdSend(QString("Sending..."), QString("Cancel"), 0, iCount, this);
qdstrm.setVersion(QDataStream::Qt_4_6);
qprogrsdSend.setWindowModality(Qt::WindowModal);
for(int i = 0; i < iCount; i++)
{
if(qprogrsdSend.wasCanceled())
break;
qdstrm << (quint16)0;
qdstrm << (*qlist)[i].data();
qdstrm.device()->seek(0);
qdstrm << (quint16)(qbarr.size() - sizeof(quint16));
qprogrsdSend.setValue(i);
qtcpsoClient->write(qbarr);
qtcpsoClient->flush();
qtcpsoClient->waitForBytesWritten();
qbarr.clear();
}
qlblStatus2->setText("File is send.");
}
But it Takes to many time to send each elemt from qlist. Now I tried to modify the methode, so that first all elements from qlist has been saved in qbarr. And than I send the File. This is the code that doesnt work:
void MainWindow::send()
{
QByteArray qbarr;
QDataStream qdstrm(&qbarr, QIODevice::WriteOnly);
int iCount = qlist->count();
QProgressDialog qprogrsdSend(QString("Sending..."), QString("Cancel"), 0, iCount, this);
qdstrm.setVersion(QDataStream::Qt_4_6);
qprogrsdSend.setWindowModality(Qt::WindowModal);
qdstrm << (quint16)0;
for(int i = 0; i < iCount; i++)
{
if(qprogrsdSend.wasCanceled())
break;
qdstrm << (*qlist)[i].data();
qprogrsdSend.setValue(i);
}
qdstrm.device()->seek(0);
qdstrm << (quint16)(qbarr.size() - sizeof(quint16));
qtcpsoClient->write(qbarr);
qtcpsoClient->flush();
qtcpsoClient->waitForBytesWritten();
qbarr.clear();
qlblStatus2->setText("File is send.");
}
And here is the methode I use to read the data:
void QServerThread::onReadyRead(void)
{
if(read == false)
{
read = true;
emit reading(true);
}
while(!qtcpsoClient->atEnd())
{
QDataStream qdstrmIn(qtcpsoClient);
QDataStream qdstrmOut(qfile);
QByteArray qbarrData;
quint16 qui16BlockSize = 0;
int iVersion = qdstrmIn.version();
qdstrmIn.setVersion(iVersion);
qdstrmOut.setVersion(iVersion);
if(qtcpsoClient->bytesAvailable() < (int)sizeof(quint16))
break;
qdstrmIn >> qui16BlockSize;
if(qtcpsoClient->bytesAvailable() < qui16BlockSize)
break;
qdstrmIn >> qbarrData;
qdstrmOut << qbarrData.data();
qfile->flush();
}
read = false;
emit reading(false);
}
I hope somebody can help me.
Thanks
Paul
Couldn't the problem be in your server? I suppose you connect your onReadyRead to readyRead singal of the socket. That signal is emitted once per a chunk of data received. So if you send all your data at once, it is possible the signal gets emitted only once. I suppose qtcpsoClient is the socket. Now, I can see this happening:
You ask bytesAvailable() < somesize before that much data arrived yet. In that case you read a size, but break out right after that and on next read you already lost your size information and read garbage.
This might not be a problem before as you send multiple short messages, and every message managed to fully arrive before you asked for the data size. The bug was still there though.
On a sidenote. In your original client code - why did you flush() and waitForBytesWritten() after every write? This may be the reason why it was so slow.
[edit: corrected based on Sergey Tachenov's comment]