29. 5. 2008, 18:00 napsal Yo'Sarin, sekce Škola, přečteno 911×,
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.
#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();
}
#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
#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();
}
#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();
}
#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
#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();
}
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
Ahojky..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..) 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..