QTcpSocket two way client-server communication - c++

i'm developing an app for Raspberry PI based on socket interface. The main idea is that Raspberry will be connected to a sensor, collect data and send it via WiFi to Android device. From Android I can communicate with sensor sending some commands. I'm a beginner in this kind of development and following some tutorials about QTcpSocket I have created a simple client-server app but it is only in one direction. Server listens for what client is sending. Could you help me to improve it into two way communication? I've read that QTcpSocket doesn't require threading for this kind of problem but I didn't find any solution.
I would appreciate any help!
server.cpp:
#include "server.h"
#include <QTcpServer>
#include <QTcpSocket>
#include <cstdio>
#include <QtDebug>
Server::Server(QObject *parent) :
QObject(parent)
{
server = new QTcpServer(this);
connect(server, SIGNAL(newConnection()),
this, SLOT(on_newConnection()));
}
void Server::listen()
{
server->listen(QHostAddress::Any, 5100);
}
void Server::on_newConnection()
{
socket = server->nextPendingConnection();
if(socket->state() == QTcpSocket::ConnectedState)
{
printf("New connection established.\n");
qDebug()<<socket->peerAddress();
}
connect(socket, SIGNAL(disconnected()),
this, SLOT(on_disconnected()));
connect(socket, SIGNAL(readyRead()),
this, SLOT(on_readyRead()));
}
void Server::on_readyRead()
{
while(socket->canReadLine())
{
QByteArray ba = socket->readLine();
if(strcmp(ba.constData(), "!exit\n") == 0)
{
socket->disconnectFromHost();
break;
}
printf(">> %s", ba.constData());
}
}
void Server::on_disconnected()
{
printf("Connection disconnected.\n");
disconnect(socket, SIGNAL(disconnected()));
disconnect(socket, SIGNAL(readyRead()));
socket->deleteLater();
}
client.cpp
#include "client.h"
#include <QTcpSocket>
#include <QHostAddress>
#include <cstdio>
Client::Client(QObject *parent) : QObject(parent)
{
socket = new QTcpSocket(this);
printf("try to connect.\n");
connect(socket, SIGNAL(connected()),
this, SLOT(on_connected()));
}
void Client::on_connected()
{
printf("Connection established.\n");
char buffer[1024];
forever
{
while(socket->canReadLine())
{
QByteArray ba = socket->readLine();
printf("from server: %s", ba.constData());
}
printf(">> ");
gets(buffer);
int len = strlen(buffer);
buffer[len] = '\n';
buffer[len+1] = '\0';
socket->write(buffer);
socket->flush();
}
}
void Client::connectToServer()
{
socket->connectToHost(QHostAddress::LocalHost, 5100);
}

From architectural point of view you should define some communication rules (message flow) between your server and client first.
Then just read(write) from(to) instance of QTCPSocket according to defined flow.
You can, for instance, read data on server side, check what you should answer and write response to the same socket from which you have read. For line oriented messages (and only for them) code could look like:
void Server::on_readyRead()
{
// "while" loop would block until at least one whole line arrived
// I would use "if" instead
if(socket->canReadLine())
{
QByteArray ba = socket->readLine();
QByteArray response;
// some code which parses arrived message
// and prepares response
socket->write(response);
}
//else just wait for more data
}
Personally, I would move parsing and sending responses out from on_readyRead() slot to avoid blocking event loop for too long time, but since you are a beginner in network programming I just wanted to clarify what could be done to implement two way communication.
For more details you can see http://qt-project.org/doc/qt-4.8/qtnetwork.html
Remember about checking if whole message has arrived on both client and server side. If you use your own protocol (not HTTP, FTP nor other standarized) you can add message length on the beginning of message.

All you need to do is to write/read from the socket from the client or server. A TCP connection is already two way connection.
You may be having some issues with your forever loop in Client. You should really use the readyRead signal on the socket in the same way that you do for the server and drop that forever loop.
Handle keyboard input in a non-blocking manner rather than using gets(). gets() will block the main thread and prevent it from running the event loop. This is not the best way to handle things in your case since you want to be able to handle data from the server and from the user at the same time.
Maybe take a look at this with respect to keyboard handling from a console app:
using-qtextstream-to-read-stdin-in-a-non-blocking-fashion
Alternatively make it a GUI app and use a QPlainTextEdit.

Related

Handling TCP/IP Packet Splitting using Qt or Similar Libraries?

I am trying to send TCP messages over my local network that exceed the MTU limit of ~1500 bytes. I know that TCP protocol splits messages exceeding 1500 bytes into individual packets at some level, but am unclear if it's something I need to deal with at the application level. So I wrote a test application in ROS and Qt, C++, to test the behavior. I picked these because they are what my overall project is written in.
My test_fragmentation_test node sets up a server, then the test_client node establishes a connection and sends a message exceeding 1500 bytes. The goal is to get the server to receive this full message as one cohesive unit.
When I test these nodes on the same computer, the server receives the full messages as one unit. However, when I put the server and client on separate computers (still same network), the server receives the message as multiple packets. The packets are either 1500 bytes, or multiples thereof, so I believe they are sometimes getting squashed together in the read buffer in pairs of two or three.
Here is my code:
tcp_fragmentation_test.cpp
#include "ros/ros.h"
#include <QtCore/QCoreApplication>
#include <QTcpSocket>
#include <QTcpServer>
#include <QDebug>
//prototype functions
void initialConnect();
void receiveMessage();
//global(ish) variables
extern QTcpServer* subserver;
extern QTcpSocket* subsocket;
QTcpServer* subserver;
QTcpSocket* subsocket;
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
//initiate ROS
ros::init(argc, argv, "tcp_fragmentation_test_node");
ros::NodeHandle n;
subserver = new QTcpServer();
//call initialConnect on first connection attempt
QObject::connect(subserver, &QTcpServer::newConnection,initialConnect);
quint16 server_port = 9998;
//start listening
QHostAddress server_IP = QHostAddress("127.0.0.1");
//start listening for incoming C2 connections
if(!subserver->listen(server_IP, server_port))
{
qDebug().noquote() << "subserver failed to start on: " + server_IP.toString() + "/" + QString::number(server_port);
}
else
{
qDebug().noquote() << "subserver started on: " + server_IP.toString() + "/" + QString::number(server_port);
}
return app.exec(); //inifite loop that starts qt event listener
}
//accept incoming connection
void initialConnect()
{
//accept the incoming connection
subsocket = subserver->nextPendingConnection();
QObject::connect(subsocket, &QTcpSocket::readyRead, receiveMessage);
}
void receiveMessage()
{
//read data in
QByteArray received_message = subsocket->readAll();
qDebug() << "message received: ";
qDebug() << received_message;
}
test_client.cpp
#include "ros/ros.h"
#include <QtCore/QCoreApplication>
#include <QTcpSocket>
#include <QTcpServer>
#include <QDebug>
//function prototypes
bool initialConnect(QString server_IP, quint16 server_port);
void sendMessage(QByteArray message);
//global(ish) variables
QTcpSocket* test_socket;
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
ros::init(argc, argv, "test_client");
ros::NodeHandle n;
initialConnect("127.0.0.1",9998);
//send 1500+ byte message, to test IP fragmentation
sendMessage("<message large than 1500 bytes>");
return app.exec(); //inifite loop that starts qt event listener
}
bool initialConnect(QString server_IP, quint16 server_port)
{
//create test_socket
test_socket = new QTcpSocket();
//try to connect, then wait a bit to make sure it was successful
test_socket->connectToHost(server_IP, server_port);
if(test_socket->waitForConnected(1000))
{
return true;
}
else
{
qDebug() << "Failed to connect to server";
return false;
}
}
//send test message
void sendMessage(QByteArray message)
{
//send message
test_socket->write(message);
test_socket->waitForBytesWritten(1000);
return;
}
I talked to one of my colleagues about this, and they said that they had not run into this issue when sending messages that exceed the MTU in both Tcl scripting language, and HTML websockets. They sent me their code to check out, but unfortunately it is a small part of a really large and not well documented codebase, so I am having parsing out what they did and why they did not run into the same issues as me.
I know that I could probably avoid this issue by including identifying headers for each message, that include the length, and then combine read messages into an overall buffer until the correct number of bytes have been read. However, I'm trying to see if there's a simpler way than this, especially after my colleague telling me that he never ran into this issue. It seems like something that should be handled behind the scenes by the TCP protocol, and I'm trying to avoid dealing with it at the application level if at all possible.
Any ideas?

How to read complete data using QTcpSocket (Qt4.7)

I created a TcpServer in order to receive data from a client. The client sends a lot of messages and I would like to read them. So far my TcpServer.cpp looks like this :
void TcpServer::serverStart()
{
server = new QTcpServer(this);
if (!server->listen(QHostAddress("192.168.x.x"), 48583))
{
qDebug() << "Not listening";
server->close();
delete server;
return;
}
else {
qDebug() << "Listening";
}
connect(server, SIGNAL(newConnection()), this, SLOT(newConnection()));
}
void TcpServer::newConnection()
{
socket = server->nextPendingConnection();
qDebug() << "Client connected";
connect(socket, SIGNAL(readyRead()), this, SLOT(getData()));
connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater()));
}
void TcpServer::getData()
{
QByteArray buffer;
while (socket->bytesAvailable())
{
buffer.append(socket->readAll());
}
qDebug() << buffer;
}
void TcpServer::serverStop()
{
server->close();
delete server;
}
I know my getData function needs a lot more in order to receive everything but I don't understand the steps needed to do that.If someone could give me some pointers I would be grateful !
TCP is a transport protocol which is stream oriented. Imagine it as being a continuous flow of data. There are no messages defined by TCP yet, because once again it is a continuous flow of data.
I'm taking from your comment that you are not using any application layer protocol. You need an application layer protocol, like e.g. http, which is then defining "messages" and giving you further instructions on how to read a complete message.

Bad Request, Your browser sent a request that this server could not understand - Qt Websocket Server

I have two questions about this issue.
First of all I'm trying to get the following code working
socket = new QTcpSocket(this);
// I'm a little confused as to why we're connecting on port 80
// when my goal is to listen just on port 3000. Shouldn't I just
// need to connect straight to port 3000?
socket->connectToHost("localhost", 80);
if (socket->waitForConnected(3000))
{
qDebug() << "Connected!";
// send
socket->write("hello server\r\n\r\n\r\n\r\n");
socket->waitForBytesWritten(1000);
socket->waitForReadyRead(3000);
qDebug() << "Reading: " << socket->bytesAvailable();
qDebug() << socket->readAll();
socket->close();
}
else
{
qDebug() << "Not connected!";
}
But this is the error that I get:
"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n<html><head>\n<title>400 Bad Request</title>\n</head><body>\n<h1>Bad `Request</h1>\n<p>Your browser sent a request that this server could not understand.<br />\n</p>\n<hr>\n<address>Apache/2.4.18 (Ubuntu) Server at 127.0.1.1 Port 80</address>\n</body></html>\n"`
Has anyone got any ideas about this?
Second question is: I'm trying to get a c++/Qt server working similar to a node js server. So I'm wanting to be able to access the connection requests in the browser. So when someone connects to site:3000 I will be able to catch the request and display some content. Can it be achieved with a QTcpSocket server? If so then how could I implement something like :
// I know this isn't valid c++, Just to give an idea of what I'm trying to achieve
socket.on(Request $request) {
if ($request.method() == 'GET') {
}
}
If this is achievable is there much speed gains in comparison to doing this in nodejs?
I'm personally trying to avoid js as much as possible.
if i comment the code then I can get a running program but when I try to connect on port 8000 from the browser nothing happens (just a 404 error)
updated answer:
header file:
#ifndef SOCKETTEST_H
#define SOCKETTEST_H
#include <QObject>
#include <QTcpServer>
#include <QTcpSocket>
#include <QDebug>
class SocketTest : public QTcpServer
{
public:
SocketTest(QObject *parent);
private:
QTcpSocket *client;
public slots:
void startServer(int port);
void readyToRead(void);
void incomingConnection(int socket);
};
#endif // SOCKETTEST_H
.cpp file
#include "sockettest.h"
SocketTest::SocketTest(QObject *parent) :
QTcpServer(parent)
{
this->startServer(8000);
}
void SocketTest::startServer(int port)
{
bool success = listen(QHostAddress::Any, port); // this starts the server listening on your port
// handle errors
}
void SocketTest::incomingConnection(int socket)
{
// a client has made a connection to your server
QTcpSocket *client = new QTcpSocket(this);
//client->setSocketDescription(socket);
// these two lines are important, they will direct traffic from the client
// socket to your handlers in this object
connect(client, SIGNAL(readyRead()), this, SLOT(readToRead()));
connect(client, SIGNAL(disconnect()), this, SLOT(disconnected()));
}
void SocketTest::readyToRead(void)
{
QTcpSocket *client = (QTcpSocket*)sender();
qDebug() << "Just got a connection";
// you can process requests differently here. this example
// assumes that you have line breaks in text requests
while (client->canReadLine())
{
QString aLine = QString::fromUtf8(client->readLine()).trimmed();
// Process your request here, parse the text etc
}
}
// this gives me the following error
// /user_data/projects/qt/QtServer/sockettest.cpp:47: error: no ‘void
// SocketTest::disconnected()’ member function declared in class ‘SocketTest’
void SocketTest::disconnected()
^
void SocketTest::disconnected()
{
// jsut a qu, wont all these * vars lead to a memory leak? and shouldn't I be using a var Qtc... *client; in the header file?
QTcpSocket *client = (QTcpSocket*)sender();
// clean up a disconnected user
}
Here with waitForConnected, you are connecting on port 80, and waiting 3000ms maximum for the "connected state", i.e. not connecting on port 3000 at all. This is the blocking way of waiting for a connection to be established, instead of connecting to the QTcpSocket::connected signal.
Like Yuriy pointed out, QNetworkAccessManager is way more convenient to handle HTTP requests as a client. As in your example, you created a TCP client, and not a server
Yes you can build an web server with Qt, it's a bit painfull from scratch (QTcpServer class), but several projects make it a bit easier: QHttpServer, QtWebApp
If performance is your goal, I doubt you can achieve something significantly better (or just "better") without spending a lot of time on it. Namely to be able to handle a large number of request simultaneously in a fast way, a basic implementation will not be enough.
You should subclass QTCPServer. Set it up to listen on the port you want. This object will then get the requests and you can parse them and respond to them.
Something like this (partial code);
#include <QTcpServer>
#include <QTcpSocket>
class mySuperNodeLikeServer : public QTcpServer
{
mySuperNodeLikeServer(QObject *parent);
void startServer(int port);
void readyToRead(void);
void incomingConnection(int socket);
}
// in your .cpp file
void mySuperNodeLikeServer::startServer(int port)
{
bool success = listen(QHostAddress::Any, port); // this starts the server listening on your port
// handle errors
}
void mySuperNodeLikeServer::incomingConnection(int socket)
{
// a client has made a connection to your server
QTcpSocket *client = new QTcpSocket(this);
client->setSocketDescription(socket);
// these two lines are important, they will direct traffic from the client
// socket to your handlers in this object
connect(client, SIGNAL(readyRead()), this, SLOT(readToRead()));
connect(client, SIGNAL(disconnect()), this, SLOT(disconnected()));
}
void mySuperNodeLikeServer::readyToRead(void)
{
QTcpSocket *client = (QTcpSocket*)sender();
// you can process requests differently here. this example
// assumes that you have line breaks in text requests
while (client->canReadLine())
{
QString aLine = QString::fromUtf8(client->readLine()).trimmed();
// Process your request here, parse the text etc
}
}
void mySuperNodeLikeServer::disconnected()
{
QTcpSocket *client = (QTcpSocket*)sender();
// clean up a disconnected user
}

QTcpSocket / QTcpServer memory management / server crash

I am designing and making a server that should be able to handle about 100+ hits per second. The information I am getting from the server is just the HTTP header. Based on the information from the header, it will query a database(different thread) for some information and send the final information back to the QTcpServer which create an output string, and send back a HTTP Response. I am having a big problem with this that I cannot debug. My code look similar to this:
TCPInterface::TCPInterface(QObject *parent): QTcpServer(parent)
{
//start listening for tcp traffic on port 80
listen(QHostAddress::Any, 80);
connect(this,SIGNAL(sendInfo(QTcpSocket*, QString *)), databaseThread, SLOT(recieveInfo(QTcpSocket*, QString*)));
connect(databaseThread, SIGNAL(sendToTCPSend(QTcpSocket *, QString *)), this, SLOT(TCPSend(QTcpSocket*, QString*)));
}
`
void TCPInterface::incomingConnection(int socket)
{
QTcpSocket *s = new QTcpSocket(this);
connect(s, SIGNAL(readyRead()), this, SLOT(readClient()));
//connect(s, SIGNAL(disconnected()), this, SLOT(discardClient()));
s->setSocketDescriptor(socket);
}
`
//void TCPInterface::discardClient()
//{
//QTcpSocket* socket = (QTcpSocket*)sender();
//socket->deleteLater();
//}
`
void TCPInterface::readClient()
{
QTcpSocket* socket = (QTcpSocket*)sender();
QString header;
while(socket->canReadLine())
{
header += socket->readLine();
}
emit sendInfo(socket, headerInfo);
}
`
void databaseThread::recieveInfo(QTcpSocket* socket, QString* headerInfo)
{
QString*outputInfo = getDatabaseInfo(headerInfo);
emit sendToTCPSend(socket, outputInfo);
}
`
void TCPInterface::TCPSend(QTcpSocket* socket, QString* outputInfo);
{
QString response = "HTTP/1.0 200 Ok\r\n";
response += "Content-Type: text/html; charset=\"utf-8\"\r\n";
response += "\r\n" + *outputInfo + "\n";
if(socket->isWritable() && socket->isOpen())
{
socket->write(response.toAscii());
}
//socket->disconnectFromHost();
socket->close();
delete headerInfo;
}
I having one main problem which I have an idea what it is, but cannot find a solution to fix it.
My problem is my memory is constantly increasing as I get more hits. I am sure the cause of this is my QTcpSockets are never being deleted, since I am just closing them. However when I don't use close, and use disconnectFromHost and disconnected/discardClient slot/signal my server will crash with heavy traffic(no message or anything so I am not sure of the exact reason of the crash). Has anyone run into this problem before? Any ideas at all.
I have the same problem!
close() suppose to be as de-constructor of the object. (QT manual QTcpSocket::~QTcpSocket())
There for I suggest to do an experiment: close a socket, and try to re open it. If it fails, it means the socket Object was destroyed, if not means the object should be deletelater()..
In my case the connection is closed by the client, and there for disconnect() SIGNAL is invoked and it trigger the correspond method to your discardClient() SLOT, where I deletelater() the socket.
When I stress test it, it usually crash when I bombard it with 600-800 connection simultaneously on an I5 laptop dual core. it crash every 5 times on average.
Other wise it doesn't.
Happy to discuss it further.
Gil
You should call deleteLater() on your client socket:
connect(socket, SIGNAL(disconnected()),
socket, SLOT(deleteLater()));

Qt bidirectional client server using QTcpSocket and QTcpServer

I am trying to implement a bidirectional client-server program, where clients and servers can pass serialized objects between one another. I am trying to do this using Qt (QTcpSocket and QTcpServer). I have implemented programs like this in java, but I can't figure out how to do it using Qt. I've checked out the fortune client and fortune server examples...but from what I can see, the client is simply signaling the server, and the server sends it some data. I need for the client and server to send objects back and forth. I am not looking for a complete solution, all I am looking for is some guidance in the right direction.
I wrote some code, which accepts a connection, but does not accept the data.
SERVER
this class is the server; it should be accepting a connection and outputting the size of the buffer which is being sent. However it is outputting 0
#include "comms.h"
Comms::Comms(QString hostIP, quint16 hostPort)
{
server = new QTcpServer(this);
hostAddress.setAddress(hostIP);
this->hostPort = hostPort;
}
void Comms::attemptConnection(){
connect(server, SIGNAL(newConnection()), this, SLOT(connectionAccepted()));
//socket = server->nextPendingConnection();
server->listen(hostAddress,hostPort);
//receivedData = socket->readAll();
}
void Comms::connectionAccepted(){
qDebug()<<"Connected";
socket = new QTcpSocket(server->nextPendingConnection());
char* rec = new char[socket->readBufferSize()];
qDebug()<<socket->readBufferSize();
}
CLIENT
This class is the client. It should be sending the string 'hello'. It sends it successfully (to my knowledge)
#include "toplevelcomms.h"
#include "stdio.h"
TopLevelComms::TopLevelComms(QString hostIP, quint16 hostPort)
{
tcpSocket = new QTcpSocket();
hostAddress.setAddress(hostIP);
this->hostPort = hostPort;
}
void TopLevelComms::connect(){
tcpSocket->connectToHost(hostAddress,hostPort,QIODevice::ReadWrite);
//tcpSocket->waitForConnected(1);
QString string = "Hello";
QByteArray array;
array.append(string);
qDebug()<<tcpSocket->write(array);
}
Please tell me what I'm doing wrong, or tell me the general logic of establishing what I want in Qt.
QTcpSocket is asynchronous by default, so when you call connectToHost and write in same context it won't be sent, as socket is not connected. You should change your "client" code:
void TopLevelComms::connect(){
tcpSocket->connectToHost(hostAddress,hostPort,QIODevice::ReadWrite);
if(tcpSocket->waitForConnected()) // putting 1 as parameter isn't reasonable, using default 3000ms value
{
QString string = "Hello";
QByteArray array;
array.append(string);
qDebug()<<tcpSocket->write(array);
}
else
{
qDebug() << "couldn't connect";
}
}
Note: you also didn't check if you're able to listen
void Comms::attemptConnection(){
connect(server, SIGNAL(newConnection()), this, SLOT(connectionAccepted()));
//socket = server->nextPendingConnection();
if(server->listen(hostAddress,hostPort))
{
qDebug() << "Server listening";
}
else
{
qDebug() << "Couldn't listen to port" << server->serverPort() << ":" << server->errorString();
}
//receivedData = socket->readAll();
}
And last thing. Note that QTcpServer::nextPendingConnection() return QTcpSocket, so instead of taking that new connection you create new QTcpSocket with nextPendingConnection as parent
void Comms::connectionAccepted(){
qDebug()<<"Connected";
// WRONG! it will use QTcpSocket::QTcpSocket(QObject * parent)
//socket = new QTcpSocket(server->nextPendingConnection());
// use simple asign
socket = server->nextPendingConnection();
// move reading to slot
connect(socket, SIGNAL(readyRead()), this, SLOT(readSocket()));
}
now we will move reading to separate slot
void Comms::readSocket()
{
// note that dynamic size array is incompatible with some compilers
// we will use Qt data structure for that
//char* rec = new char[socket->readBufferSize()];
qDebug()<<socket->readBufferSize();
// note that QByteArray can be casted to char * and const char *
QByteArray data = socket->readAll();
}
I must admit, that it is a lot of errors as for such small code sample. You need to get some knowledge about TCP/IP connections. Those are streams and there is no warranty that whole data chunk will get to you at once
It looks like you have a timing issue. Since your client and server are different processes, there's no way you can guarantee that the entirety of TopLevelComms::connect() is being executed (along with the network data transfer) before your server's connectionAccepted() function tries to read from the socket.
I suspect that if you take advantage of QTcpSocket's waitForReadyRead() function, you should have better luck:
void Comms::connectionAccepted(){
qDebug()<<"Connected";
socket = new QTcpSocket(server->nextPendingConnection());
if( socket->waitForReadyRead() ) {
char* rec = new char[socket->readBufferSize()];
qDebug()<<socket->readBufferSize();
}
}