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();
}
}
Related
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.
I am trying to get data from a LAN-connected infrared camera.
I don't know how to approach packets from the external network.
Here's my code.
The code below was run on Qt 5.9.7 (msvc2017_64) I've changed codes from Qt UDP example. (https://www.bogotobogo.com/Qt/Qt5_QUdpSocket.php)
#include "myudp.h"
MyUDP::MyUDP(QObject *parent) :
QObject(parent)
{
// create a QUDP socket
socket = new QUdpSocket(this);
socket_cam = new QUdpSocket(this);
// The most common way to use QUdpSocket class is
// to bind to an address and port using bind()
// bool QAbstractSocket::bind(const QHostAddress & address,
// quint16 port = 0, BindMode mode = DefaultForPlatform)
socket->bind(QHostAddress::LocalHost, 1234);
connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead()));
}
void MyUDP::HelloUDP()
{
QByteArray Data;
QHostAddress camera_loc = QHostAddress("192.168.10.197");
quint16 cameraPort = 32197;
qint64 deg_num = socket->readDatagram(Data.data() ,964, &camera_loc,
&cameraPort);
qDebug() << "Deg_num: " << deg_num; //this returns -1. Therefore, it
seems it can't read any data from camera_loc.
// Sends the datagram datagram
// to the host address and at port.
// qint64 QUdpSocket::writeDatagram(const QByteArray & datagram,
// const QHostAddress & host, quint16 port)
socket->writeDatagram(Data, QHostAddress::LocalHost, 1234);
}
void MyUDP::readyRead()
{
// when data comes in
QByteArray buffer;
buffer.resize(socket->pendingDatagramSize());
QHostAddress sender = QHostAddress("192.168.10.197");
quint16 senderPort = 32197;
// qint64 QUdpSocket::readDatagram(char * data, qint64 maxSize,
// QHostAddress * address = 0, quint16 * port = 0)
// Receives a datagram no larger than maxSize bytes and stores it
// The sender's host address and port is stored in *address and *port
// (unless the pointers are 0).
socket->readDatagram(buffer.data(), buffer.size(),
&sender, &senderPort);
qDebug() << "Message from: " << sender.toString();
qDebug() << "Message port: " << senderPort;
qDebug() << "Message: " << buffer;
}
As I've seen on Wireshark, packets arrived correctly as I expected.
However, my code doesn't work as I expected. (readDatagram on camera_loc returns (-1))
According to other threads, we don't need to connect them because UDP communication doesn't need to make connections.
What I want to make with this code is as follow.
(0. Save data from the camera (192.168.10.197) on a variable using readDatagram) I am not sure this is a really necessary process...
1. Write data to buffer as written in this code (using writeDatagram function).
I could not find solutions even if I struggled.
I thought it would be easy but it wasn't ...
Any advice will be very grateful because I am a newbie to qt and UDP network.
Thanks in advance.
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
}
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.
I am writing a client in C++ and a server in Python.
The server accepts the connection from the client, and sends to the client its player ID number, formated for the regular expression "id\s\d". (e.g. "id 3")
if s is serversocket:
print "Listening..."
if accept_connection or nb_player < 5:
connection, client_address = s.accept();
print 'New connection from ', client_address
connection.setblocking(0)
inputs.append(connection)
# Send player I to new connection
connection.send("id "+str(len(inputs)-1))
The client initializes its socket, and connect. I implemented connected() to display a message on the GUI, if it is emitted. It is emitted without problem. Same thing on the server side, I receive the connection without issues.
Window::Window(QWidget *parent) :
QDialog(parent),
ui(new Ui::Window)
{
ui->setupUi(this);
/* Initialize socket */
socket = new QTcpSocket(this);
socket->connectToHost("localhost", 13456);
connect(socket, SIGNAL(readyRead()), this, SLOT(data_received()));
connect(socket, SIGNAL(connected()), this, SLOT(connected()));
}
The server receives data from the client without problem. It is the client that does not receive the information correctly.
void Window::data_received(){
QRegExp id_re("id\\s(\\d)");
while (socket->canReadLine()){
/* Read line in socket (UTF-8 for accents)*/
ui->log->append("listening...");
QString line = QString::fromUtf8(socket->readLine()).trimmed();
/* Player ID returned by server */
if ( id_re.indexIn(line) != -1){
//Test
ui->log->append("The ID arrived");
//Extract ID
QString id_str = id_re.cap(1);
//Put in data structure of player
player->set_player_id(id_str);
//Display message
ui->log->append(QString("You are Player "+ player->get_player_id()));
}
}
}
get_player_id() returns a QString
I targeted my problem down, and it seems that canReadLine() is never returning true, therefore I can never read it. What could cause that?
It is because canReadLine() looks for "\n". Python does not automatically add it, therefore, there was no end-of-line to my string of character. Simply adding "\n" in the Python code solved my problem.