Yo'Sarin bloguje

Přeskočit navigaci

  • Všechny sekce
    (116 článků)
  • Fotogalerie
    (14 článků)
  • PC
    (15 článků)
  • Jen tak
    (58 článků)
  • Povídky
    (4 články)
  • O bloku
    (7 článků)
  • Škola
    (9 článků)
  • Hádanky
    (3 články)
  • Štafety
    (6 článků)
  • Přihlášení
  • Přepínač stylů

  • Semestrálka X36PKO - protokol UDP

    29. 5. 2008, 18:00 napsal Yo'Sarin, sekce Škola, přečteno 737×, Linkuj si !

    Druhá semestrálka na X36PKO, tentokrát přenášení souborů přes protokol UDP, tentokrát pod Windows a tentokrát za pomoci Qt. Malé info - klient má jen jeden timer (potřeba by byly dva), což snižuje hodnotu semestrálky o deset bodů (tzn. na 20). Možná jsou tam i nějaké další bugíky (kromě toho že je to úděsně pomalé).

    kompletní projekt (300 kB) pro super IDE Code::Blocks.

    Server:

    server_main.cpp

    #include <QApplication>
    #include "server.h"

    // main
    int main(int argc, char* argv[])
    {    QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
    QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
    QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));

    QApplication app(argc, argv);

    uint port;
    bool ok;
    if (argc > 0) {    port = QString(argv[0]).toUInt(&ok);
    }
    if (!ok) {    port = 45454;
    }

    Server * s = new Server(port);
    s->show();

    return app.exec();
    }

    server.h

    #ifndef SERVER_H_INCLUDED
    #define SERVER_H_INCLUDED

    #include <QPushButton>
    #include <QTextEdit>
    #include <QString>
    #include <QWidget>
    #include <QHBoxLayout>

    #include <QtNetwork>

    class Server : public QWidget
    {    Q_OBJECT

    public:
    enum State { INIT, RECIEVING, TRANSFER_END } ;
    Server(uint port, QWidget * parent = 0, Qt::WindowFlags f = 0);
    ~Server();

    public slots:
    void packetRecieved();
    void complete();
    void connectionClose();
    void sendLastPacketInRow();
    void sendConfirm();

    private:
    void init();

    State curState;
    uint port;
    uint packetsCount;
    uint fileSize;
    uint recievedPackets;
    uint dataFrom;
    uint bufferStart;
    QHostAddress client;
    uint clientPort;
    QString fileName;
    QFile * file;
    int buffer[4];
    QByteArray dataBuffer[4];
    bool validBuffer[4];
    QUdpSocket * sock;
    QTextEdit * status;
    QTimer * timer, *firstTimer;
    } ;


    #endif // SERVER_H_INCLUDED

    server.cpp

    #include "server.h"
    #include <math.h>

    // definice metod třídy server
    Server::Server(uint p, QWidget * parent , Qt::WindowFlags f)
    : QWidget(parent, f)
    {    port = p;

    timer = new QTimer(this);

    firstTimer = new QTimer(this);
    connect(firstTimer, SIGNAL(timeout()), this, SLOT(sendConfirm()));

    status = new QTextEdit();
    status->setReadOnly(true);

    QHBoxLayout * layout = new QHBoxLayout;
    layout->addWidget(status);
    setLayout(layout);

    status->show();

    sock = new QUdpSocket(this);
    sock->bind(QHostAddress::LocalHost, port);
    connect(sock, SIGNAL(readyRead()), this, SLOT(packetRecieved()));
    connect(sock, SIGNAL(disconnected()), this, SLOT(connectionClose()));

    status->append(tr("Server poslouchá na %1:%2")
    .arg(sock->localAddress().toString())
    .arg(sock->localPort())
    );

    init();
    }

    Server::~Server()
    {    }

    void Server::init()
    {    status->append(tr("Čekám na příjem dat (port %1)...").arg(port));

    fileName = "";
    client = 0;
    clientPort = 0;

    packetsCount = 0;
    fileSize = 0;
    recievedPackets = 0;
    dataFrom = 0;
    bufferStart = 0;

    curState = INIT;
    file = NULL;
    }

    void Server::sendLastPacketInRow()
    {    int lastPacket = -2;
    for (int k = 0; k < 4 && lastPacket == -2; k++) {    if (!validBuffer[(bufferStart + k) % 4]) {    lastPacket = buffer[(bufferStart + k) % 4] - 1;
    }
    }
    if (lastPacket == -2) {    lastPacket = buffer[(bufferStart + 3) % 4];
    } else if (lastPacket <= 0) {    lastPacket = 1;
    }

    status->append(tr("Posílám poslední packet před gapem: %1").arg(lastPacket));

    QByteArray send = QByteArray::number(lastPacket);
    sock->writeDatagram(send.data(), send.size(), client, clientPort);

    if (lastPacket == packetsCount) {    complete();
    }
    }



    //SLOTS

    void Server::packetRecieved()
    {    switch (curState) {    case INIT: {    QByteArray info;
    while (sock->hasPendingDatagrams()) {    QByteArray recieved;
    recieved.resize(sock->pendingDatagramSize());
    QHostAddress sender;
    quint16 senderPort;
    sock->readDatagram(recieved.data(), recieved.size(),
    &sender, &senderPort);
    if (clientPort == 0) {    client = sender;
    clientPort = senderPort;
    }
    status->append(tr("Odesílatel: %1:%2")
    .arg(client.toString())
    .arg(clientPort)
    );
    if (client != sender || clientPort != senderPort) {    continue;
    }
    if(recieved.data()[0] == 'S') {    info = recieved;
    recieved.remove(0, 1);
    int place = recieved.indexOf('|');
    fileSize = recieved.mid(0, place).toUInt();
    packetsCount = ceil(1 + double(fileSize) / double(128 - 4));
    dataFrom = 4;
    fileName = recieved.mid(place+1).data();
    status->append(tr("<strong>Přijato info:</strong>"));
    status->append(tr("%4 => %1 (%2 kb, %3 paketů)")
    .arg(fileName)
    .arg(fileSize)
    .arg(packetsCount)
    .arg(recieved.data())
    );

    if (file == NULL) {    file = new QFile(tr("transported/%1").arg(fileName), this);
    if (!file->open(QIODevice::WriteOnly)) {    exit(1);
    }
    }
    sendConfirm();
    } else if (fileName != "" && packetsCount != 0) {    status->append("Postupuju dál");
    curState = RECIEVING;
    break;
    }
    }
    if (curState == INIT) {    break;
    }
    else if (fileName != "" && packetsCount != 0) {    status->append(tr("Navázáno spojení (z %3 - [%1:%2])")
    .arg(client.toString())
    .arg(clientPort)
    .arg(sock->peerName())
    );
    buffer[0] = 1; validBuffer[0] = false;
    buffer[1] = 2; validBuffer[1] = false;
    buffer[2] = 3; validBuffer[2] = false;
    buffer[3] = 4; validBuffer[3] = false;
    }
    }

    case RECIEVING: {    while (sock->hasPendingDatagrams()) {    char recieved[128];
    QHostAddress sender;
    quint16 senderPort;
    sock->readDatagram(recieved, 128,
    &sender, &senderPort);;
    if (client != sender || clientPort != senderPort) {    continue;
    }
    quint64 packetNumber = 0;
    packetNumber = quint8(recieved[0]) * 255 * 255 * 255;
    packetNumber += quint8(recieved[1]) * 255 * 255;
    packetNumber += quint8(recieved[2]) * 255;
    packetNumber += quint8(recieved[3]);
    if (packetNumber >= buffer[bufferStart] && packetNumber <= buffer[bufferStart] + 3) {    quint64 index = (bufferStart - buffer[bufferStart] + packetNumber) % 4;
    if (packetNumber != buffer[index]) {    status->append(tr("Chyba bufferu očekávaných packetů"));
    } else /*    if (dataBuffer[index] != "") */ {    validBuffer[index] = true;
    for (int k = 0; k < 124; k++) {    dataBuffer[index][k] = recieved[dataFrom + k];
    }
    sendLastPacketInRow();
    status->append(tr("Přijat paket %1")
    .arg(packetNumber)
    );
    }
    status->append(tr("Buffer: [%1,%2,%3,%4]")
    .arg(!validBuffer[0] ? tr("<span style='background-color:#ffcccc;'>%1</span>")
    .arg(buffer[0])
    : tr("<span style='background-color:#ccffcc;'>%1</span>")
    .arg(buffer[0]))
    .arg(!validBuffer[1] ? tr("<span style='background-color:#ffcccc;'>%1</span>")
    .arg(buffer[1])
    : tr("<span style='background-color:#ccffcc;'>%1</span>")
    .arg(buffer[1]))
    .arg(!validBuffer[2] ? tr("<span style='background-color:#ffcccc;'>%1</span>")
    .arg(buffer[2])
    : tr("<span style='background-color:#ccffcc;'>%1</span>")
    .arg(buffer[2]))
    .arg(!validBuffer[3] ? tr("<span style='background-color:#ffcccc;'>%1</span>")
    .arg(buffer[3])
    : tr("<span style='background-color:#ccffcc;'>%1</span>")
    .arg(buffer[3]))
    );
    }
    while (validBuffer[bufferStart]) {    if (buffer[bufferStart] == packetsCount - 1) {    qint8 size = 124 - (124 * (packetsCount-1) - fileSize);
    status->append(tr("Poslední paket má %1").arg(size));
    file->write(dataBuffer[bufferStart], size);
    } else {    file->write(dataBuffer[bufferStart], 124);
    }
    validBuffer[bufferStart] = false;
    buffer[bufferStart] = buffer[(bufferStart + 3) % 4] + 1;
    bufferStart = (bufferStart + 1) % 4;
    }
    break;
    }
    }

    default: {    break; // nop
    }
    }
    }

    void Server::sendConfirm()
    {    QByteArray send = "I_OK";
    status->append(tr("Posílám potvrzení [%3] na %1:%2")
    .arg(client.toString())
    .arg(clientPort)
    .arg(send.data())
    );
    sock->writeDatagram(send.data(), send.size(), client, clientPort);
    }

    void Server::connectionClose()
    {    //disconnect(this, SLOT(connectionClose()));
    status->append(tr("\n--------------\n\n"));
    init();
    }

    void Server::complete()
    {    curState = TRANSFER_END;
    status->append("Přenos byl dokončen");
    file->close();
    //disconnect(this, SLOT(packetRecieved));
    //sock->disconnect();
    sock->disconnectFromHost();
    }

    Klient:

    klient_main.cpp

    #include <QApplication>
    #include "klient.h"

    int main(int argc, char* argv[])
    {    QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
    QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
    QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));

    QApplication app(argc, argv);

    uint port = 45454;
    QHostAddress add;
    bool ok;
    bool ok2 = false;
    if (argc > 1) {    port = QString(argv[1]).toUInt(&ok);
    ok2 = add.setAddress(QString(argv[0]));
    }
    if (!ok) {    port = 45454;
    }
    if (!ok2) {    if (!add.setAddress(QString("127.0.0.1"))) {    return 1;
    }
    }


    Client * c = new Client(add, port);
    c->show();

    return app.exec();
    }

    klient.h

    #ifndef KLIENT_H_INCLUDED
    #define KLIENT_H_INCLUDED

    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>

    #include <QPushButton>
    #include <QTextEdit>
    #include <QString>
    #include <QWidget>
    #include <QHBoxLayout>
    #include <QFileDialog>
    #include <QFileInfo>

    #include <QtNetwork>

    class Client : public QWidget
    {    Q_OBJECT

    public:
    enum State { INIT, TRANSFER_PREPARING, SENDING, TRANSFER_END } ;
    Client(QHostAddress addr, uint port, QWidget * parent = 0, Qt::WindowFlags f = 0);
    ~Client();

    public slots:
    void sendFile();
    void sendFileInfo();
    void prepareSendFile();
    void browseFile();
    void confirmationRecieved();
    void connectionClose();

    private:
    void init();
    void sendPacket();
    void aproximateLength();

    QByteArray getLine();

    bool infoSended;

    State curState;
    uint packetsCount;
    uint recievedPackets;
    uint dataFrom;
    uint bufferStart;
    QByteArray fileData;
    QHostAddress server;
    uint serverPort;
    QFile * file;

    int buffer[4];
    QByteArray dataBuffer[4];

    QUdpSocket * sock;
    QTextEdit * status;
    QTimer * timer;

    QAction * openFile;

    QPushButton * send;
    QPushButton * open;
    } ;


    #endif // KLIENT_H_INCLUDED

    klient.cpp

    #include "klient.h"
    #include <math.h>

    Client::Client(QHostAddress addr, uint p, QWidget * parent , Qt::WindowFlags f)
    : QWidget(parent, f)
    {    srand(time(NULL));
    dataBuffer[0].resize(124);
    dataBuffer[1].resize(124);
    dataBuffer[2].resize(124);
    dataBuffer[3].resize(124);

    serverPort = p;
    server = addr;
    sock = new QUdpSocket(this);
    sock->bind(QHostAddress::LocalHost, serverPort + 1);
    connect(sock, SIGNAL(readyRead()), this, SLOT(confirmationRecieved()));
    connect(sock, SIGNAL(disconnected()), this, SLOT(connectionClose()));

    status = new QTextEdit();
    status->setReadOnly(true);

    timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(sendFile()));

    open = new QPushButton(tr("&Procházet soubory..."));
    connect(open, SIGNAL(clicked()), this, SLOT(browseFile()));
    open->show();

    send = new QPushButton(tr("&Poslat soubor"));
    connect(send, SIGNAL(clicked()), this, SLOT(prepareSendFile()));
    send->setDisabled(true);
    send->show();

    QHBoxLayout * layout = new QHBoxLayout;
    layout->addWidget(status);
    layout->addWidget(open);
    layout->addWidget(send);

    setLayout(layout);

    status->show();

    init();
    }

    Client::~Client()
    {    }

    void Client::aproximateLength()
    {    dataFrom = 4;
    qint64 fileSize = file->size();
    packetsCount = ceil(1 + double(fileSize) / double(128 - 4));
    // x = 1023 * n - n * floor(log(n)/log(2))
    }

    void Client::sendPacket()
    {    }

    void Client::init()
    {    curState = INIT;
    status->append(tr("Klient poslouchá na %1:%2")
    .arg(server.toString())
    .arg(serverPort + 1)
    );
    infoSended = false;

    bufferStart = 0;
    }

    void Client::browseFile()
    {    QFileDialog::Options options;
    QString selectedFilter;
    QString fileName = QFileDialog::getOpenFileName(this,
    tr("Vyberte soubor pro přenesení"),
    QString(),
    tr("Všechny soubory (*.*)"),
    &selectedFilter,
    options);
    if (!fileName.isEmpty()) {    file = new QFile(fileName, this);
    if (!file->open(QIODevice::ReadOnly)) {    return;
    }
    send->setDisabled(false);
    }
    }

    void Client::sendFileInfo()
    {    if (!infoSended) {    QByteArray send = "S" + QByteArray::number(file->size());
    QFileInfo i(file->fileName());
    send += "|" + i.fileName().toUtf8();
    status->append(tr("Posílám info (%1)").arg(send.data()));
    sock->writeDatagram(send.data(), send.size(), server, serverPort);
    }
    }

    void Client::prepareSendFile()
    {    curState = TRANSFER_PREPARING;
    aproximateLength();
    status->append(tr("Posílám informace o souboru:"));
    status->append(tr("%1").arg(file->fileName()));
    status->append(tr("%1 Bytů, %2 paketů")
    .arg(file->size())
    .arg(packetsCount)
    );
    connect(timer, SIGNAL(timeout()), this, SLOT(sendFileInfo()));
    sendFileInfo();
    timer->start(100);
    }

    void Client::sendFile()
    {    for (int k = 0; k < 4 && buffer[(bufferStart + k) % 4] <= packetsCount; k++) {    if (buffer[(bufferStart + k) % 4] > 0) {    uint value = buffer[(bufferStart + k) % 4];
    status->append(tr("Posílám paket %1")
    .arg(value)
    );
    QByteArray data;
    data.resize(4);
    data.fill(0);
    data[0] = (quint8(value / (255*255*255)));
    value %= 255*255*255;
    data[1] = (quint8(value / (255*255)));
    value %= 255*255;
    data[2] = (quint8(value / (255)));
    value %= 255;
    data[3] = (quint8(value));
    data.append(dataBuffer[(bufferStart + k) % 4]);
    int k = rand() % 100 + 1;
    if (k <= 70)
    sock->writeDatagram(data.data(), data.size(), server, serverPort);
    else
    status->append(tr("<span style='color: red; margin-left: 20px;'>Paket %1 se ztratil</span>").arg(value));
    }
    }
    timer->start(1000);
    }

    void Client::confirmationRecieved()
    {    switch (curState) {    case TRANSFER_PREPARING: {    while (sock->hasPendingDatagrams() && !infoSended) {    QByteArray recieved;
    recieved.resize(sock->pendingDatagramSize());
    sock->readDatagram(recieved.data(), recieved.size());
    if (server != sock->peerAddress() || serverPort != sock->peerPort()) {    //continue;
    }
    if (QString(recieved.data()) == QString("I_OK")) {    status->append(tr("Dorazilo potvrzení o zpracování informací o souboru."));
    infoSended = true;
    }
    if (infoSended) {    disconnect(this, SLOT(sendFileInfo()));
    curState = SENDING;
    buffer[0] = 1; dataBuffer[0] = getLine();
    buffer[1] = 2; dataBuffer[1] = getLine();
    buffer[2] = 3; dataBuffer[2] = getLine();
    buffer[3] = 4; dataBuffer[3] = getLine();
    connect(timer, SIGNAL(timeout()), this, SLOT(sendFile()));
    }
    }
    break;
    }
    case SENDING: {    while (sock->hasPendingDatagrams()) {    QByteArray recieved;
    recieved.resize(sock->pendingDatagramSize());
    sock->readDatagram(recieved.data(), recieved.size());
    uint id = recieved.toUInt();
    status->append(tr("Přijato (SENDING): %1 (buffer[%3] = %2)")
    .arg(recieved.data())
    .arg(buffer[bufferStart])
    .arg(bufferStart)
    );
    if (id >= buffer[bufferStart] && id <= buffer[bufferStart] + 3) {    timer->stop();
    int index = (bufferStart - buffer[bufferStart] + id) % 4;
    while (bufferStart != index && buffer[bufferStart] <= packetsCount) {    int backup = buffer[bufferStart];
    buffer[bufferStart] = -1;
    dataBuffer[bufferStart] = getLine();
    buffer[bufferStart] = backup + 4;
    bufferStart = (bufferStart + 1) %4;
    }
    status->append(tr("Potvrzen paket %1 (buffer: [%2, %3, %4, %5])")
    .arg(id)
    .arg(buffer[0])
    .arg(buffer[1])
    .arg(buffer[2])
    .arg(buffer[3])
    );
    if (buffer[bufferStart] == packetsCount) {    sock->disconnectFromHost();
    } else {    sendFile();
    }
    }
    }
    break;
    }
    default:
    ;
    }
    }

    QByteArray Client::getLine()
    {    QByteArray data;
    char c;
    for (int k = 0; k < 124; k++) {    file->getChar(&c);
    data.append(c);
    }
    return data;
    }

    void Client::connectionClose()
    {    init();
    }

    Diskuze k článku:

    Diskusní příspěvky vyjadřují názory diskutujících, nikoli autora článku.
    Příspěvky nemající souvislost s článkem a příspěvky jejichž jediným účelem je urážet a nadávat budou po zralé úvaze smazány - uvědomte si, že jste na mém písečku.

    #1: Petule jezule, přidáno: 30. 5. 2008, 19:03

    Avatar uživatele Petule jezuleAhojky..mno prekvapilo me,ze si se ozval..ze sis vzpomnel..mno maturuju tedka v pondeli..takze dekuji za pranicko..praktickou mam uz za sebou..a pisemna moc dobre nedopadla,ale uz je to za mnou..Jinak nemam kredit (myslim,ze te to ani neprekvapuje..:-D) a tak se ozyvam aspon touto cestou..Pres celej svatak sem byla na chate - takze pristup k netu byl nulovy...Doufam,ze se ti taky lip dari nez kdyz sme byli spolu.. a ze se vlastne porad usmivas.. :-) Jeste jednou dekuji za prani k maturite (jo a uz sem prijata na VS - masarna i mendlovka) Tak se mej krasne..


    Přidat nový příspěvek




    Yo'Sarinkovy stránky Blog o všem, co jste už četli jinde Jediný blog se zápornou návštěvností No-more-Didinka I admini mají své dny
    Yo'Sarinkův blok I RSS tu mám Pagerank? Co s ním.
    http://blok.yosarin.net/sekce-8~Skola/clanek-101~Semestralka-X36PKO-protokol-UDP