-
Notifications
You must be signed in to change notification settings - Fork 0
/
lemonplusconnection.cpp
184 lines (165 loc) · 6.47 KB
/
lemonplusconnection.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#include "lemonplusconnection.h"
LemonPlusConnection::LemonPlusConnection(QObject *parent, QPair<QString, QHostAddress> &srv, QDir dir): QObject(parent)
{
logger = new Logger();
this->server = srv;
this->dir = dir;
}
LemonPlusConnection::~LemonPlusConnection()
{
if(logger != nullptr) delete logger;
if(tcp != nullptr) delete tcp;
if(stream != nullptr) delete stream;
}
void LemonPlusConnection::startConnection()
{
tcp = new QTcpSocket();
tcp->connectToHost(server.second, 53667);
connectionTimer = static_cast<QSharedPointer<QTimer> >(new QTimer());
connectionTimer->setSingleShot(true);
connectionTimer->setInterval(4980);
connectionTimer->start();
connect(&(*connectionTimer), SIGNAL(timeout()), this, SLOT(checkConnection()));
stream = new QDataStream(tcp);
stream->setVersion(QDataStream::Qt_5_7);
connect(tcp, SIGNAL(connected()), this, SLOT(doRegister()));
connect(tcp, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
connect(tcp, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onTcpError(QAbstractSocket::SocketError)));
}
void LemonPlusConnection::doRegister()
{
(*stream) << uniqueId << QString("REGISTER") << dir.dirName();
tcp->flush();
}
void LemonPlusConnection::checkConnection()
{
if(!online) {
emit logger->log(tr("Cannot connect to the server. Maybe the contest hasn't been loaded."), Logger::FATAL);
tcp->disconnectFromHost();
this->thread()->quit();
}
}
void LemonPlusConnection::onReadyRead()
{
while(true) {
stream->startTransaction();
this->readCmd();
if(stream->commitTransaction()) {
qDebug() << "commited";
} else break;
}
}
void LemonPlusConnection::readCmd()
{
QString id, cmd;
(*stream) >> id >> cmd;
if(stream->status() != stream->Ok) return;
qDebug() << "SERVER CMD : " << id << cmd;
if(id != uniqueId) {
online = false;
emit logger->log(tr("Client cannot understand the protocal used by this server."), Logger::FATAL);
tcp->disconnectFromHost();
this->thread()->quit();
return;
}
if(cmd == "OK_CONNECTION_ESTABLISHED") {
if(online) {
emit logger->log(tr("Server is attempting to establish more than one connection."), Logger::WARNING);
return;
} else {
online = true;
emit logger->log(tr("Connected to Lemon+ contest server."), Logger::SUCCESS);
return;
}
} else if(cmd == "ERR_NAME_USED") {
online = false;
emit logger->log(tr("Contestant name already used.") + " " + tr("Try renaming the contestant folder."), Logger::FATAL);
tcp->disconnectFromHost();
this->thread()->quit();
return;
} else if(cmd == "ERR_INVALID_NAME") {
online = false;
emit logger->log(tr("Contestant name invalid.") + " " + tr("Try renaming the contestant folder."), Logger::FATAL);
tcp->disconnectFromHost();
this->thread()->quit();
return;
} else if(cmd == "OK_GOT_CODE") {
emit logger->log(tr("The source code folder has been successfully uploaded."), Logger::SUCCESS);
return;
} else if(cmd == "COLLECT_CODE") {
// TODO:
// 1. zip
// 2. fragment size = 1024byte
// 3. upload per fragment; size:
// [size] -1 -1 -1 ...
emit logger->log(tr("Server has requested the client to upload the contestant's code."), Logger::INFO);
emit logger->log(tr("Compressing the code..."), Logger::INFO);
emit setProgress(0);
tmpZipName = QDir::toNativeSeparators(QDir::tempPath()) + QDir::separator()
+ QString("LemonPlusClient_ContestantCode_%1").arg(QRandomGenerator::system()->generate())
+ ".zip";
{
int i;
for(i = 0; i < COMPRESSION_RETRY_TIMES; i++) {
if(!JlCompress::compressDir(tmpZipName, dir.absolutePath())) {
emit logger->log(tr("Compression failed! Retry in %1 seconds... Close any program that uses the files.")
.arg(COMPRESSION_RETRY_WAIT), Logger::ERROR);
this->thread()->sleep(COMPRESSION_RETRY_WAIT);
} else break;
}
if(i == COMPRESSION_RETRY_TIMES) {
emit logger->log(tr("All attempts of compression failed!"), Logger::FATAL);
tcp->disconnectFromHost();
this->thread()->quit();
return;
}
emit logger->log(tr("Source code compressed."), Logger::INFO);
}
qDebug() << tmpZipName;
QSharedPointer<QFile> tmpZip =
QSharedPointer<QFile>(new QFile(tmpZipName));
bool firstPacket = true;
qint64 bytesWritten = 0, tmpZipSize = 0;
tmpZip->open(QIODevice::ReadOnly);
tmpZipSize = tmpZip->size();
tmpZip->seek(0);
emit logger->log(tr("Compressed file contains %1 bytes.").arg(tmpZipSize), Logger::INFO);
tcp->flush();
QByteArray buf;
while((buf = tmpZip->read(BYTES_PER_PACKET)).size() != 0) {
(*stream) << uniqueId << QString("CODE_PAYLOAD");
if(firstPacket) {
(*stream) << tmpZipSize;
firstPacket = false;
emit logger->log(tr("Now uploading files..."), Logger::INFO);
} else {
(*stream) << qint64(-1);
}
(*stream) << buf;
if(stream->status() == QDataStream::Ok) {
// ok
tcp->flush();
bytesWritten += buf.size();
buf.clear();
emit setProgress(bytesWritten * 100 / tmpZipSize);
} else {
emit setProgress(0);
emit logger->log(tr("Failed to write data at %1 byte. Broken pipe.")
.arg(tmpZip->pos()), Logger::FATAL);
tcp->disconnectFromHost();
this->thread()->quit();
return;
}
}
tcp->flush(); // flush again to avoid failure
tmpZip->close();
emit logger->log(tr("The contestant's code has been sent to the server. Waiting for reply..."), Logger::INFO);
}
}
void LemonPlusConnection::onTcpError(QAbstractSocket::SocketError)
{
online = false;
emit logger->log(tr("TCP Error! Broken pipe. Maybe the server has closed the connection."), Logger::FATAL);
tcp->disconnectFromHost();
this->thread()->quit();
}