4

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);
}
Krzysztof Jackowski
  • 170
  • 1
  • 3
  • 10

2 Answers2

6

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.

undercover
  • 176
  • 4
  • Thanks a lot for your input! I've manage to do it but right now I have another problem. My on_readyRead() is right now like this in client and server: if(socket->canReadLine()){ QByteArray ba = socket->readLine(); printf(">> %s\n", ba.constData()); char buffer[] = "server to client"; socket->write(buffer); } The problem is when I would like to send response from client to server only optionally. The app stops and waits for data from client and readyRead(). My guess is that I should move writing on server side somewhere else. What would be the best place for it? – Krzysztof Jackowski Jun 19 '13 at 09:47
  • 1
    @lycha90 Your app will not stop while waiting for readyRead() signal. When there is some ready-to-read data in socket it sends signal, then your on_readyRead() slot gets called. You read data, send response (or not if it's not necessary) and control goes back to main event loop, where your application can process further events. In QT it's called a "non-blocking" behaviour. – undercover Jun 19 '13 at 10:05
  • ok I understand. The thing is that I need server to send data all the time and client should send only occasionally. So I cannot write on server side in on_readyRead() method because it will execute only when client will send something. The question is where should I write data on server side? Is there any slot that I can use for this problem? – Krzysztof Jackowski Jun 19 '13 at 11:49
  • @lycha90 There is no let's say "generic" solution. First of all it depends on what will be your data provider. In simple case your server could be a data provider itself. Then you would have to store socket recieved in on_newConnection() slot i.e. as a member of server, implement data collecting functionality and write collected data to stored socket. Better solution would be separate data provider inherited from QObject. You could implement communication between them using signal-slot model. – undercover Jun 19 '13 at 12:06
  • Thanks for your time and help! I guess signal-slot model will do work for me. – Krzysztof Jackowski Jun 19 '13 at 12:17
1

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.

Community
  • 1
  • 1
Pete
  • 4,784
  • 26
  • 33