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.

Logo

更多推荐