PageRenderTime 64ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/src/serve_socket.cpp

http://qipmsg.googlecode.com/
C++ | 408 lines | 299 code | 52 blank | 57 comment | 63 complexity | 3a240d5b7c81d26ae846c4843aa76834 MD5 | raw file
Possible License(s): GPL-3.0
  1. // This file is part of QIpMsg.
  2. //
  3. // QIpMsg is free software: you can redistribute it and/or modify
  4. // it under the terms of the GNU General Public License as published by
  5. // the Free Software Foundation, either version 3 of the License, or
  6. // (at your option) any later version.
  7. //
  8. // QIpMsg is distributed in the hope that it will be useful,
  9. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. // GNU General Public License for more details.
  12. //
  13. // You should have received a copy of the GNU General Public License
  14. // along with QIpMsg. If not, see <http://www.gnu.org/licenses/>.
  15. //
  16. #include "serve_socket.h"
  17. #include "constants.h"
  18. #include "transfer_codec.h"
  19. #include "send_file_map.h"
  20. #include "send_file_manager.h"
  21. #include "global.h"
  22. #include <QDir>
  23. #include <QDateTime>
  24. #include <QTextCodec>
  25. #include <unistd.h>
  26. #include <fcntl.h>
  27. #include <errno.h>
  28. #include <sys/socket.h>
  29. #include <sys/time.h>
  30. #include <sys/types.h>
  31. #define REQUST_FILE_PACKET_ID_POSITION 5
  32. #define REQUST_FILE_FILE_ID_POSITION 6
  33. #define REQUST_FILE_OFFSET_POSITION 7
  34. #define MAXBUFF 8192
  35. #define BLOCK_SIZE 1024*16
  36. struct RequsetFile
  37. {
  38. bool isFileSended;
  39. int fileType;
  40. QString filePath;
  41. qint64 offset;
  42. int fileId;
  43. };
  44. ServeSocket::ServeSocket(int socketDescriptor, QObject *parent)
  45. : QObject(parent)
  46. {
  47. m_sockfd = socketDescriptor;
  48. #if 0
  49. connect(&m_tcpSocket, SIGNAL(bytesWritten(qint64)),
  50. this, SLOT(updateBytesWrited(qint64)));
  51. connect(&m_tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)),
  52. this, SLOT(socketError(QAbstractSocket::SocketError)));
  53. #endif
  54. }
  55. ServeSocket::~ServeSocket()
  56. {
  57. close( m_sockfd );
  58. }
  59. bool ServeSocket::startSendFile()
  60. {
  61. qDebug() << "ServeSocket::startSendFile";
  62. QByteArray recvBlock;
  63. int readlen = 0;
  64. char buff[MAXBUFF];
  65. while ( 1 ) {
  66. readlen = read( m_sockfd, buff, MAXBUFF-1 );
  67. if ( errno == EINTR ) {
  68. continue;
  69. }
  70. if ( readlen <= 0 ) {
  71. return false;
  72. }
  73. buff[readlen] = '\0';
  74. recvBlock.append( buff );
  75. if ( canParsePacket(recvBlock) ) {
  76. break;
  77. }
  78. }
  79. // forever {
  80. // if (!m_tcpSocket.waitForReadyRead(3000)) {
  81. // m_errorString = m_tcpSocket.errorString();
  82. // return false;
  83. // }
  84. //
  85. // recvBlock.append(m_tcpSocket.read(m_tcpSocket.bytesAvailable()));
  86. //
  87. // if (canParsePacket(recvBlock)) {
  88. // break;
  89. // }
  90. // }
  91. return handleRequest(recvBlock);
  92. }
  93. bool ServeSocket::canParsePacket(const QByteArray &requestPacket) const
  94. {
  95. QList<QByteArray> bl = requestPacket.split(':');
  96. if (bl.count() < 8) {
  97. return false;
  98. }
  99. if (bl.count() >= 8) {
  100. // Only regular file have offset field.
  101. bool ok;
  102. int command = bl.at(MSG_FLAGS_POS).toInt(&ok, 16);
  103. if (command == IPMSG_GETFILEDATA && bl.count() < 9) {
  104. return false;
  105. }
  106. }
  107. return true;
  108. }
  109. bool ServeSocket::handleRequest(const QByteArray &requestPacket)
  110. {
  111. qDebug() << "ServeSocket::handleRequest";
  112. struct RequsetFile requestFile;
  113. parseRequestPacket(requestPacket, requestFile);
  114. SendFileMap *map = 0;
  115. Global::sendFileManager->m_lock.lock();
  116. map = Global::sendFileManager->transferFileMap.value(m_packetNoString);
  117. if (map) {
  118. map->setState(SendFileMap::Transfer);
  119. Global::sendFileManager
  120. ->transferFileModel.updateTransfer(m_packetNoString);
  121. } else {
  122. Global::sendFileManager->m_lock.unlock();
  123. return false;
  124. }
  125. map->sem.release();
  126. Global::sendFileManager->m_lock.unlock();
  127. if (!requestFile.isFileSended) {
  128. m_errorString = "ServeSocket::handleRequest: Request file not sended";
  129. goto handle_request_fail;
  130. }
  131. if (requestFile.fileType == IPMSG_FILE_REGULAR) {
  132. if (!tcpSendFile(requestFile.filePath, requestFile.offset)) {
  133. goto handle_request_fail;
  134. }
  135. } else if (requestFile.fileType == IPMSG_FILE_DIR) {
  136. if (!tcpSendDir(requestFile.filePath)) {
  137. goto handle_request_fail;
  138. }
  139. } else {
  140. // unsupported type
  141. goto handle_request_fail;
  142. }
  143. Global::sendFileManager->m_lock.lock();
  144. map->m_map[requestFile.fileId]->setState(SendFile::SendOk);
  145. map->incrTransferCount();
  146. map->setState(SendFileMap::NotTransfer);
  147. Global::sendFileManager
  148. ->transferFileModel.updateTransfer(m_packetNoString);
  149. // if transfer finished, delete transfer
  150. if (map->isFinished()) {
  151. Global::sendFileManager->removeTransfer(m_packetNoString);
  152. }
  153. Global::sendFileManager->m_lock.unlock();
  154. return true;
  155. handle_request_fail:
  156. qDebug() << "ServeSocket::handleRequest: send file error";
  157. // when this error happed, the file may have finished transfer by a
  158. // client's retry transfer, then file has been deleted from sendFileMap's
  159. // m_map.
  160. // we user handle class, so we can not test if recvFileHandle is bounded
  161. // how to test if a handle class is bounded????????????????
  162. Global::sendFileManager->m_lock.lock();
  163. map = Global::sendFileManager->transferFileMap.value(m_packetNoString);
  164. if (map) {
  165. qDebug() << "sem.available:" << map->sem.available();
  166. if (map->sem.available() == 1) {
  167. map->setState(SendFileMap::NotTransfer);
  168. Global::sendFileManager
  169. ->transferFileModel.updateTransfer(m_packetNoString);
  170. map->m_map[requestFile.fileId]->setState(SendFile::SendFail);
  171. }
  172. map->sem.acquire();
  173. }
  174. Global::sendFileManager->m_lock.unlock();
  175. return false;
  176. }
  177. void ServeSocket::parseRequestPacket(const QByteArray &requestPacket,
  178. struct RequsetFile &requestFile)
  179. {
  180. QList<QByteArray> list = requestPacket.split(':');
  181. // XXX NOTE: canParsePacket() make sure we have enough items in list,
  182. // so we do not need to check size of list before call list.at()
  183. bool ok;
  184. long id = list.at(REQUST_FILE_PACKET_ID_POSITION).toLong(&ok, 16);
  185. m_packetNoString = QString("%1").arg(id);
  186. int fileId = list.at(REQUST_FILE_FILE_ID_POSITION).toLong(&ok, 16);
  187. int command = list.at(MSG_FLAGS_POS).toInt(&ok, 16);
  188. QMutexLocker locker(&Global::sendFileManager->m_lock);
  189. SendFileMap *sendFileMap
  190. = Global::sendFileManager->transferFileMap.value(m_packetNoString);
  191. requestFile.isFileSended = false;
  192. if (sendFileMap && sendFileMap->canSendFile(fileId)) {
  193. requestFile.isFileSended = true;
  194. requestFile.fileType = sendFileMap->m_map[fileId]->type();
  195. requestFile.filePath = sendFileMap->m_map[fileId]->absoluteFilePath();
  196. requestFile.fileId = fileId;
  197. if (command == IPMSG_GETFILEDATA) {
  198. requestFile.offset
  199. = list.at(REQUST_FILE_OFFSET_POSITION).toLongLong(&ok, 16);
  200. } else {
  201. requestFile.offset = 0;
  202. }
  203. }
  204. }
  205. bool ServeSocket::tcpSendFile(QString filePath, qint64 offset)
  206. {
  207. QFile file(filePath);
  208. if (!file.open(QIODevice::ReadOnly)) {
  209. return false;
  210. }
  211. file.seek(offset);
  212. while (!file.atEnd()) {
  213. QByteArray block = file.read(BLOCK_SIZE);
  214. if (!tcpWriteBlock(block)) {
  215. return false;
  216. }
  217. }
  218. return true;
  219. }
  220. bool ServeSocket::tcpWriteBlock(QByteArray &block)
  221. {
  222. size_t nbytes = block.size();
  223. char* buff = block.data();
  224. size_t sent = 0;
  225. ssize_t n = 0;
  226. while ( sent < nbytes ) {
  227. n = send(m_sockfd, buff+sent, nbytes-sent, 0);
  228. if ( n > 0 ) {
  229. sent += n;
  230. } else if ( n < 0 ) {
  231. if ( errno == EINTR ) {
  232. continue;
  233. }
  234. return false;
  235. } else {
  236. return false;
  237. }
  238. }
  239. return true;
  240. // qint32 bytesToWrite = block.size();
  241. // forever {
  242. // qint32 n = m_tcpSocket.write(block);
  243. // if (n == -1) {
  244. // return false;
  245. // }
  246. // if (!m_tcpSocket.waitForBytesWritten(-1)) {
  247. // m_errorString = m_tcpSocket.errorString();
  248. // return false;
  249. // }
  250. // bytesToWrite -= n;
  251. // block.remove(0, n);
  252. // if (bytesToWrite == 0) {
  253. // break;
  254. // }
  255. // }
  256. // return true;
  257. }
  258. bool ServeSocket::tcpSendDir(QString filePath)
  259. {
  260. QByteArray dirBlock = constructDirSendBlock(filePath, NormalBlockMode);
  261. if (!tcpWriteBlock(dirBlock)) {
  262. return false;
  263. }
  264. QDir dir(filePath);
  265. QFileInfoList fileInfoList = dir.entryInfoList();
  266. foreach (QFileInfo fi, fileInfoList) {
  267. // Skip '.' and '..' directory
  268. if (fi.fileName() == "." || fi.fileName() == "..") {
  269. continue;
  270. }
  271. if (fi.isFile()) {
  272. QByteArray fileBlock
  273. = constructFileSendBlock(fi.absoluteFilePath());
  274. if (!tcpWriteBlock(fileBlock)) {
  275. return false;
  276. }
  277. if (!tcpSendFile(fi.absoluteFilePath(), 0)) {
  278. return false;
  279. }
  280. }
  281. if (fi.isDir()) {
  282. if (!tcpSendDir(fi.absoluteFilePath())) {
  283. return false;
  284. }
  285. }
  286. }
  287. QByteArray retParentBlock
  288. = constructDirSendBlock(filePath, RetParentBlockMode);
  289. if (!tcpWriteBlock(retParentBlock)) {
  290. return false;
  291. }
  292. return true;
  293. }
  294. QByteArray ServeSocket::constructDirSendBlock(QString filePath,
  295. DirBlockModes mode)
  296. {
  297. QFileInfo fi(filePath);
  298. QString fileName;
  299. if (mode == NormalBlockMode) {
  300. fileName = fi.fileName();
  301. // If send directory is root directory, we change its name to
  302. // "(root-directory)"
  303. if (fileName == "/") {
  304. fileName = "(root-directory)";
  305. }
  306. } else if (mode == RetParentBlockMode) {
  307. fileName = ".";
  308. }
  309. QString str(":");
  310. str.append(fileName);
  311. str.append(":");
  312. str.append(QString("%1").arg(0, TRANSFER_FILE_FILE_SIZE_LENGTH, 16, QChar('0')));
  313. str.append(":");
  314. if (mode == RetParentBlockMode) {
  315. str.append(QString("%1").arg(IPMSG_FILE_RETPARENT, 0, 16));
  316. } else if (mode == NormalBlockMode) {
  317. str.append(QString("%1").arg(IPMSG_FILE_DIR, 0, 16));
  318. }
  319. str.append(":");
  320. str.append(QString("%1=%2").arg(IPMSG_FILE_MTIME, 0, 16)
  321. .arg(fi.lastModified().toTime_t(), 0, 16));
  322. str.append(":");
  323. str.append(QString("%1=%2").arg(IPMSG_FILE_CREATETIME, 0, 16)
  324. .arg(fi.created().toTime_t(), 0, 16));
  325. str.append(":");
  326. QByteArray block = Global::transferCodec->codec()->fromUnicode(str);
  327. QString s = QString("%1").arg(block.size() + TRANSFER_FILE_HEADER_SIZE_LENGTH,
  328. TRANSFER_FILE_HEADER_SIZE_LENGTH, 16, QChar('0'));
  329. QByteArray ba = Global::transferCodec->codec()->fromUnicode(s);
  330. return ba + block;
  331. }
  332. QByteArray ServeSocket::constructFileSendBlock(QString filePath) const
  333. {
  334. QFileInfo fi(filePath);
  335. QString fileName;
  336. fileName = fi.fileName();
  337. QString str(":");
  338. str.append(fileName);
  339. str.append(":");
  340. str.append(QString("%1").arg(fi.size(), TRANSFER_FILE_FILE_SIZE_LENGTH,
  341. 16, QChar('0')));
  342. str.append(":");
  343. str.append(QString("%1").arg(IPMSG_FILE_REGULAR, 0, 16));
  344. str.append(":");
  345. str.append(QString("%1=%2").arg(IPMSG_FILE_MTIME, 0, 16)
  346. .arg(fi.lastModified().toTime_t(), 0, 16));
  347. str.append(":");
  348. str.append(QString("%1=%2").arg(IPMSG_FILE_CREATETIME, 0, 16)
  349. .arg(fi.created().toTime_t(), 0, 16));
  350. str.append(":");
  351. QByteArray block = Global::transferCodec->codec()->fromUnicode(str);
  352. QString s = QString("%1").arg(block.size() + TRANSFER_FILE_HEADER_SIZE_LENGTH,
  353. TRANSFER_FILE_HEADER_SIZE_LENGTH, 16, QChar('0'));
  354. QByteArray ba = Global::transferCodec->codec()->fromUnicode(s);
  355. return ba + block;
  356. }