Trouble in reading from Serial port using QSerialPort - qt

I have to develop a C++ program for an embedded FriendlyARM-based processor system.
I use Qt Creator 3.0.0 (based on Qt 5.2.0) for desktop computer. My program should be able to read from serial port at Mini2440 FriendlyARM processor.
Before going to target system (embedded system), I tried to read and write from/to a serial port on my Laptop. My main problem is how to read from serial port. As you know, new computers and laptops don't have serial port so I try to simulate serial port programming using hand-made USB-to-serial adapter cable. When the USB serial cable is plugged in, it is recognized as "/dev/ttyUSB0" on Ubuntu. It seems to work well.
Please note, the other end of cable (the serial port) isn't connected to anything.
My first question is: Is it OK to configure cable like this, or I have to connect it to other device?
I try to wrote to /dev/ttyUSB0 each 10 seconds and read the data. I ended up the following code:
void MainWindow::refreshNotificationArea()
{
generateNotifAreaData(); // a typical random data-generator
QList<QSerialPortInfo> L = QSerialPortInfo::availablePorts();
for (auto e : L)
qDebug() << e.portName() << '\n'; // it prints 1 serial port: :ttyUSB0
// write to the port
QSerialPort notifAreaPort;
// 1. set properties
notifAreaPort.setBaudRate(QSerialPort::Baud9600);
notifAreaPort.setStopBits(QSerialPort::OneStop);
notifAreaPort.setParity(QSerialPort::NoParity);
notifAreaPort.setDataBits(QSerialPort::Data8);
notifAreaPort.setFlowControl(QSerialPort::NoFlowControl);
QObject::connect(&notifAreaPort,SIGNAL(error(QSerialPort::SerialPortError)),
this, SLOT(errorReport(QSerialPort::SerialPortError)));
notifAreaPort.setPortName(serial_device.c_str());
// 2. open port
notifAreaPort.open(QIODevice::ReadWrite);
if (!notifAreaPort.isOpen())
qDebug() << "Open failed"; // open is OK, no error message printed
string s = convertNotifAreadData2Str();
qDebug() << "Generated data " << s.c_str(); // OK
int a = notifAreaPort.write(s.c_str()); // write done
qDebug() << "Write count" << a; // OK
// now read the info
QByteArray ba = notifAreaPort.readLine(3); // read failed
QSerialPort::SerialPortError err = notifAreaPort.error();
qDebug() << "Error code" << err;
qDebug() << "What? " << notifAreaPort.errorString();
qDebug() << "Read count " << ba.size(); // 0
notifAreaPort.close();
}
void MainWindow::errorReport(QSerialPort::SerialPortError error)
{
if(error!=0)
qDebug()<<"ERROR:"<<endl<<error; // nothing printed
}
Writing to serial port is OK. but Reading issues sometimes "No such file or directory"!
sometimes "File temporarily unavalable!
The strange thing is notifAreaPort.error() returns 0, and it means no error
occured!
Thoughts?
-- Saeed Amrollahi Boyouki

You cannot (well, SHOULD not) write then read from a QSerialPort in the same function.
There are two methods which I use for QSerialPort processing:
METHOD ONE
Create and open your QSerialPort object.
Set up a QTimer with a timeout of around 50 ms or so (depends on hardware).
Connect the readyRead() signal to a slot which basically just reads all data into your buffer (QByteArray is ideal for this). The slot stops the QTimer, reads all data available with readAll() and then restarts the QTimer and returns.
Connect the timeout signal of the QTimer to a function to process the read bytes of input.
The premise here is that eventually all data will have arrived and the QTimer will timeout, at which point you will have had all of your data in the buffer to process.
METHOD TWO
The slot which handles readyRead() signal can check all data in the buffer for some "marker" which denotes that some chunk of data has fully arrived. Many devices use 0x0D or 0x0d0x0A as the delmiter. Others use NULL 0x00 or some other byte.
Evaluate the buffer at each iteration of the readyRead() handler slot.
This example shows the second choice and it works well for small reads.
r_port = new QSerialPort(this);
r_port->setPortName("COM3");
r_port->setBaudRate(QSerialPort::Baud9600);
r_port->setDataBits(QSerialPort::Data8);
r_port->setParity(QSerialPort::NoParity);
r_port->setStopBits(QSerialPort::OneStop);
r_port->setFlowControl(QSerialPort::NoFlowControl);
if (r_port->open(QSerialPort::ReadWrite))
{
connect(r_port, &QSerialPort::readyRead, this, &MYPROG::on_readyRead);
connect(r_port, &QSerialPort::errorOccurred, this, &MYPROG::breakCaught);
}
else
{
QMessageBox::critical(this, "SERIAL PORT NOT CONNECTED", "Unable to connect to the radio.\n\nPlease check your connections\nand configuration and try again.");
return;
}
void MYPROG::on_readyRead()
{
// keep reading until we get a \r\n delimiter
rxBytes.append(r_port->readAll());
qDebug()<<"raw rxBtes"<<rxBytes;
if(!rxBytes.contains("\r\n"))
{
return;
}
int end = rxBytes.lastIndexOf("\r\n") + 2;
QStringList cmds = QString(rxBytes.mid(0, end)).split("\r\n", QString::SkipEmptyParts);
rxBytes = rxBytes.mid(end);
foreach(QString cmd, cmds){
qDebug()<<"serial read"<<cmd;
}
}
This allows the QSerialPort to be read when data arrives and then the program can return to the event loop in order to keep a GUI from becoming unresponsive. Typical serial reads are small and of short time duration so UI freezing rarely happens using these methods.

There are a couple of issues in your code, but I will highlight the most important of those:
You are setting the parameters before opening. This should happen after opening. That is how the API was sadly designed, but we are in the process of revamping it.
You should command line examples for reading that I added in 5.2? It seems that you do not know how to read and those would give you a simple example. In short: you are basically trying to read before the write potentially even finished.

„Hand-made USB to serial adapter“ - sounds interesting. Are you sure that this works correctly?. I think it’s a good idea to connect PIN 2(Rx) and 3(Tx), so you getting data. Now you can test your device with any other terminal software.
I use for serial ports always the readyRead() signal and I check before reading with port->bytesAvailable().
And I open my port with port->open(QIODevice::ReadWrite | QIODevice::Unbuffered).

Related

Using ZeroMQ ZMQ_STREAM to be a tcp client. Why am I receiving extra info?

I have an application that uses ZeroMQ for various things and I want to also use it as a tcp-client for other external connections.
Currently if the external tcp-server sends data, the client receives 5 byte id, 0 bytes, 5 bytes, and then actual message.
How do I get ZeroMQ not to send this stuff?
#include <iostream>
#include <string>
#include <zmq.h>
#include <cstring>
#include <assert.h>
#include <chrono>
#include <thread>
int main()
{
void *mpSocketContext = zmq_ctx_new();
/* Create ZMQ_STREAM socket */
void *mpSerialSocket = zmq_socket(mpSocketContext, ZMQ_STREAM);
void *mpSocket = mpSerialSocket;
bool aeBlocking = true;
std::string asAddress = "127.0.0.1:1236";
asAddress = "tcp://" + asAddress;
std::cout << "tcSerialServerPort::tcSerialServerPort: connecting to " << asAddress << std::endl;
int rc = zmq_connect(mpSerialSocket, asAddress.c_str());
if (rc != 0)
std::cout << "ZMQ ERROR: zmq_connect " << zmq_strerror(zmq_errno()) << std::endl;
uint8_t id [256];
size_t id_size = 256;
rc = zmq_getsockopt (mpSerialSocket, ZMQ_IDENTITY, id, &id_size);
assert(rc == 0);
while(true)
{
zmq_msg_t msg;
zmq_msg_init(&msg);
size_t lnBytesReceived = 0;
std::string lsStr;
lnBytesReceived = zmq_recvmsg(mpSocket, &msg, aeBlocking ? 0 : ZMQ_DONTWAIT);
lsStr = std::string(static_cast<const char*>(zmq_msg_data(&msg)),
zmq_msg_size(&msg));
std::cout << "Received Bytes=" << lsStr.size() << " Data=" << lsStr << std::endl;
zmq_msg_close(&msg);
std::this_thread::sleep_for(std::chrono::seconds(1));
}
zmq_close(mpSerialSocket);
zmq_ctx_destroy(mpSocketContext);
return 0;
}
Step #1: Don't panic.
It is very easy - either stop using ZeroMQ, or start to design things compatible with the published ZeroMQ API documentation. Seeking a third way is still possible, but one may easily guess what such a fork-project will finish in.
Best let's start re-reading the design rules from the API:
"Why am I receiving extra info?" The ZeroMQ published API says:
Native pattern
The native pattern is used for communicating with TCP peers and allows asynchronous requests and replies in either direction.
ZMQ_STREAM
A socket of type ZMQ_STREAM is used to send and receive TCP data from a non-ØMQ peer, when using the tcp:// transport. A ZMQ_STREAM socket can act as client and/or server, sending and/or receiving TCP data asynchronously.
When receiving TCP data, a ZMQ_STREAM socket shall prepend a message part containing the identity of the originating peer to the message before passing it to the application. Messages received are fair-queued from among all connected peers.
When sending TCP data, a ZMQ_STREAM socket shall remove the first part of the message and use it to determine the identity of the peer the message shall be routed to, and unroutable messages shall cause an EHOSTUNREACH or EAGAIN error.
To open a connection to a server, use the zmq_connect call, and then fetch the socket identity using the ZMQ_IDENTITY zmq_getsockopt call.
To close a specific connection, send the identity frame followed by a zero-length message (see EXAMPLE section).
When a connection is made, a zero-length message will be received by the application. Similarly, when the peer disconnects (or the connection is lost), a zero-length message will be received by the application.
You must send one identity frame followed by one data frame. The ZMQ_SNDMORE flag is required for identity frames but is ignored on data frames.
The rest is obvious, follow the API documented behaviour in the user-code and all the ZeroMQ things work as charm.

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.

Sending AT commands to a GSM/GPRS and displaying the reply on a Serial Monitor

I am quite rusty when it comes to Serial ports. I want to send an AT command to a GSM/ GPRS shield connected to my Arduino UNO. The AT command I want to pass in particular is the command to get a networks signal strength.
I am using the SIM900 and SoftwareSerial library to send the command as the GSM library does not compile correctly for me. Meaning I have to use the SoftwareSerial library.
I have this example code from the SIM900 library working that relies on reading inputs from the serial monitor to carry out commands but I need it to be automated and the command to be passed in hardcoded. In this example code, the place of interest is the simplehwread() method.
#include "SIM900.h"
#include <SoftwareSerial.h>
int numdata;
char inSerial[40];
int i=0;
void setup()
{
//Serial connection.
Serial.begin(9600);
Serial.println("GSM Shield testing.");
//Start configuration of shield with baudrate.
//For http uses is raccomanded to use 4800 or slower.
if (gsm.begin(9600))
Serial.println("\nstatus=READY");
else Serial.println("\nstatus=IDLE");
};
void loop()
{
//Read for new byte on serial hardware,
//and write them on NewSoftSerial.
serialhwread();
//Read for new byte on NewSoftSerial.
serialswread();
};
void serialhwread()
{
i=0;
if (Serial.available() > 0) {
while (Serial.available() > 0) {
inSerial[i]=(Serial.read());
delay(10);
i++;
}
inSerial[i]='\0';
if(!strcmp(inSerial,"/END")) {
Serial.println("_");
inSerial[0]=0x1a;
inSerial[1]='\0';
gsm.SimpleWriteln(inSerial);
}
//Send a saved AT command using serial port.
if(!strcmp(inSerial,"TEST")) {
Serial.println("SIGNAL QUALITY");
gsm.SimpleWriteln("AT+CSQ");
} else {
Serial.println(inSerial);
gsm.SimpleWriteln(inSerial);
}
inSerial[0]='\0';
}
}
void serialswread()
{
gsm.SimpleRead();
}
No matter how I modify this code, the command does not get passed in and response displayed while the method here does it but not the way I want it to be done. i.e Direct input. Could anyone assist here?
i have dealt with exactly this scenario at a company with a cellular radio on board. there are many status signals that come over and if not dealt with appropriately these status flags from the cell modem will be lost
you need to look at the data sheets associated with your cell modem and its protocol so you know what flags to watch for at the various steps taken along the way from configuration, to eventual connection to cellular service.
multi-threaded coding techniques must be followed as well.
keep in mind that the comm channel is not ideal and there WILL be failures. provided your coding techniques are sound and you follow protocol requirements, then it should work.
Ron
Boise, ID

Winsock bind() failing with WSAEADDRNOTAVAIL for directed broadcast address

I am setting up a UDP socket and trying to bind what should be a valid network broadcast address to it (192.168.202.255 : 23456), but bind fails with error 10049, WSAEADDRNOTAVAIL. If I use a localhost broadcast address, 127.0.0.255, it succeeds.
WSAEADDRNOTAVAIL's documentation says that "The requested address is not valid in its context. This normally results from an attempt to bind to an address that is not valid for the local computer. This can also result from connect, sendto, WSAConnect, WSAJoinLeaf, or WSASendTo when the remote address or port is not valid for a remote computer (for example, address or port 0)." But I think this address, 192.168.202.255, should be a valid broadcast address because of the following entry when running ipconfig:
What might be the problem?
Code
I am new to Winsock programming and am probably making an elementary error, but I can't find it. The code I have so far is:
m_ulAddress = ParseIPAddress(strAddress);
// Winsock 2.2 is supported in XP
const WORD wVersionRequested = MAKEWORD(2, 2);
WSADATA oWSAData;
const int iError = WSAStartup(wVersionRequested, &oWSAData);
if (iError != 0) {
PrintLine(L"Error starting the network connection: WSAStartup error " + IntToStr(iError));
} else if (LOBYTE(oWSAData.wVersion) != 2 || HIBYTE(oWSAData.wVersion) != 2) {
PrintLine(L"Error finding version 2.2 of Winsock; got version " + IntToStr(LOBYTE(oWSAData.wVersion)) + L"." + IntToStr(HIBYTE(oWSAData.wVersion)));
} else {
m_oSocket = socket(AF_INET, SOCK_DGRAM /*UDP*/, IPPROTO_UDP);
if (m_oSocket == INVALID_SOCKET) {
PrintLine(L"Error creating the network socket");
} else {
// Socket needs to be able to send broadcast messages
int iBroadcast = true; // docs say int sized, but boolean values
if (setsockopt(m_oSocket, SOL_SOCKET, SO_BROADCAST, (const char*)&iBroadcast, sizeof(iBroadcast)) != 0) {
PrintLine(L"Error setting socket to allow broadcast addresses; error " + IntToStr(WSAGetLastError()));
} else {
m_oServer.sin_family = AF_INET;
m_oServer.sin_port = m_iPort;
m_oServer.sin_addr.S_un.S_addr = m_ulAddress;
// !!! This is the failing call
if (bind(m_oSocket, (sockaddr*)&m_oServer, sizeof(m_oServer)) == -1) {
PrintLine(L"Error binding address " + String(strAddress.c_str()) + L":" + IntToStr(m_iPort) + L" to socket; error " + IntToStr(WSAGetLastError()));
} else {
m_bInitialisedOk = true;
}
}
}
}
Comments
ParseIPAddress is a wrapper around inet_addr; inspecting the value of m_oServer.sin_addr.S_un.S_addr it appears to be correct. m_oSocket is a SOCKET. I added the call to setsockopt since you can't broadcast via anything but TCP by default (see the second paragraph in sendto's Remarks); this call doesn't make any difference. PrintLine is a wrapper to the console output. The odd String / c_str() casts are converting to and from C++ wstrings to VCL Unicode strings, since I am using C++ Builder and its VCL libraries. The IP address is a narrow (char) string.
The sendto documentation states that "If a socket is opened, a setsockopt call is made, and then a sendto call is made, Windows Sockets performs an implicit bind function call." This implies that bind is not needed at all. If I omit the call, then calling sendto like so:
const int iLengthBytes = strMessage.length() * sizeof(char); // Narrow string
const int iSentBytes = sendto(m_oSocket, strMessage.c_str(), iLengthBytes, 0, (sockaddr*)&m_oServer, sizeof(m_oServer));
if (iSentBytes != iLengthBytes) {
PrintLine(L"Error sending network message; error: " + IntToStr(WSAGetLastError()));
fails with error 10047, WSAEAFNOSUPPORT, "Address family not supported by protocol family."
The output of netsh winsock show catalog (mentioned at the bottom of socket's Remarks) is lengthy but does include several entries mentioning UDP and IPv4.
A possible complication is that this is running in a VMWare Fusion host; Fusion does have an odd setup for networks. I also have a Cisco VPN configured running back to my office. Connecting and disconnecting this makes no difference.
One thing that seems dodgy to me is hard-casting the SOCKET m_oSocket to sockaddr, but this seems to be normal practice for Winsock programming when I've been reading examples. Reading up it may be required since the underlying interpretation depends on the protocol family. It seems a potential source of error, but I'm not sure how to avoid it.
Any ideas? I am stumped :)
Setup
Windows 7 Pro running on VMWare Fusion 4.1.3
The program is compiled as 32-bit with Embarcadero C++ Builder 2010.
The program is a console program only
Much confusion here. I'll address it point by point for your edification, but if you just want working code, skip to the end.
// Winsock 2.2 is supported in XP
Actually, Winsock 2.2 goes back to NT 4 SP4, which dates it to 1998. Because of that, I wouldn't bother checking oWSAData.wVersion in the error case. There's basically no chance this is going to happen any more.
If broad portability is your goal, I'd target Winsock 1.1, which is all you need for the code you show, and will let the code build and run on anything that supports Winsock, even back to Windows 3.x.
m_oSocket = socket(AF_INET, SOCK_DGRAM /*UDP*/, IPPROTO_UDP);
Bad style. You should use PF_INET here instead of AF_INET. They have the same value, but you're not specifying an address family (AF) here, you're specifying a protocol family (PF). Also, the third parameter can safely be zero, because it's implied by the first two parameters. Again, it's just a style fix, not a functional fix.
int iBroadcast = true; // docs say int sized, but boolean values
Yup. Don't second-guess the docs and use bool here. Remember, Winsock is based on BSD sockets, and that goes back to the days before C++ existed.
m_oServer.sin_addr.S_un.S_addr = m_ulAddress;
You really shouldn't be digging into the internals of the sockaddr_in structure this way. The sockets API has a shortcut for that, which is shorter and hides some of the internal implementation details. It is:
m_oServer.sin_addr.s_addr = m_ulAddress;
Moving on...
if (bind(m_oSocket, ...
Although Remy is right that the bind() call isn't correct, you actually don't need it at all. You can depend on your system's routing layer to send the packet out the right interface. You don't need to "help" it with a bind() call.
you can't broadcast via anything but TCP by default (see the second paragraph in sendto's Remarks);
You've misunderstood what MSDN is telling you. When you see the term "TCP/IP", it often (but not always!) includes UDP. They're using it in that generic sense here.
The MSDN bit you point to talks about TCP/IP because Winsock was created in a world when TCP/IP had not yet won the network protocol wars. They're trying to restrict the discussion to TCP/IP (UDP, really) so you don't get the idea that what they're saying applies to other network transports supported by Winsock stacks in the early days: NetBIOS, IPX, DECNet...
In fact, you can only broadcast (or multicast) using UDP sockets. TCP is point-to-point, only.
One thing that seems dodgy to me is hard-casting the SOCKET m_oSocket to sockaddr,
That's also part of the multiple network transport support in sockets. In addition to sockaddr_in, there's sockaddr_ipx for IPX, sockaddr_dn for DECnet... Winsock is a C API, not a C++ API, so we can't subclass sockaddr and pass a reference to the base class, or create function overloads for each of the variations. This trick of casting structures is a typical C way to get a kind of polymorphism.
Here's a working example, which builds with MinGW, g++ foo.cpp -o foo.exe -lwsock32:
#include <winsock.h>
#include <iostream>
#include <string.h>
using namespace std;
int main(int argc, char* argv[])
{
WSADATA wsa;
if (WSAStartup(MAKEWORD(1, 1), &wsa)) {
cerr << "Failed to init Winsock!" << endl;
return 1;
}
// Get datagram socket to send message on
SOCKET sd = socket(PF_INET, SOCK_DGRAM, 0);
if (sd < 0) {
cerr << "socket() failed: " << WSAGetLastError() << endl;
return 1;
}
// Enable broadcasts on the socket
int bAllow = 1;
if (setsockopt(sd, SOL_SOCKET, SO_BROADCAST, (char*)&bAllow,
sizeof(bAllow)) < 0) {
cerr << "setsockopt() failed: " << WSAGetLastError() << endl;
closesocket(sd);
return 1;
}
// Broadcast the request
string msg = "Hello, world!";
const int kMsgLen = msg.length();
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
const uint16_t kPort = 54321;
sin.sin_port = htons(kPort);
sin.sin_family = AF_INET;
if (argc == 1) {
sin.sin_addr.s_addr = INADDR_BROADCAST;
}
else if ((sin.sin_addr.s_addr = inet_addr(argv[1])) == INADDR_NONE) {
cerr << "Couldn't parse IP '" << argv[1] << "'!" << endl;
}
int nBytes = sendto(sd, msg.c_str(), kMsgLen, 0,
(sockaddr*)&sin, sizeof(struct sockaddr_in));
closesocket(sd);
// How well did that work out, then?
if (nBytes < 0) {
cerr << "sendto() IP " << inet_ntoa(sin.sin_addr) <<
" failed" << WSAGetLastError() << endl;
return 1;
}
else if (nBytes < kMsgLen) {
cerr << "WARNING: Short send, " << nBytes << " bytes! "
"(Expected " << kMsgLen << ')' << endl;
return 1;
}
else {
cerr << "Sent " << kMsgLen << "-byte msg to " <<
inet_ntoa(sin.sin_addr) << ':' << kPort << '.' << endl;
}
return 0;
}
It sends to 255.255.255.255 (INADDR_BROADCAST) by default, but if you pass a directed broadcast IP (such as your 192.168.202.255 value) as the first parameter, it will use that instead.
You should not bind() to a broadcast IP address. You need to bind() to an individual network adapter IP instead. If you want to send out a broadcast message, you bind() to the adapter that is going to send the broadcast, and then sendto() the broadcast IP. If you want to receive a broadcast message, you bind() to the specific adapter whose IP matches the broadcast IP being sent to.

Persistent Qt Local Socket IPC

I'm developing an application that uses IPC between a local server and a client application. There is nothing particular to it, as it's structured like the Qt documentation and examples.
The problem is that the client sends packets frequently and connecting/disconnecting from the server local socket (named pipe on NT) is very slow. So what I'm trying to achieve is a "persistent" connection between the two applications.
The client application connects to the local server (QLocalServer) without any problem:
void IRtsClientImpl::ConnectToServer(const QString& name)
{
connect(_socket, SIGNAL(connected()), this, SIGNAL(connected()));
_blockSize = 0;
_socket->abort();
_socket->connectToServer(name, QIODevice::ReadWrite);
}
And sends requests also in the traditional Qt manner:
void IRtsClientImpl::SendRequest( quint8 cmd, const QVariant* const param_array,
unsigned int cParams )
{
// Send data through socket
QByteArray hdr(PROTO_BLK_HEADER_PROJ);
QByteArray dataBlock;
QDataStream out(&dataBlock, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_5);
quint8 command = cmd;
out << blocksize_t(0) // block size
<< hdr // header
<< quint32(PROTO_VERSION_PROJ) // protocol version
<< command // command
<< cParams; // number of valid parameters
for (unsigned int i = 0; i < cParams; ++i)
out << param_array[i];
// Write the current block size
out.device()->seek(0);
out << dataBlock.size() - sizeof(blocksize_t);
_socket->write(dataBlock);
}
No problem. But the trick resides on the readyRead() signal in the server-side. Here's the current implementation of the readyRead() handling slot:
void IRtsServerImpl::onReadyRead()
{
QDataStream in(_lsock);
in.setVersion(QDataStream::Qt_4_5);
if (_blocksize == 0)
{
qDebug("Bytes Available on socket: %d", _lsock->bytesAvailable());
if (_lsock->bytesAvailable() < sizeof(blocksize_t))
return;
in >> _blocksize;
}
// We need more data?
if (_lsock->bytesAvailable() < _blocksize)
return;
ReadRequest(in);
// Reset
_blocksize = 0;
}
Without setting _blocksize to zero I could not receive more data, only the first block group (I would expect an entire block to arrive without segmentation since this is through a pipe, but it does not, go figure). I expect that behavior, sure, since the _blocksize does not represent the current stream flow anymore. All right, resetting _blocksize does the trick, but I can't resend another packet from the client without getting an increasing array of bytes on the socket. What I want is to process the request in ReadRequest and receive the next data blocks without resorting to connecting/reconnecting the applications involved.
Maybe I should 'regulate' the rate of the incoming data?
Thank you very much.

Resources