qt實現基於FTP文件存儲系統實現多線程上傳,多文件下載

github:https://github.com/xiaofengyun/FTP-FILE-transport

             https://github.com/xiaofengyun/FTP-FILE-transport.git

無線網絡下基於FTP文件存儲系統運行環境如下:
開發平臺:Windows 10(客戶端),Linux(服務端)
開發環境:Qt Creator,Centos 7
編譯環境:G++,GDB,qmake
運行環境:Qt 5.5.1,Centos 7

多線程上傳的邏輯:

選擇完一個文件後,通過偏移文件指針lseek()得到文件的size。接下來將大文件按照一定的方法拆分,首先,先比較文件的大小與切片大小,切片大小是一個定值。如果文件的大小小於切片的大小,那麼不必進行多線程上傳,單線程就可以了。當需要多線程上傳的時候,首先計算出線程個數,通過用size/切片大小和size%切片大小的方式計算出需要的線程數。接下來便是傳輸文件,使用read()函數按字節去讀取文件,每次讀取一定的字節數到一個緩衝區。然後便是加標誌位。猶豫線程是併發執行的,無法預測哪一個線程會首先完成傳輸,這會對文件的合併差生巨大的影響,所以必須加上標誌位,判斷數據片段是屬於哪一個位置的。在緩衝區的前兩個字節加上線程ID作爲標誌位,然後將讀取的文件數據憑藉到緩衝區後面,這樣便組成了一個數據包,每個線程將這個數據包發送給服務端,然後自行關閉。

客戶端多線程上傳程序:

void MainWindow::thread_upload()/////////////多線程上傳
{
	qDebug() << "start thread upload...";
	qint64 m_totalBytes;
	QFile * m_localFile;

	QString path = QFileDialog::getOpenFileName(this, "Open a file", "/", "files (*)");
	QFileInfo info(path);//path就是打開文件的路徑

	m_localFile = new QFile(path);//類似一個文件指針
	if (!m_localFile->open(QFile::ReadOnly))
	{
		qDebug() << "open file error!";
		return;
	}
	//打開成功
	qDebug() << "打開文件成功!";
	m_totalBytes = m_localFile->size();//得到文件總大小
	QString size = QString::number(m_totalBytes, 10);
	qDebug() << "文件大小:" << m_localFile->size();
	QString up = "upload";
	up += " " + info.fileName() + " " + size;

	char*  ch;
	QByteArray ba = up.toLatin1(); // must
	ch = ba.data();
	sockfd->write(ch);///向服務端發送上傳請求,等待迴應
	if (sockfd->waitForReadyRead(10))
	{
		QByteArray buff = sockfd->readAll();//接收服務端返回的消息
		qDebug() << buff;
		if (strncmp(buff, "ok", 2) != 0)
		{
			qDebug() << "server no message...";
			return;
		}
		int n = 0;
		int i = 0;
		threadDown *thread[SIZE];
		char sendbuff[1024] = { 0 };
		size_int = size.toInt();
		qDebug() << "size=" << size_int;
		int num_thread = size_int / 1000;////1000爲分片大小,大小自己定義
		if (size_int - num_thread * 1000>0)
		{
			num_thread++;///需要開啓的線程數,也就是分片的個數
		}
		qDebug() << "thread=" << num_thread;

		for (; i<num_thread; i++)
		{
			qDebug() << "i=" << i;

			if (i <= 9)
			{
				sprintf(sendbuff, "%d", i);
				sprintf(sendbuff + 1, "%s", NULL);
			}
			else
			{
				sprintf(sendbuff, "%d", i);
			}
			n = m_localFile->read(sendbuff + 2, 1000);////每個數據包的前兩個字節表示線程id,也就是標識符
	
			thread[i] = new threadDown(sendbuff, i); //新建線程對象,在線程中將數據發送就可以了
			thread[i]->start();  //啓動線程
			bool b = connect(thread[i], SIGNAL(thread_to_main(char *, int)), this, SLOT(start_to_up(char *, int)));
			qDebug() << "b=" << b;
			thread[i]->wait();
			memset(sendbuff, 0, sizeof(sendbuff));
			qDebug() << "------------------------------------------------------------";
		}	
	}
//////多線程上傳時,服務端接收處理程序
while ((num = recv(c, buff, 1002, 0))>0)
{
	printf("num =%d\n", num);
	printf("%s\n", buff);
	char seq[2] = "";
	seq[0] = buff[0];
	seq[1] = buff[1];
	//cout<<"seq="<<seq;
	printf("seq=%s\n", seq);
	int pro_seq = atoi(seq);//求出線程數
	printf("id =%d\n", pro_seq);
	cout++;
	num -= 2;
	printf("%s\n", buff);
	printf("len=%d\n", strlen(buff));
	cur_size += num;
	printf("cur_size=%d\n", cur_size);

	printf("線程id=%d\n", pro_seq);

	int off = lseek(fd, pro_seq * 1000, SEEK_SET);//偏移到原本數據分片的地方
	printf("偏移字節=%d\n", off);

	write(fd, buff + 2, strlen(buff) - 2);
	memset(buff, 0, sizeof(buff));
	lseek(fd, 0, SEEK_SET);//指針重置
	printf("-----------------------------------=--------------\n");
	printf("cur_size=%d\n", cur_size);
	if (cur_size >= size)
	{
		break;
	}
}

多文件下載:

爲了實現多文件同時下載,採用了多客戶端程序連接服務端的方式。前面提到過,服務端可以承受多客戶端的連接,所以當有多個文件去下載時,表面上是由一個用戶發出的請求,在內部實現時,模擬多用戶去下載的過程。

步驟:

1.開啓多個線程,將要下載的文件名傳入各個線程;

2.再線程函數中創建套接字去下載文件即可。

運行截圖:

 

代碼在git裏面,需要借鑑的可以看。代碼完全是本人自己的想法,如有不對或者錯誤請指正,交流加QQ:965829642.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章