sigset_t unix using sigprocmask() - unix

I am trying to print sigset using printf. In this program, I have used sigprocmask to block SIGHUP and SIGTERM. After initializing set and oset to empty sets, they are giving some random hex strings as output. How should I solve this problem??
Also, I have a doubt regarding the expected output of this program if I press Ctrl-C after execution. Does the catcher function also inherit the present signal set and should print the same as set??
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<bits/sigset.h>
void catcher(int sig){
sigset_t set;
sigprocmask(SIG_SETMASK, NULL, &set);
printf("%x inside function\n",set);
}
int main()
{
sigset_t set,oset;
signal(SIGINT,catcher);
sigemptyset(&set);
sigemptyset(&oset);
printf("%x\n",oset);
printf("%x\n",set);
sigaddset(&set,SIGHUP);
sigaddset(&set,SIGTERM);
sigprocmask(SIG_SETMASK,NULL,&oset);
printf("%x\n",oset);
printf("%x\n",set);
sigprocmask(SIG_BLOCK,&set,&oset);
pause();
sigprocmask(SIG_SETMASK,&oset,&set);
printf("%x\n",set);
}

Manual says :
The sigprocmask() function examines and/or changes the current signal
mask (those signals
that are blocked from delivery). Signals are blocked if they are members of the current
signal mask set.
This mask is for the whole process, and is used to block some signals for delivery. There is no inheritence here. There is a mask that describe at each execution point the set of blocked signals.
What you should know is that by default when a signal is catched, it is automatically added to the set of blocked signals (to prevent reentrance to the catching routine), and removed at the end of it.
An execution of your code on my machine gives :
0 // empty set
0 // empty oset
0 // empty oset
4001 // set = { HUP, TERM }
^C4003 // in catcher, signal is automatically blocked set={HUP, INT, TERM}
4001 // after catcher back in old state set={HUP,TERM}
That means that bit 0 and 14 are used for SIGHUP and SIGTERM, and bit 1 for SIGINT, which is exactly what I found in my system header file :
#define SIGHUP 1 /* hangup */
#define SIGINT 2 /* interrupt */
#define SIGTERM 15 /* software termination signal from kill */

Related

Task watchdog got triggered - The tasks did not reset the watchdog in time

I am trying to write a little asynchronous WebServer. Let me briefly describe the scenario:
My ESP32 as well is a router. So if I connect with my mobile phone into the WiFi the ESP32 is spreading and call the ip address and a special path with a browser, a WebSite is delivered. Here a button is displayed. Until this point it works pretty well. Now, if I click on that button, a HTTPS Web Request (method: GET) is sent to a special machine. This machine answers and returns a JSON. This could last a couple of seconds. After extracting a value out of the JSON string, this value shall be displayed.
To achieve this, I am using the following libraries:
ESPAsyncWebServer
AsyncTCP
WiFiClientSecure
WiFi.h
HTTPClient.h
I know (by another sketch) that the last three ones work without any problems.
Unfortunately, when I click the button, the following output appears onto my serial monitor:
Starting connection to server...
[HTTPS] begin... Path: https://192.168.4.101/api/unlock/generate_pin
[HTTPS] GET...
E (137906) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
E (137906) task_wdt: - async_tcp (CPU 0/1)
E (137906) task_wdt: Tasks currently running:
E (137906) task_wdt: CPU 0: IDLE0
E (137906) task_wdt: CPU 1: loopTask
E (137906) task_wdt: Aborting.
abort() was called at PC 0x400e08af on core 0
Backtrace: 0x4008cc18:0x3ffbe170 0x4008ce49:0x3ffbe190 0x400e08af:0x3ffbe1b0 0x40084f21:0x3ffbe1d0 0x4016581b:0x3ffbc120 0x400e1c66:0x3ffbc140 0x4008ab21:0x3ffbc160 0x4008932d:0x3ffbc180
Rebooting...
ets Jun 8 2016 00:22:57
rst:0xc (SW_CPU_RESET),boot:0x17 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:1044
load:0x40078000,len:8896
load:0x40080400,len:5816
entry 0x400806ac
Serial initial done
Anyone any idea what is happening and how to fix this? So that the GET request is sent correctly / the answer is received?
I am using a Heltec WiFi Kit 32.
Would be very happy about every answer, thanks in advance.
Best regards
P.S.: Please let me finally add my code:
#include <heltec.h>
#include "WiFi.h"
#include "ESPAsyncWebServer.h"
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
const char* ssid = "MyWiFiSSID";
const char* password = "MyWiFiPW";
AsyncWebServer server(80);
void setup() {
Heltec.begin(true, false, true, true, 470E6);
WiFi.softAP(ssid, password);
IPAddress IP = WiFi.softAPIP();
Serial.print("AccessPoint IP address: ");
Serial.println(IP);
server.on("/hello", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(200, "text/html", "<!DOCTYPE html><html><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" charset=\"UTF-8\"><link rel=\"icon\" href=\"data:,\"><style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}.button { background-color: #4CAF50; border: none; color: white; padding: 16px 40px; text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}</style></head><body><h1>Welcome to the Landing Page of the Web Server</h1><p><button class=\"button\">Click Me</button></p></body></html>");
});
server.on("/get_unlock_pin", HTTP_GET, [](AsyncWebServerRequest *request){
String firstpartofrawhtmlcode = "<!DOCTYPE html><html><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" charset=\"UTF-8\"><link rel=\"icon\" href=\"data:,\"><style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}.button { background-color: #4CAF50; border: none; color: white; padding: 16px 40px; text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}</style></head><body><h2>Received Pin: </h2><h2 style=\"color: #FF0000\">";
String receivedPin = getPin("192.168.4.101");
String secondpartofrawhtmlcode = "</h2></body></html>";
String fullrawhtmlcode;
firstpartofrawhtmlcode = firstpartofrawhtmlcode.concat(receivedPin);
fullrawhtmlcode = firstpartofrawhtmlcode.concat(secondpartofrawhtmlcode);
request->send(200, "text/html", fullrawhtmlcode);
});
server.begin();
}
void loop() {
}
String getPin(String ip){
Serial.println("\nStarting connection to server...");
WiFiClientSecure *wificlient = new WiFiClientSecure;
HTTPClient https;
https.setAuthorization("MyUserName", "MyPassword");
String path = "https://" + ip + "/api/unlock/generate_pin";
Serial.print("[HTTPS] begin... Path: " + path + "\n");
if (https.begin(*wificlient, path)) {
Serial.print("[HTTPS] GET...\n");
int httpCode = https.GET();
if (httpCode > 0) {
Serial.printf("[HTTPS] GET... code: %d\n", httpCode);
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
String payload = https.getString();
Serial.println(payload);
//Extract Pin from JSON
String tmp = payload.substring(payload.indexOf(':'), payload.indexOf('}'));
String tmp2 = tmp.substring(tmp.indexOf('"')+1,tmp.lastIndexOf('"'));
if(tmp2.substring(0,1) == "-"){
return "-";
}else{
return tmp2;
}
}
} else {
Serial.printf("[HTTPS] GET... failed, error: %s\n", https.errorToString(httpCode).c_str());
}
https.end();
} else {
Serial.printf("[HTTPS] Unable to connect\n");
}
}
The way the question is formulated, I assume you don't know what a watchdog is nor why it is triggering. Hence:
Arduino for the ESP is built on the ESP-IDF, which in turn is built around FreeRTOS. FreeRTOS creates one IDLE task per core. It also sets watchdog timers on these Tasks. This means if these tasks get starved of execution time, then after a timeout period, the watchdog gets triggered and resets the chip. The IDLE tasks do some important FreeRTOS "household" work in the background, so you have to give them time. They also have the lowest possible priority. So if any task is running with a higher priority (such as your callbacks), these will always run first while the IDLE tasks will have to wait. Thus all higher priority tasks must be short enough to avoid triggering the watchdog. If this isn't feasible, you have to insert pauses in sufficient intervals, for example by calling vTaskDelay(...) or by performing some IO function that blocks long enough. In either case, the current task will go to sleep, and FreeRTOS will kick in another task, and if there are no other higher priority tasks waiting, it will finally allow the IDLE tasks to execute. What all this means is that usually no code you write should ever preoccupy the CPU for 100% of the time for any period longer than the watchdog timeout period. Moreover, you can't turn this off in Arduino. It is only configurable in ESP-IDF, meaning you have to code within that framework or recompile Arduino on your own. Moreover it is a bad idea to turn it off anyways.
You must use a value of atleast 1 in the call to vTaskDelay(...). Due to integer arithmetic and the various expressions passed as a parameter, it could end up amounting to 0, ie no delay at all.
If you really need to run a task without pause, you'll have to create the task on your own and give it a priority of tskIDLE_PRIORITY, such as:
xTaskCreate(someFunction, "HumanReadableNameofTask", 4096, NULL, tskIDLE_PRIORITY, NULL);
where your function has the following signature:
void someFunction(void* arg) {...}
You can do this in Arduino too. So, either keep the code that runs in the callbacks to a minimum, or shift the heavy lifting to a separate task and have the callbacks simply forward any relevant information to that task (such as using volatile variables and semaphores, ie the usual methods of synchronization in parallel processing. that's beyond the scope of this answer).
Note I'm not sure if calling delay(...) in Arduino has the same effect as vTaskDelay, but it probably does (with some subtleties). Also, there is an official way for a task to "yield" to another lower task, I'm not sure about the details though.
Important: Using delays (such as vTaskDelay(...)) within callbacks, especially timer callbacks, is a bad idea, since they block other callbacks (within the same task) from executing. Hence the best option is to pass information to a separate task running at idle priority (ie priority 0). The reason why a task running at 0 priority doesn't trigger the watchdog, is because the IDLE tasks also have that priority, and FreeRTOS round-robins all those tasks at the same priority, ie gives them interleaving slices of time to execute in "parallel". But when you have two tasks with differing priorites, the higher one ALWAYS executes until it is done or stalls/sleeps, only then will the lower priority run, either until it itself finishes, or until the higher priority task wakes up again and demands execution time.
Update:
Keeping the Arduino loop() empty, will definately trigger the wdt, because it isn't really "empty", because the loop() function is internally wrapped in an infinite loop, so the CPU consumption would actually shoot to 100% without doing anything useful.
That is what is triggering the watchdog. The GET process seems to be happening asynchronously, so it is not blocking the loop() from executing.
ESPAsyncWebServer callbacks prevent the watchdog timer from being reset while they run. This means they are not meant for doing any actual processing. Register the request and defer processing to the main loop (or some other thread). Have a look at this question for details.

Cannot wakeup MIFARE 1k card

What we need to do
We need to detect both the presence and absence of an RFID chip. We intend to do this by continually checking for a card. If one is there, we read it and confirm which it is. If there isn't one there after there previously was, then that is noted. It doesn't have to be super fast.
What we're using
Arduino UNO + RC522 FRID reader, with MIFARE 1k cards. Using the library at: https://github.com/miguelbalboa/rfid
The problems
Cannot read multiple cards
Contrary to what seems to be stated in example code found elsewhere, I am never able to read more than one card without resetting the RC522. As soon as it reads a card, and it's HALTed, no more cards can be read until the processor is reset. Other examples suggest this shouldn't be the case and a new card should immediately be readable on the next loop. Using:
void loop() {
// Look for new cards
if ( ! mfrc522.PICC_IsNewCardPresent()) {
return;
}
// Select one of the cards
if ( ! mfrc522.PICC_ReadCardSerial()) {
return;
}
// Dump debug info about the card; PICC_HaltA() is automatically called
mfrc522.PICC_DumpDetailsToSerial(&(mfrc522.uid));
mfrc522.PICC_HaltA();
}
Successfully gives:
Card UID: B6 35 9F 46
Card SAK: 08
PICC type: MIFARE 1KB
But I cannot read any more cards until I reset the RF522 (either using software or just restarting the device).
Cannot authenticate card
void loop() {
// Look for new cards
if ( ! mfrc522.PICC_IsNewCardPresent()) {
return;
}
// Select one of the cards
if ( ! mfrc522.PICC_ReadCardSerial()) {
return;
}
// Dump debug info about the card; PICC_HaltA() is automatically called
mfrc522.PICC_DumpToSerial(&(mfrc522.uid));
}
Gives the following output
Card UID: B6 35 9F 46
Card SAK: 08
PICC type: MIFARE 1KB
Sector Block 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 AccessBits
15 63 PCD_Authenticate() failed: Timeout in communication.
14 59 PCD_Authenticate() failed: Timeout in communication.
13 55 PCD_Authenticate() failed: Timeout in communication.
...
Cannot read card when starting
I cannot read a card if it's present when starting the RC522, the communication times out. It does, however, take longer when a card is present. Using the following code:
// this is triggered by a serial command input
void resetAndRead() {
Serial.println("Init and check for any card");
mfrc522[0].PCD_Reset();
Serial.print("Reset complete (antenna off): ");
Serial.println(millis() - time_last_action);
delay(2000);
time_last_action = millis();
initialise();
Serial.print("Init complete: ");
Serial.println(millis() - time_last_action);
delay(10);
time_last_action = millis();
PICC_IsAnyCardPresent();
Serial.print("Check for card complete: ");
Serial.println(millis() - time_last_action);
}
void initialise() {
mfrc522[0].PCD_Init(ssPins[0], RST_PIN); // Init each MFRC522 card
mfrc522[0].PCD_DumpVersionToSerial();
}
bool PICC_IsAnyCardPresent() {
byte bufferATQA[2];
byte bufferSize = sizeof(bufferATQA);
MFRC522::StatusCode result = mfrc522[0].PICC_WakeupA(bufferATQA, &bufferSize);
Serial.print("Status Code: ");
Serial.print(mfrc522[0].GetStatusCodeName(result));
Serial.println();
return (result == MFRC522::STATUS_OK || result == MFRC522::STATUS_COLLISION);
}
I get the following output with no card present
Init and check for any card
Reset complete (antenna off): 50
Firmware Version: 0x92 = v2.0
Init complete: 51
Status Code: Timeout in communication.
Check for card complete: 41
And with a card present
Init and check for any card
Reset complete (antenna off): 52
Firmware Version: 0x92 = v2.0
Init complete: 53
Status Code: Timeout in communication.
Check for card complete: 294
The Questions
Particularly given the first issue - does this sound like a hardware issue? No one else seems to have problems reading multiple cards without requiring a reset.
Should it be possible to solve the third issue? i.e. should an RFID reader be able to communicate with an RFID chip when the antenna is first switched on.
You forgot exit an authentificated PICC. Try to use the MFRC522::PCD_StopCrypto1() function.
Used to exit the PCD from its authenticated state. Remember to call
this function after communicating with an authenticated PICC -
otherwise no new communications can start.
You can use the method after dumping the information:
mfrc522.PICC_DumpToSerial(&(mfrc522.uid));
or directly before checking a new card (multiple calls are no problem):
mfrc522.PICC_IsNewCardPresent()
Bit late,
but perhaps someone will find these usefull..
https://github.com/miguelbalboa/rfid/issues/269#issuecomment-292783655
https://github.com/miguelbalboa/rfid/issues/269#issuecomment-472342583
// Function requires an integer assigned to specific reader
// If not multi-reader, just drop the [...]
void mfrc522_fast_Reset(int reader) {
digitalWrite(RST_PIN, HIGH);
mfrc522[reader].PCD_Reset();
mfrc522[reader].PCD_WriteRegister(mfrc522[reader].TModeReg, 0x80); // TAuto=1;
// Timer starts automatically at the end of the transmission
// in all communication modes at all speeds
mfrc522[reader].PCD_WriteRegister(mfrc522[reader].TPrescalerReg, 0x43); // 10μs.
// mfrc522.PCD_WriteRegister(mfrc522.TPrescalerReg, 0x20); // test
mfrc522[reader].PCD_WriteRegister(mfrc522[reader].TReloadRegH, 0x00);
// Reload timer with 0x064 = 30, ie 0.3ms before timeout.
mfrc522[reader].PCD_WriteRegister(mfrc522[reader].TReloadRegL, 0x1E);
//mfrc522.PCD_WriteRegister(mfrc522.TReloadRegL, 0x1E);
mfrc522[reader].PCD_WriteRegister(mfrc522[reader].TxASKReg, 0x40);
// Default 0x00. Force a 100 % ASK modulation independent
//of the ModGsPReg register setting
mfrc522[reader].PCD_WriteRegister(mfrc522[reader].ModeReg, 0x3D);
// Default 0x3F. Set the preset value for the CRC coprocessor
// for the CalcCRC command to 0x6363 (ISO 14443-3 part 6.2.4)
mfrc522[reader].PCD_AntennaOn(); // Enable the antenna driver pins TX1 and TX2
// (they were disabled by the reset)
}

How to get QDBusConnection::connect() failure reason

I'm trying to connect to a D-Bus signal this way:
bool result = QDBusConnection::systemBus().connect(
"foo.bar", // service
"/foo/bar", // path
"foo.bar", // interface
"SignalSomething",
this,
SLOT(SignalSomethingSlot()));
if( !result )
{
// Why!?
}
QDBusConnection::connect() returns a boolean, how do I get extended error information? If a check QDBusConnection::lastError() it returns no useful information (as QDBusError::isValid() is false).
I had the same issue and it turned out that the slot I connected to had the wrong parameter types. They must match according to Qt's documentation and it looks like connect() verifies that, despite not explicitly mentioned.
Warning: The signal will only be delivered to the slot if the parameters match.
I suggest d-feet to list signals and check their parameter types. dbus-monitor does list signals, paths and such too, but not always the exact type of parameters.
One important observation though: I fixed the issue in my particular case by using different slot parameters than the actual signal has!
I wanted to connect to a com.ubuntu.Upstart0_6 signal mentioned here to detect when the screen in Ubuntu is locked/unlocked. dbusmonitor prints the following and d-feet shows parameters (String, Array of [String])
// dbusmonitor output
signal time=1529077633.579984 sender=:1.0 -> destination=(null destination) serial=809 path=/com/ubuntu/Upstart; interface=com.ubuntu.Upstart0_6; member=EventEmitted
string "desktop-unlock"
array [
]
Hence the signal should be of type
void screenLockChangedUbuntu(QString event, QVector<QString> args) // connect() -> false
This however made connect() return false. The solution was to remove the array parameter from the slot:
void screenLockChangedUbuntu(QString event) // works
I am aware that the array parameter was always empty, but I cannot explain why it only worked when removing it.
You could try these tricks:
1) Set QDBUS_DEBUG environment variable before running your application.
export QDBUS_DEBUG=1
2) Start dbus-monitor to see what's happening on the bus. You may need to set a global policy to be able to eavesdrop system bus depending on your distro.
Update:
Are you sure connecting to the system bus succeeded? If it fails you should probably check system.conf policy and possibly create own conf in system.d. This post might be helpful.
You could first connect to the system bus with QDBusConnection::connectToBus and check if it succeeded with QDBusConnection::isConnected. Only after that you try to connect to the signal and check if that succeeded.
QDBusConnection bus = QDBusConnection::connectToBus(QDBusConnection::systemBus, myConnectionName);
if (bus.isConnected())
{
if(!bus.connect( ... ))
{
// Connecting to signal failed
}
}
else
{
// Connecting to system bus failed
}

Qt Signal and slots not working as expected

When the socket times out while waiting for a read it occasionally fails. But when it does fail, it continuously fails, and the log message in slotDisconnected never gets reported despite mpSocket's disconnected signal being connected to slotDisconnect(). It's as if the return statement in slotConnected isn't being hit and it's going round in a continous loop.
void Worker::slotDisconnected()
{
// Attempt to reconnect
log("Disconnected from Server. Attempting to reconnect...");
// fires mpSocket's connect signal (which is connected to slotConnected)
connectToServer();
}
void Worker::slotConnected()
{
// Loop forever while connected and receiving messages correctly
while(1)
{
if(mpSocket->bytesAvailable())
{
// A message is ready to read
}
else if(!mpSocket->waitForReadyRead(mSocketTimeOut))
{
// waitForReadyRead returned false - instead of continuing and trying again, we must disconnect as sometimes
// (for some unknown reason) it gets stuck in an infinite loop without disconnecting itself as it should
log("Socket timed out while waiting for next message.\nError String: " + mpSocket->errorString());
msleep(3000);
mpSocket->disconnect();
return;
}
}
}
The signals/slots are connected like so:
connect(mpSocket, &QAbstractSocket::disconnected, this, &TRNGrabberWorker::slotDisconnected);
connect(mpSocket, &QAbstractSocket::connected, this, &TRNGrabberWorker::slotConnected);
Anyone have any idea's what's going on? Would be much appreciated
To disconnect from server use mpSocket->disconnectFromHost(); instead of mpSocket->disconnect();.
Actually mpSocket->disconnect(); disconnects all signals/slots of object mpSocket.

QT socket does no read all data

I want to read the data through socket in Qt. I am using QBytearray to store the data. Actually server sends 4095 bytes in a single stretch, but in the QT client side I am receiving in different chunks because of my application design.
void Dialog::on_pushButton_clicked()
{
socket=new QTcpSocket(this);
socket->connectToHost("172.17.0.1",5000);
if(socket->waitForConnected(-1))
qDebug()<<"Connected";
Read_data();
}
void Dialog::Read_data()
{
QString filename(QString("%1/%2.bin").arg(path,device));
qDebug()<<"filename"<<filename;
QFile fileobj(filename);
int cmd,file_size,percentage_completed;
if(!fileobj.open(QFile::WriteOnly | QFile::Text))
{
qDebug()<<"Cannot open file for writting";
return;
}
QTextStream out(&fileobj);
while(1)
{
socket->waitForReadyRead(-1);
byteArray=socket->read(4);
qDebug()<<"size of bytearray"<<byteArray.size();
length=0xffff & ((byteArray[3]<<8)|(0x00ff & byteArray[2]));
int rem;
byteArray=socket->read(length);
while(byteArray.size()!=length)
{
rem=length-byteArray.size();
byteArray.append( socket->read(rem));
}
fileobj.write(byteArray);
fileobj.flush();
byteArray.clear();
}
}
server code:
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<sys/ioctl.h>
#include<mtd/mtd-user.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
#include<math.h>
#include <netinet/tcp.h>
static int msb,lsb,size,listenfd = 0, connfd = 0,len;
main()
{
struct sockaddr_in serv_addr;
serverlen=sizeof(serv_addr);
listenfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&serv_addr, '0', sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(5000);
if(bind(listenfd,(struct sockaddr*)&serv_addr, sizeof(serv_addr))<0)
{
perror("\n Error in binding");
exit(1);
}
size=100000;
listen(listenfd, 1);
fd=fopen(new.bin,"r");
len=4089;
while(1)
{
buff[0]=25;
buff[1]=2;
buff[2]=60;
buff[3]=47;
n=fread(buff+4,1,length, fd);
buff[len+4]=5;
buff[len+5]='\n';
if(n>0)
sent_bytes=send(connfd,buff,n+6,0);
size =size-len;
if(size==0)
break;
}
}
If I execute the code in localhost(127.0.0.1) I can receive the data fully. The problem arises only when I connect to different host IP. Kindly help me in this regard
EDIT 1:
The problem is when bytesAvailable() returns the maximum bytes I am waiting for waitForReadyRead() times out. It works fine if the bytesAvailable() is less than as expected. Does bytesAvailable() allocate any buffer annoyed by this behaviour.
while(1)
{
while(socket->bytesAvailable()<4)
{
if (!socket->waitForReadyRead())
{
qDebug() << "waitForReadyRead() timed out";
return;
}
}
byteArray=socket->read(4);
length=0xffff & ((byteArray[3]<<8)|(0x00ff & byteArray[2]));
int rem_bytes=length+2;
qDebug()<<"bytes available"<<socket->bytesAvailable();
while(socket->bytesAvailable()<=rem_bytes)
{
qDebug()<<"reading";
if (!socket->waitForReadyRead(10000))//times out here if bytesAvailable() == rem_bytes but executes well in other cases
{
qDebug() << "waitForReadyRead() timed out";
return;
}
qDebug()<<"ready";
byteArray.append(socket->read(rem_bytes));
qDebug()<<"size of bytearray"<<byteArray.size();
if(byteArray.size()==length+2)
{
for(int j=0;j<length;j++)
newarray.append(byteArray[j]);
fileobj.write(newarray);
fileobj.flush();
newarray.clear();
byteArray.clear();
break;
}
else
{
rem_bytes -=byteArray.size();
}
}
Send();
}
I have tried by sending different data sizes cannot figure it out why?. Please provide me a solution pointing where I have gone wrong
Your problem stems from your misunderstanding of how TCP works.
When data is transmitted from a sender, it is broken into packets and then each packet is transmitted one by one until all the data has finished sending. If packets go missing, they are re-transmitted until either they reach their destination, or a timeout is reached.
As an added complication, each packet might follow various routes before arriving at the destination. The receiver has the task of acknowledging to the sender that packets have been received and then making sure that the packets are joined back together in the correct order.
For this reason, the longer the network route, the greater the chance of getting a delay in getting the data re-assembled. This is what you've been experiencing with your localhost versus networked-computer tests.
The IP stack on your computer does not wait for the complete data to arrive before passing it to your application but it will pause if it's missing a packet in sequence.
e.g. If you have 10 packets and packet 4 arrives last, the IP stack will pass the data to your application in two sets: 1-2-3, [[wait for 4 to arrive]], 4-5-6-7-8-9-10.
For this reason, when waitForReadyRead() returns true, you cannot expect that all your data has arrived, you must always check how many bytes have been actually received.
There are two places in your code where you wait for data. The first thing you wait for is a four-byte number to tell you how much data has been sent. Even though it's highly likely that you will have received all four bytes, it's good practice to check.
while(socket.bytesAvailable() < 4){
if (!socket.waitForReadyRead()) { // timeout after 30 second, by default
qDebug() << "waitForReadyRead() timed out";
return;
}
}
byteArray=socket->read(4);
qDebug()<<"size of bytearray"<<byteArray.size();
length=0xffff & ((byteArray[3]<<8)|(0x00ff & byteArray[2]));
The next thing you need to do is keep cycling through a wait-read-wait-read loop until all your data has arrived, each time keeping track of how many bytes you still expect to receive.
int bytesRemaining = length;
while(socket->bytesAvailable() < bytesRemaining){
if (!socket->waitForReadyRead()){
qDebug() "waitForReadyRead() timed out";
return;
}
// calling read() with the bytesRemaining argument will not guarantee
// that you will receive all the data. It only means that you will
// receive AT MOST bytesRemaining bytes.
byteArray = socket->read(bytesRemaining);
bytesRemaining -= byteArray.size();
fileobj.write(byteArray);
fileobj.flush();
}
All this said, you should not use the blocking API in your main thread or your GUI could freeze up. I suggest either using the asynchronous API, or create a worker thread to handle the downloading (and use the blocking API in the worker thread).
To see examples of how to use the two different APIs, looking in the documentation for the Fortune Client Example and the Blocking Fortune Client Example.
EDIT:
My apologies, there's a bug in the code above that doesn't take an number of possibilities into account, most importantly, if all data has already been received, and the end game once all data has finally arrived.
The following one-line change should clear that up:
Change
while(socket->bytesAvailable() < bytesRemaining){
To
while (bytesRemaining > 0) {
So you are saying that waitForReadyRead() returns false regardless of the time given once your the buffer has all 3000 expected bytes. What other behavior would you want? Perhaps you need rethink the trigger logic here. Many TCP/IP app protocols have some sort of frame start detection logic they combine with the required message size to then trigger processing. This lets them cope with widely different package sizes that the intermediate networks will impose, as well as truncated/partial messages. Once you have it working, connect to it by way of your cell phone and you will get different set of packet fragmentation examples to test with.

Resources