Mostrando entradas con la etiqueta udp. Mostrar todas las entradas
Mostrando entradas con la etiqueta udp. Mostrar todas las entradas

lunes, 25 de julio de 2011

Servidor simple de echo UDP

Ya tengo listo el servidor UDP de echo, la verdad es que las herramientas que nos brindan las librerías Qt son muy potentes y fáciles de usar.

echoserver.h
#ifndef ECHOSERVER_H
#define ECHOSERVER_H

#include "IForm.h"
#include 
#include 

namespace Ui {
    class EchoServer;
}

class EchoServer : public QWidget
{
    Q_OBJECT

public:
    explicit EchoServer(QWidget *parent = 0);
    virtual ~EchoServer();
    static const quint16 PORT_MIN;
    static const quint16 PORT_MAX;
    static const quint16 PORT_DEFAULT;

protected:
    QUdpSocket *udpServer;

    virtual void putTexts();
    virtual void setuid();

    static const QString START;
    static const QString STOP;

public slots:
    void onClickStart();
    void readPendingDatagrams();

private:
    Ui::EchoServer *ui;
    bool started;

};

#endif // ECHOSERVER_H

echoserver.cpp
#include "echoserver.h"
#include "ui_echoserver.h"
#include 

const quint16 EchoServer::PORT_MIN = 1;
const quint16 EchoServer::PORT_MAX = 65535;
const quint16 EchoServer::PORT_DEFAULT = 8888;

const QString EchoServer::START = tr("Start");
const QString EchoServer::STOP = tr("Stop");

EchoServer::EchoServer(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::EchoServer)
{
    ui->setupUi(this);
    this->udpServer = new QUdpSocket(this);
    this->started = false;
    putTexts();
    setuid();    
}

EchoServer::~EchoServer()
{
    delete ui;
    delete this->udpServer;
    qDebug("Delete EchoServer");
}

void EchoServer::putTexts()
{
    this->setWindowTitle(tr("Servidor echo UDP"));
    ui->lbPort->setText(tr("Port"));
    ui->pbStart->setText(tr("Start"));
}

void EchoServer::setuid()
{
    setAttribute(Qt::WA_DeleteOnClose);
    connect(ui->pbStart, SIGNAL(clicked()),SLOT(onClickStart()));
    connect(ui->sldPort, SIGNAL(valueChanged(int)), ui->lcdPort, SLOT(display(int)));

    ui->sldPort->setMinimum(EchoServer::PORT_MIN);
    ui->sldPort->setMaximum(EchoServer::PORT_MAX);
    ui->sldPort->setValue(EchoServer::PORT_DEFAULT);
}

void EchoServer::onClickStart()
{

    if(!this->started)
    {
        qDebug("Server started");
        quint16 port = ui->lcdPort->value();
        udpServer->bind(port, QUdpSocket::ShareAddress);
        connect(udpServer, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams()));
        ui->pbStart->setText(STOP);
    } else
    {
        qDebug("Server stoped");
        udpServer->close();
        ui->pbStart->setText(START);
    }

    this->started = !this->started;
}

void EchoServer::readPendingDatagrams()
{
    qDebug("readPendingDatagrams");
    while (udpServer->hasPendingDatagrams())
    {
         QByteArray datagram;
         datagram.resize(udpServer->pendingDatagramSize());
         QHostAddress sender;
         quint16 senderPort;

         udpServer->readDatagram(datagram.data(), datagram.size(),
                                 &sender, &senderPort);

         QString str(datagram);
         str.append(" from " + sender.toString() + " on " + QDateTime::currentDateTime().toString()+ "\n");
         ui->textBrowser->insertPlainText(str);
         udpServer->writeDatagram(datagram, sender, senderPort);
     }
}

Para iniciar el servidor, simplemente hay que indicar el puerto por el que escuchará y el modo. Además hay que conectar la llegada de paquetes a una función, en este caso la función es readPendingDatagrams() y se ejecutará cada vez que llegue un paquete UDP.

quint16 port = ui->lcdPort->value();
udpServer->bind(port, QUdpSocket::ShareAddress);
connect(udpServer, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams()));

En la función readPendingDatagrams() es donde se realiza el procesamiento de datos y se monta la respuesta para el cliente. En este caso el procesamiento de datos consiste en sacar los datos recibidos por pantalla y la respuesta contiene exactamente los mismos datos que se han recibido.

while (udpServer->hasPendingDatagrams())
{
     QByteArray datagram;
     datagram.resize(udpServer->pendingDatagramSize());
     QHostAddress sender;
     quint16 senderPort;

     udpServer->readDatagram(datagram.data(), datagram.size(),&sender, &senderPort);

     QString str(datagram);
     str.append(" from " + sender.toString() + " on " + QDateTime::currentDateTime().toString()+ "\n");
     ui->textBrowser->insertPlainText(str);
     udpServer->writeDatagram(datagram, sender, senderPort);
}

Como siempre podeis descargar el código desde el svn, para esta versión en concreto desde aquí:
svn checkout http://infobasic.googlecode.com/svn/tags/infobasic-0.0.3 infobasic-read-only

sábado, 23 de julio de 2011

Analisis de datos enviados por el cliente udp.

Al capturar los datos enviado por nuestro cliente de echo UDP obteniamos la siguiente secuencia de bytes:

0x0000:  4500 0028 0000 4000 4011 3cc3 7f00 0001  E..(..@.@.<.....
0x0010:  7f00 0001 df38 22b8 0014 fe27 6865 6c6c  .....8"....'hell
0x0020:  6f20 7365 7276 6572                      o.server

Teniendo en cuenta que el encabezado de un paquete UDP es el siguiente:

puerto de origen
(16 bits)
puerto de destino
(16 bits)
longitud total
(16 bits)
suma de comprobación del encabezado
(16 bits)
datos
(longitud variable).

Tenemos que:

Puerto origen: df38 (2 bytes)
Puerto destino: 22b8 (2 bytes. 0x22b8 es 8888 en decimal)
Longitud total: 0014 (2 bytes. La longitud total es de 20 bytes, es decir 0x14 en hexadecimal)
Checksum: fe27 (2 bytes)
Datos enviados: 6865 6c6c 6f20 7365 7276 6572 (12 bytes)

Los datos anteriores corresponde al protocolo IP en el que va montada la trama UDP.

0x0000:  4500 0028 0000 4000 4011 3cc3 7f00 0001  E..(..@.@.<.....
0x0010:  7f00 0001

Y su significado es el siguiente:

Versión: 45 (4 bits 1/2 byte, 0100 significa que se está usando la versión IPV4)
Longitud del encabezado: 00 (4 bits, 1/2 byte, 0101 es decir 5 en decimal)
Tipo de servicio: 00 (1 bytes)
Longitud total: 0028 (2 bytes, corresponde a una longitude de 40 bytes)
Identificación: 0000 (2 bytes)
Fragment offset: 4000 (2 bytes)
Tiempo de vida: 40 (1 byte)
Protocolo: 11 (1 byte, corresponde al protocolo UDP 17 en decimal)
Checksum de la cabecera: 3cc3 (2 bytes)
Dirección origen: 7f00 0001 (4 bytes, 127.0.0.1)
Dirección destino: 7f00 0001 (4 bytes, 127.0.0.1)

El formato de un paquete IP es el siguiente:

Versión
(4 bits)
Longitud del encabezado
(4 bits)
Tipo de servicio
(8 bits)
Longitud total
(16 bits)
Identificación
(16 bits)
Indicador
(3 bits)
Margen del fragmento
(13 bits)
Tiempo de vida
(8 bits)
Protocolo
(8 bits)
Suma de comprobación del encabezado
(16 bits)
Dirección IP de origen (32 bits)
Dirección IP de destino (32 bits)
Datos

En los posteriores post intentaré analizar cuales son las diferencias al utilizar diferentes protocolos como TCP y diferentes puertos para el cliente de echo.

lunes, 18 de julio de 2011

Cliente simple echo. UDP.

Se trata de un cliente UDP de echo. Consta de un una caja de texto para escribir el nombre del servidor, y una otra caja de texto para enviar texto en claro al servidor. Aquí dejo la implementación de la clase principal.

echoclient.h
#ifndef ECHOCLIENT_H
#define ECHOCLIENT_H

#include < QWidget>
#include "IForm.h"

namespace Ui {
    class EchoClient;
}

class EchoClient : public QWidget, public IForm
{
    Q_OBJECT

public:
    explicit EchoClient(QWidget *parent = 0);
    virtual ~EchoClient();

    virtual void putTexts();
    virtual void setuid();

public slots:
    void onClickSend();

private:
    Ui::EchoClient *ui;
};

#endif // ECHOCLIENT_H

echoclient.cpp
#include "echoclient.h"
#include "ui_echoclient.h"
#include "qtudpclient.h"
#include < QUdpSocket>
#include < QHostAddress>
#include < iostream>

EchoClient::EchoClient(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::EchoClient)
{
    ui->setupUi(this);
    putTexts();
    setuid();
}

EchoClient::~EchoClient()
{
    delete ui;
    qDebug("Delete EchoClient");
}

void EchoClient::putTexts()
{
    ui->lbServerName->setText(tr("Server Name"));
    ui->lbText->setText(tr("Text"));
    ui->leServerName->setText("localhost");
    ui->pbSend->setText(tr("Send"));
}

void EchoClient::setuid()
{
    setAttribute(Qt::WA_DeleteOnClose);
    connect(ui->pbSend, SIGNAL(clicked()),SLOT(onClickSend()));
}

void EchoClient::onClickSend()
{
    qDebug("onClickSend");
    QUdpSocket *udp =new QUdpSocket(this);
    const QHostAddress *host = new QHostAddress(ui->leServerName->text());

    //const char *txt = (const char*) ui->leText->text().toLocal8Bit().data();
    QByteArray ba = ui->leText->text().toLocal8Bit();
    const char *txt = ba.data();
    qint16 len = ui->leText->text().length();
    qint16 port = 8888;
    udp->writeDatagram(txt, len, QHostAddress::LocalHost,  port);
    qDebug(ui->leServerName->text().toStdString().c_str());
    delete host;
    delete udp;
}

Y aquí podemos ver la información enviada al servidor:

sudo tcpdump -xX -i lo
0x0000:  4500 0028 0000 4000 4011 3cc3 7f00 0001  E..(..@.@.<.....
0x0010:  7f00 0001 c30e 22b8 0014 fe27 6865 6c6c  ......"....'hell
0x0020:  6f20 7365 7276 6572                      o.server
En los próximos envíos veremos la interpretación de esta información y apliaremos el cliente para que sea capaz de recibir e interpretar las respuestas del servidor.