跨平台网盘
-
概述
基于tcp连接,实现跨平台网盘项目,服务器采用linux开发,客户端使用qt进行开发,实现了,注册,登录,上传文件的功能
-
技术点
- 网络部分采用了epoll 模型 加 reactor 模型实现
- 对于到来的请求回复采用了map映射,提高软件的扩展性
- 对粘包问题进行了处理
- 处理类中使用了单例模式
- 通过sql拼接语句,实现数据库的增删改查
- 客户端使用了线程池实现多个文件同时传递,同时对线程池进行了优化,控制最大同时上传文件数
- 使用qt进行了页面开发,利用qt信号与槽的机制实现页面交互
- 使用MD5算法对文件进行处理,实现相同文件闪传
-
模块划分
模块主要划分为网络模块,处理模块,数据库模块以及客户端
1.网络模块
网络模块主要实现收发数据的功能,接受数据然后把数据交给处理模块进行处理。在网络模块中主要使用了tcp协议作为文件传输协议,因为相比于udp他更加安全,可靠。为了避免程序运行时出现的粘包问题采用了,固定每个包的长度,接收包的长度。使用了epoll模型和rector模型,提高了程序的监听速度
2.处理模块
处理模块中使用了单例模式作为设计模式,单例模式分为懒汉式和饿汉式,这里使用了饿汉式,直接创建出来一个,然后有申请直接返回这个。还使用了协议映射将不同处理函数与他们对应协议包的类型通过数组一一映射,添加功能只需要在数组中添加元素即可,提高了程序的扩展性。还通过使用sql拼接语句操作数据库进行增删改查,实现了用户注册,登录,获取文件信息,上传和下载文件内容。
3.数据库模块
数据库中的信息存储,分成了三张表,文件信息,用户信息,以及用户id与文件id关联。
文件信息包括一个文件id,文件存储位置,文件大小,文件上传时间,以及文件引用次数。用户信息包括用户手机号,用户id,用户密码等信息。用户id与文件id关联表将用户拥有的文件与用户的id相关联。
在数据库中又通过视图拼接成一张完整信息的视图,用来查找用户完整的信息。
4.客户端
客户端是基于qt进行的页面开发,使用了信号与槽机制实现线程间的通信。
为了避免在一个文件上传时整个程序阻塞,使用了线程池进行优化,通过限制线程池最大线程数量,来限制最多同时上传多少文件。
-
功能实现
1.注册:
在客户端输入信息后,点击注册,客户端会向服务器端发送一个注册请求包,向数据库中插入信息,如果失败,说明在数据库中查找存在相同手机号,将注册回复包中信息标志变为注册失败,成功则变为注册成功,向客户端发送注册回复包。
2.登录:
在客户端输入信息后,点击登录,客户端会向服务器端发送一个登录请求包,服务器端根据登录请求包的信息,查询数据库,如果查询到的信息id和密码都是一一对应,则返回登录成功回复包,否则返回失败回复包,二者只是内部结果不同,类型相同。
3.获取文件列表:
在客户端收到登录成功包时会发送获取文件列表请求包,服务器收到后会根据包内的用户id查询数据库,获取与用户关联的信息,将信息打包放在一起作为文件列表回复包,客户端将文件列表回复包的文件信息显示到界面上。
4.上传文件:
客户端点击上传文件后,在文件管理中选择需要上传的文件,客户端先进行读取,计算出md5值,同时记录文件地址,将这些信息放入widght类下的文件信息列表,同时将文件上传请求包发送给服务器端,服务器端去根据MD5值查询该文件(这里可以看文件的被引用次数的值来判断,若为0,则是上传一部分,大于等于1则是执行秒传),会出现以下三种情况
(1).如果已经存在,则执行秒传:
若是秒传,向数据库查询对应文件的文件id,与该用户id对应插入到数据库用户文件列表中,文件引用次数加一,返回类型上传成功。
(2).如果是上传了但是上传了一半则断点续传
若是断点续传,则将上次文件传输到的位置放入上传文件回复包中。
(3).如果是没传过,则正常传:
若是没上传过,则通过文件名创建一个文件存储地址,将文件信息加入到数据库文件信息列表当中。断点续传和正常传,服务器列表记录下这个文件信息
将文件信息放入文件上传回复包中,表明类型为闪传,断点续传,还是正常传。
客户端收到文件上传信息回复包后,根据类型进行处理,根据回复包找到文件MD5值,根据MD5值找到对应文件信息。
如果是闪传,直接将文件显示在界面上,把对应文件信息从文件信息列表中删除。
如果是断点续传和正常传递,则把文件指针移动到文件上传回复包中文件位置对应的地方(正常传是0),开始循环读取文件并发送文件信息包。
服务器端接收到后,写入对应文件名的文件(根据文件名创建的文件)。
因为这里如果在主线程中传输文件,大的文件会一直传递造成阻塞,所以我们这里采用线程池的方式,将文件上传的过程放在线程池中,客户端收到文件上传信息回复包后,初始化创建一个任务投递到线程池当中。
5.下载文件:同上传文件。
服务器代码
一. 包内容
packdef.h
#pragma once
#ifndef PACKDEF_H
#define PACKDEF_H
#pragma pack(4)
#define _DEF_PROTOCOL_BASE 10
//注册
#define _DEF_PROTOCOL_REGISTER_RQ _DEF_PROTOCOL_BASE +1
#define _DEF_PROTOCOL_REGISTER_RS _DEF_PROTOCOL_BASE +2
//登录
#define _DEF_PROTOCOL_LOGIN_RQ _DEF_PROTOCOL_BASE +3
#define _DEF_PROTOCOL_LOGIN_RS _DEF_PROTOCOL_BASE +4
//获取文件列表
#define _DEF_PROTOCOL_GETFILELIST_RQ _DEF_PROTOCOL_BASE +5
#define _DEF_PROTOCOL_GETFILELIST_RS _DEF_PROTOCOL_BASE +6
//上传文件信息
#define _DEF_PROTOCOL_UPLOAD_FILEINFO_RQ _DEF_PROTOCOL_BASE +7
#define _DEF_PROTOCOL_UPLOAD_FILEINFO_RS _DEF_PROTOCOL_BASE +8
//上传文件内容
#define _DEF_PROTOCOL_UPLOAD_FILECONTENT_RQ _DEF_PROTOCOL_BASE +9
//下载文件信息
#define _DEF_PROTOCOL_DOWNLOAD_FILEINFO_RQ _DEF_PROTOCOL_BASE +10
#define _DEF_PROTOCOL_DOWNLOAD_FILEINFO_RS _DEF_PROTOCOL_BASE +11
//下载文件内容
#define _DEF_PROTOCOL_DOWNLOAD_FILECONTENT_RQ _DEF_PROTOCOL_BASE +12
#define _upload_file_exists 0 //文件我自己已经上传过,不需要重复上传
#define _upload_file_continue 1 //曾经传过,没有传完。断点续传
#define _upload_file_success 2 // 秒传
#define _upload_file_normal 3 //正常传
#define MAXSIZE 45
#define MAXFILENUM 500
#define MAXSQLLEN 500
#define MAXFILECONTENT 4096
//协议包 注册失败 注册成功
#define _register_failed 0
#define _register_success 1
//用户名不存在 密码错误 登录成功
#define _login_user_noexist 0
#define _login_password_err 1
#define _login_success 2
//注册请求包
struct STRU_REGISTER_RQ
{
char m_nType;
char m_szName[MAXSIZE];
char m_szPassword[MAXSIZE];
long long m_ltel;
};
//注册回复包
struct STRU_REGISTER_RS
{
char m_nType;
char m_szResult;
};
//登录
struct STRU_LOGIN_RQ
{
char m_nType;
char m_szName[MAXSIZE];
char m_szPassword[MAXSIZE];
};
struct STRU_LOGIN_RS
{
char m_nType;
char m_szResult;
long long m_luserid;
};
//获取文件列表
struct STRU_GETFILELIST_RQ
{
char m_nType;
long long m_luserid;
};
//文件信息
struct FileInfo
{
char m_szFileName[MAXSIZE];
long long m_fileSize;
char m_szDateTime[MAXSIZE];
};
//获取文件列表
struct STRU_GETFILELIST_RS
{
char m_nType;
FileInfo m_aryFile[MAXFILENUM];
int m_nFileNum;
};
//文件信息请求
struct STRU_UPLOAD_FILEINFO_RQ
{
char m_nType;
long long m_lUserId;
long long m_lFileSize;
char m_szFileName[MAXSIZE];
char m_szFileMD5[MAXSIZE];
};
//文件信息回复
struct STRU_UPLOAD_FILEINFO_RS
{
char m_nType;
long long m_lUserId;
long long m_lFileId;
long long m_lPosition;
char m_szFileMD5[MAXSIZE];
int bresult;
};
//上传文件包
struct STRU_UPLOAD_FILECONTENT_RQ
{
char m_nType;
long long m_lUserId;
long long m_lFileId;
char m_szFileContent[MAXFILECONTENT];
int m_nNum;
};
#endif // PACKDEF_H
二.数据库模块
CMySql.h
#pragma once
#include"/usr/include/mysql/mysql.h"
//#include <WinSock2.h>
//#pragma comment(lib,"E:\\lianxichengxu\\wangpan4.23\\网盘\\libmysql.lib")
//
#include <list>
#include <string>
using namespace std;
class CMySql
{
public:
CMySql(void);
~CMySql(void);
public:
bool ConnectMySql(const char *host,const char *user,const char *pass,const char *db);//连接数据库
//主机号 , 用户名,用户密码 , 查询的表的名称
void DisConnect();//断开连接
bool SelectMySql(const char* szSql,int nColumn,list<string>& lstStr);//查询数据库
//参数为拼接好的sql语句,多少列,存储获得的信息的链表
bool UpdateMySql(const char* szSql); //更新:删除、插入、修改
private:
MYSQL *sock;
MYSQL_RES *results;
MYSQL_ROW record;
};
CMySql.cpp
#include "CMySql.h"
CMySql::CMySql(void)
{
/*这个函数用来分配或者初始化一个MYSQL对象,用于连接mysql服务端。
如果你传入的参数是NULL指针,它将自动为你分配一个MYSQL对象,
如果这个MYSQL对象是它自动分配的,那么在调用mysql_close的时候,会释放这个对象*/
sock = new MYSQL;
mysql_init(sock );
mysql_set_character_set(sock,"gb2312"); //gb2312 中华人民共和国简体字标准
}
CMySql::~CMySql(void)
{
if(sock)
{
delete sock;
sock = NULL;
}
}
void CMySql::DisConnect()
{
mysql_close(sock);//关闭接口
}
bool CMySql::ConnectMySql(const char *host,const char *user,const char *pass,const char *db)
{//主机号 , 用户名,用户密码 , 查询的表的名称
//localhost 127.0.0.1 主机号
if (!mysql_real_connect(sock, host, user, pass, db, 0, NULL, 0))
{
//连接错误
return false;
}
return true;
}
bool CMySql::SelectMySql(const char* szSql,int nColumn,list<string>& lstStr)//参数为拼接好的sql语句,多少列,存储获得的信息的链表
{
//mysql_query() 函数用于向 MySQL 发送并执行 SQL 语句
if(mysql_query(sock,szSql))return false;
/*·mysql_store_result 对于成功检索了数据的每个查询(SELECT、SHOW、DESCRIBE、EXPLAIN、CHECK TABLE等)
返回值:
. CR_COMMANDS_OUT_OF_SYNC 以不恰当的顺序执行了命令。
· CR_OUT_OF_MEMORY 内存溢出。
· CR_SERVER_GONE_ERROR MySQL服务器不可用。
· CR_SERVER_LOST 在查询过程中,与服务器的连接丢失。
· CR_UNKNOWN_ERROR 出现未知错误。*/
results=mysql_store_result(sock);
if(NULL == results)return false;
while (record = mysql_fetch_row(results))
{
for(int i = 0;i < nColumn;i++)
{
if(NULL == record[i])
{
record[i] = (char*)"null";
}
lstStr.push_back(record[i]);
}
}
return true;
}
bool CMySql::UpdateMySql(const char* szSql)//更新数据
{
if(!szSql)return false;
if(mysql_query(sock,szSql))return false;
return true;
}
三.网络模块
Inet.h
#pragma once
class Inet
{
public:
Inet()
{}
virtual ~Inet()
{}
public:
virtual bool InitNetWork() = 0;//初始化网络
virtual void UnInitNetWork() = 0;//结束连接
virtual bool SendData(int sock, char* szbuf, int nlen) = 0;//发送数据
//目标套接字 内容 长度
virtual void RecvData() = 0; // 接收文件
};
ctcpnet.h
#ifndef CTCPNET_H
#define CTCPNET_H
#include<stdio.h>
#include<stdlib.h>
#include<sys/times.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/select.h>
#include<unistd.h>
#include<pthread.h>
#include <arpa/inet.h>
#include<string.h>
#include<poll.h>
#include<sys/epoll.h>
#include <list>
#include <map>
#include"Inet.h"
#include"TCPkernel.h"
class CTCPNet;
struct rector
{
int epfd;//操作的epoll
epoll_event arr_epo[4096];//epoll 数组
};
typedef struct item //回调函数参数结构体 包括回调函数和当前套接字 一块接收数据的空间
{
int sock;//套接字
void (CTCPNet::*callback)(int, int, void*);// 回调函数
char *buf;//储存内容的地址
int sizebuf;//大小
int pos;
}ite;
class CTCPNet : public Inet
{
public:
CTCPNet(Ikernel* pkernel);
public:
//1.初始化网络
bool InitNetWork();
void UnInitNetWork();
//2.收发数据
bool SendData(int sock,char *szbuf,int nlen);
public:
void accept_epoll(int sock, int event, void* pit);//连接回调函数
void recv_cb(int sock, int event, void* arg);//接收数据回调函数
public:
static void* threadproc(void*);//线程函数
private:
int m_sock;//监听套接字
rector* m_rector;//rector结构体,epollfd epoll_event数组
pthread_t m_pid;//线程id
bool m_bFlagQuit;//退出标志
std::list<pthread_t> m_lstThread;//储存线程池各个线程的id的链表
Ikernel* m_pkernel;//处理模块的指针
};
#endif // CTCPNET_H
ctcpnet.cpp
#include "ctcpnet.h"
#include<iostream>
using namespace std;
CTCPNet::CTCPNet(Ikernel* pkernel)
{
m_sock = 0;
m_rector = NULL;
m_bFlagQuit = true;
m_pkernel = pkernel;
}
void CTCPNet::recv_cb(int sock, int event, void* arg)//接收消息的回调函数
{
ite* pite = (ite*)arg;
int res;
if (pite->sizebuf == 0)//第一次先接受包大小,前四个字节为包大小
{
res = recv(sock, &pite->sizebuf, sizeof(int), 0);
if (res < 0)
{
epoll_ctl(m_rector->epfd, EPOLL_CTL_DEL, sock, 0);
close(sock);
delete pite;
}
pite->buf = new char[pite->sizebuf];//创建相同大小的接收缓冲区
}
else//第二次接收内容
{
while (pite->sizebuf)
{
res = recv(sock, pite->buf, pite->sizebuf, 0);
if (res > 0)
{
pite->pos += res;
pite->sizebuf -= res;
}
else//客户端死亡,直接断开连接
{
epoll_ctl(m_rector->epfd, EPOLL_CTL_DEL, sock, 0);
close(sock);
delete pite;
cout << "recv failed ,maybe kehuduan die" << endl;
return;
}
//处理数据 交给处理模块进行处理
m_pkernel->dealtext(pite->buf, sock);
//数据处理完成,删除数据
delete[]pite->buf;
pite->buf = NULL;
}
}
}
void CTCPNet::accept_epoll(int sock, int event, void* pit)//连接回调函数
{
int sockwinter = accept(sock, 0, 0);
item* pite = new item;//创建一个函数结构体
pite->sock = sockwinter;//套接字为收发数据套接字
pite->callback = &CTCPNet::recv_cb;//回调函数是接收
pite->pos = 0;
pite->sizebuf = 0;
epoll_event ee;
ee.data.ptr = pite;
ee.events = EPOLLIN | EPOLLET;
epoll_ctl(m_rector->epfd, EPOLL_CTL_ADD, sockwinter, &ee);
}
bool CTCPNet::InitNetWork()
{
//创建
int m_sock = socket(AF_INET, SOCK_STREAM, 0);
//绑定
struct sockaddr_in sockaddr;
sockaddr.sin_family = AF_INET;
sockaddr.sin_port = htons(8890);
sockaddr.sin_addr.s_addr = 0;
int i = bind(m_sock, (const struct sockaddr*)&sockaddr, sizeof(sockaddr));
if (i != 0)
{
printf("%d\n", i);
perror("flase\n");
return 0;
}
//监听
if (listen(m_sock, 10))
{
perror("listen");
return 0;
}
printf("have listen\n");
//epoll初始化,交给rector管理
m_rector = new rector;
m_rector->epfd = epoll_create(4096);
struct epoll_event ee;//创建事件函数
ee.data.fd = m_sock;
ee.events = EPOLLIN;
ite* pite = new ite;
pite->sock = m_sock;
pite->callback = &CTCPNet::accept_epoll;
//事件数据包含ite结构体
ee.data.ptr = (void*)pite;
epoll_ctl(m_rector->epfd, EPOLL_CTL_ADD, m_sock, &ee);
pthread_create(&m_pid, 0,&threadproc,this);
return true;
}
void* CTCPNet::threadproc(void* lpvoid)
{
CTCPNet* pthis = (CTCPNet*)lpvoid;
while (pthis->m_bFlagQuit)
{
int readnum = epoll_wait(pthis->m_rector->epfd, pthis->m_rector->arr_epo, 4096, -1);
//等待有事件发生
int i = 0;
while (i < readnum)
{
printf("readnum > 0\n ");
if (pthis->m_rector->arr_epo[i].events & EPOLLIN)//查看是不是父套接字
{
ite* pite = (ite*)pthis->m_rector->arr_epo[i].data.ptr;
//拿到对应ite结构体 ,执行回调函数
(pthis->*pite->callback)(pite->sock, pthis->m_rector->arr_epo[i].events, pite);
}
if (pthis->m_rector->arr_epo[i].events & EPOLLOUT)//是不是有数据来到
{
ite* pite = (ite*)pthis->m_rector->arr_epo[i].data.ptr;
//拿到对应ite结构体 ,执行回调函数
(pthis->*pite->callback)(pite->sock, pthis->m_rector->arr_epo[i].events, pite);
}
++i;
}
}
}
void CTCPNet::UnInitNetWork()
{
m_bFlagQuit = false;
pthread_cancel(m_pid);
pthread_join(m_pid, 0);
if(m_sock)
{
close(m_sock);
m_sock = 0;
}
}
bool CTCPNet::SendData(int sock,char *szbuf,int nlen)
{
if(sock == -1 || !szbuf || nlen <=0)
return false;
//发送大小
if(send(sock,(char*)&nlen,sizeof(int),0) <=0)
return false;
//发送内容
if(send(sock,szbuf,nlen,0) <=0)
return false;
return true;
}
四.处理模块
Ikernel.h
#pragma once
#define Maxpath 64
class Ikernel
{
public:
Ikernel()
{}
virtual ~Ikernel()
{}
public:
virtual void do_REGISTER_RQ(char* buf,int sock) = 0;//注册
virtual void do_LOGIN_RQ(char* buf,int sock) = 0;//登录
virtual void do_GETFILELIST_RQ(char* buf, int sock) = 0;//获取文件列表
virtual void do_UPLOAD_FILEINFO(char* buf, int sock) = 0;//文件信息回复包
public:
virtual bool Opensqlandnet() = 0;//启动数据库和网络
virtual void Closesqlandnet() = 0;//关闭
virtual void dealtext(char *,int sock) = 0;//处理
};
TCPkernel.h
#pragma once
#include "Ikernel.h"
#include"ctcpnet.h"
#include"CMySql.h"
#include<iostream>
#include<sys/stat.h>
#include<sys/types.h>
#include"packdef.h"
#include<map>
#include<fstream>
class TCPkernel;
typedef void (TCPkernel::*PFUN)(char* buf, int sock);
struct protomap//类型与函数关联结构体
{
char n_type;
PFUN fun;
};
struct uploadfileinfo //文件信息
{
long long filesize;//文件大小
long long user_id;//用户id
long long fileposition;//文件上传到的位置
char pfilepath[MAXFILENUM];//文件路径
};
//单例模式,服务器只能有一个
#define Maxsocle 300
class TCPkernel :public Ikernel
{
private:
Inet* m_pinet;//网络对象
CMySql* m_pmysql;//数据库对象
static TCPkernel* m_tcpkernel;//单例模式创建一个对象
char m_path[64];//路径前缀储存的位置
map<long long, uploadfileinfo*> m_mapfileiftofileinfo;
private:
TCPkernel();
~TCPkernel();
public:
void do_REGISTER_RQ(char* buf,int sock);//注册
void do_LOGIN_RQ(char* buf, int sock);//登录
void do_GETFILELIST_RQ(char* buf, int sock);//获取文件列表
void do_UPLOAD_FILEINFO(char* buf, int sock);//发送文件信息回复包
void do_UPLOAD_FILECONTENT_RQ(char* buf, int sock);//发送文件报处理
public:
bool Opensqlandnet();//初始化网络 和 数据库
void Closesqlandnet();//关闭
void dealtext(char *,int);//处理函数
static TCPkernel* gettcpkernel()//单例模式 饿汉
{
/*if (m_tcpkernel == NULL)
m_tcpkernel = new TCPkernel;*/
return m_tcpkernel;
}
};
TCPkernel.cpp
#include "TCPkernel.h"
TCPkernel* TCPkernel::m_tcpkernel = /*nullptr;*/ new TCPkernel; //单例模式 ehan模式
TCPkernel::TCPkernel()
{
m_pinet = new CTCPNet(this);
m_pmysql = new CMySql;
strcpy(m_path, "home/buka/disk");
}
TCPkernel::~TCPkernel()
{
delete m_pinet;
delete m_pmysql;
}
bool TCPkernel::Opensqlandnet()
{
if (!m_pinet->InitNetWork())
{
cout << "net fail" << endl;
return 0;
}
if (!m_pmysql->ConnectMySql("localhost","root","123456","text423"))
{
cout << "mysql fail" << endl;
return 1;
}
return 1;
}
void TCPkernel::Closesqlandnet()
{
m_pinet->UnInitNetWork();
m_pmysql->DisConnect();
}
protomap m_pprotomap[] = {
{_DEF_PROTOCOL_REGISTER_RQ,&TCPkernel::do_REGISTER_RQ},
{_DEF_PROTOCOL_LOGIN_RQ,&TCPkernel::do_LOGIN_RQ},
{_DEF_PROTOCOL_GETFILELIST_RQ,&TCPkernel::do_GETFILELIST_RQ},
{_DEF_PROTOCOL_UPLOAD_FILEINFO_RQ,&TCPkernel::do_UPLOAD_FILEINFO},
{_DEF_PROTOCOL_UPLOAD_FILECONTENT_RQ,&TCPkernel::do_UPLOAD_FILECONTENT_RQ},
{0,0}
};
void TCPkernel::dealtext(char* buf, int sock)//通过包的第一个字节代表的类型调用对应函数
{
//未进行映射的
//switch (*buf)
//{
//case _DEF_PROTOCOL_REGISTER_RQ:
//{//注册请求
// do_REGISTER_RQ(buf,sock);
//}
// break;
//case _DEF_PROTOCOL_LOGIN_RQ:
// do_LOGIN_RQ(buf, sock);
// break;
//case _DEF_PROTOCOL_GETFILELIST_RQ:
// do_GETFILELIST_RQ(buf, sock);
// break;
//case _DEF_PROTOCOL_UPLOAD_FILEINFO_RQ:
// do_UPLOAD_FILEINFO(buf, sock);
// break;
//case _DEF_PROTOCOL_UPLOAD_FILECONTENT_RQ:
// do_UPLOAD_FILECONTENT_RQ(buf,sock);
// break;
//case _DEF_PROTOCOL_DOWNLOAD_FILEINFO_RQ:
// break;
//case _DEF_PROTOCOL_DOWNLOAD_FILECONTENT_RQ:
// break;
//default:
// break;
//}
//映射后的
int i = 0;
while (1)
{
if (m_pprotomap[i].n_type == *buf)
{
(this->*m_pprotomap[i].fun)(buf, sock);
return;
}
if (m_pprotomap[i].n_type == 0 && m_pprotomap[i].fun == 0)
break;
i++;
}
}
void TCPkernel::do_REGISTER_RQ(char* buf,int sock)//注册请求包处理
{
//请求包,转化成结构体
STRU_REGISTER_RQ* p = (STRU_REGISTER_RQ*)buf;
//请求回复包
STRU_REGISTER_RS rs;
rs.m_nType = _DEF_PROTOCOL_REGISTER_RS;
rs.m_szResult = _register_failed;
char kongjianbuf[Maxpath] = { 0 };//用来存放拼接空间的地址
//1.写入数据库
char scbuf[Maxsocle] = { 0 };
sprintf(scbuf, "insert into personinemation(m_name,m_password,m_phonenum) values('%s','%s',%lld);",
p->m_szName, p->m_szPassword, p->m_ltel);//拼接数据库语句
if (m_pmysql->UpdateMySql(scbuf))
{ //成功
rs.m_szResult = _register_success;
//分配空间
sprintf(kongjianbuf,"%s%lld", m_path, p->m_ltel);
//按每个人手机号创建空间
//CreateDirectoryA(kongjianbuf, 0);//路径,安全属性
mkdir(kongjianbuf, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
}
m_pinet->SendData(sock, (char*)&rs, sizeof(rs));//发送回复信息
}
void TCPkernel::do_LOGIN_RQ(char* buf, int sock)
{
STRU_LOGIN_RQ* rq = (STRU_LOGIN_RQ*)buf;
list<string> lis;
//校验密码是否正确
//登录回复包
STRU_LOGIN_RS rs;
rs.m_nType = _DEF_PROTOCOL_LOGIN_RS;
rs.m_szResult = _login_user_noexist;//初始为用户不存在
//拼接sql语句
char scbuf[Maxsocle] = { 0 };
sprintf(scbuf, "select m_id,m_password from personinemation where m_name = '%s';",rq->m_szName);
if (m_pmysql->SelectMySql(scbuf, 2, lis))//查询
{
if (lis.size() > 0)//查询到数据
{
rs.m_szResult = _login_password_err;//用户存在,设为密码错误,查看密码是否相同
string strmId = lis.front();
lis.pop_front();
string password = lis.front();
lis.pop_front();
if (0 == strcmp(password.c_str(), rq->m_szPassword))
{
//回复
rs.m_luserid = atoi(strmId.c_str());
rs.m_szResult = _login_success;//密码相同发送到客户端
}
}
}
m_pinet->SendData(sock, (char*)&rs, sizeof(rs));//发送函数
}
void TCPkernel::do_GETFILELIST_RQ(char* buf, int sock)//获取文件列表
{
STRU_GETFILELIST_RQ* rq = (STRU_GETFILELIST_RQ*)buf;
list<string> lis;
//查询这个id下的所有文件
STRU_GETFILELIST_RS rs;
rs.m_nType = _DEF_PROTOCOL_GETFILELIST_RS;
char scbuf[Maxsocle] = { 0 };
sprintf(scbuf, "select f_name,f_size from myview where m_id = %lld;", rq->m_luserid);
m_pmysql->SelectMySql(scbuf, 2, lis);
int i = 0;
while (lis.size() > 0)//查询的消息存在这个链表里,循环读取
{
string strf_name = lis.front();
lis.pop_front();
string strf_size = lis.front();
lis.pop_front();
rs.m_aryFile[i].m_fileSize =atoi( strf_size.c_str());
strcpy(rs.m_aryFile[i].m_szFileName, strf_name.c_str());
i++;
if (i == MAXFILENUM || lis.size() == 0)//到达每次最大发送数,或者已经读完 ,发送数据
{
rs.m_nFileNum = i;
m_pinet->SendData(sock, (char*)&rs, sizeof(rs));
i %= MAXFILENUM;
}
}
}
void TCPkernel::do_UPLOAD_FILEINFO(char* buf, int sock)//上传文件信息请求包
{
STRU_UPLOAD_FILEINFO_RQ* rq = (STRU_UPLOAD_FILEINFO_RQ*)buf;
STRU_UPLOAD_FILEINFO_RS rs;
rs.m_lFileId = rq->m_lUserId;
strcpy(rs.m_szFileMD5, rq->m_szFileMD5);
rs.m_nType = _DEF_PROTOCOL_UPLOAD_FILEINFO_RS;
rs.m_lPosition = 0;
list<string> lisstr;
//分为三种情况 ,传过,上传中,上传完毕
char strsql[MAXFILENUM] = { 0 };
sprintf(strsql, "select m_id,f_id,f_count from myview where f_md5 ='%s' and f_name = '%s';",rq->m_szFileMD5, rq->m_szFileName);
m_pmysql->SelectMySql(strsql, 3, lisstr);
if (lisstr.size() > 0)//这个文件存在
{
string user_id = lisstr.front();
lisstr.pop_front();
string file_id = lisstr.front();
lisstr.pop_front();
string f_count = lisstr.front();
lisstr.pop_front();
//为恢复包f_id赋值
rs.m_lFileId = atoi(file_id.c_str());//atoi64 改成的atoi
long long user_m_id = atoi(user_id.c_str());
//long long user_file_id = _atoi64(file_id.c_str());
long long user_m_count = atoi(f_count.c_str());
if (user_m_id == rq->m_lUserId)
{
rs.bresult = _upload_file_exists;
//自己已经上传过
//如果这个文件在未传完的文件列表里,就是断点续传
uploadfileinfo* pupfileinfo = m_mapfileiftofileinfo[rs.m_lFileId];
if (pupfileinfo)
{
rs.m_lPosition = pupfileinfo->fileposition;
rs.bresult = _upload_file_continue;
}
}
else
{
//秒传
rs.bresult = _upload_file_success;
//把文件给该用户
sprintf(strsql, "insert into user_file(f_id,m_id) value('%s',%lld);", file_id.c_str(), rq->m_lUserId);
m_pmysql->UpdateMySql(strsql);
//计数加一
sprintf(strsql, "update filie set f_count = %lld where f_id = '%s';", user_m_count, file_id.c_str());
m_pmysql->UpdateMySql(strsql);
}
}
else//这个文件不存在
{
//正常传
rs.bresult = _upload_file_normal;
//查找手机号,以手机号设置文件夹
sprintf(strsql, "select m_phonenum from personinemation where m_id = %lld;", rq->m_lUserId);
m_pmysql->SelectMySql(strsql,1,lisstr);
if (lisstr.size() > 0)
{
string tel = lisstr.front();
lisstr.pop_front();
char pathtel[MAXFILENUM] = { 0 };
//拼接地址
sprintf(pathtel, "%s%s\\%s", m_path, tel.c_str(), rq->m_szFileName);
//将文件加入到文件列表
sprintf(strsql, "insert into filie(f_name,f_size,f_path,f_md5,f_count) value('%s',%lld,'%s','%s',0);",
rq->m_szFileName, rq->m_lFileSize,pathtel , rq->m_szFileMD5);
m_pmysql->UpdateMySql(strsql);
//查询文件id
sprintf(strsql, "select f_id from filie where f_name = '%s' and f_md5 = '%s';",rq->m_szFileName,rq->m_szFileMD5);
m_pmysql->SelectMySql(strsql, 1, lisstr);
if (lisstr.size() > 0)
{
//为回复包f_id赋值
rs.m_lFileId = atoi(lisstr.front().c_str());
//获取文件id,把用户和文件关联在一起
sprintf(strsql, "insert into user_file(f_id,m_id) value('%s',%lld);", lisstr.front().c_str(), rq->m_lUserId);
lisstr.pop_front();
m_pmysql->UpdateMySql(strsql);
}
//储存文件信息,文件id,文件大小,文件位置
uploadfileinfo* pupfileinfo = new uploadfileinfo;
pupfileinfo->filesize = rq->m_lFileSize;
pupfileinfo->user_id = rq->m_lUserId;
pupfileinfo->fileposition = 0;
strcpy(pupfileinfo->pfilepath, pathtel);
m_mapfileiftofileinfo[rs.m_lFileId] = pupfileinfo;
}
}
m_pinet->SendData(sock, (char*)&rs, sizeof(rs));
}
void TCPkernel::do_UPLOAD_FILECONTENT_RQ(char* buf, int sock)//上传文件数据包
{
STRU_UPLOAD_FILECONTENT_RQ* rq = (STRU_UPLOAD_FILECONTENT_RQ*)buf;
uploadfileinfo* pupfile = m_mapfileiftofileinfo[rq->m_lFileId];
fstream file;
file.open(pupfile->pfilepath, ios::out | ios::app);
if (!file)
{
cout << "shibai_____________" << endl;
return;
}
//文件id与用户id对应
if (rq->m_lUserId == pupfile->user_id)
{
//将收到的数据写到文件里
file << rq->m_szFileContent;
pupfile->fileposition += rq->m_nNum;//收到的数据数
if (pupfile->fileposition == pupfile->filesize)//收到的数据数和文件大小相等
{
//文件传输完毕
file.close();
//将这个节点删除
auto ite = m_mapfileiftofileinfo.begin();
while (ite != m_mapfileiftofileinfo.end())
{
if ((*ite).second == pupfile)
{
m_mapfileiftofileinfo.erase(ite);
break;
}
ite++;
}
delete pupfile;
}
}
}
主函数
#include<iostream>
#include"TCPkernel.h"
using namespace std;
int main()
{
cout << "hello world" << endl;
Ikernel* k = TCPkernel::gettcpkernel();
if (k->Opensqlandnet())
{
cout << 1 << endl;
}
system("pause");
return 0;
}
客户端代码
一.计算md5
md5.h
#pragma once
#ifndef MD5_H
#define MD5_H
#include <string>
#include <fstream>
/* Type define */
typedef unsigned char byte;
typedef unsigned long ulong;
using std::string;
using std::ifstream;
/* MD5 declaration. */
class MD5 {
public:
MD5();
MD5(const void *input, size_t length);
MD5(const string &str);
MD5(ifstream &in);
void update(const void *input, size_t length);
void update(const string &str);
void update(ifstream &in);
const byte* digest();
string toString();
void reset();
private:
void update(const byte *input, size_t length);
void final();
void transform(const byte block[64]);
void encode(const ulong *input, byte *output, size_t length);
void decode(const byte *input, ulong *output, size_t length);
string bytesToHexString(const byte *input, size_t length);
/* class uncopyable */
MD5(const MD5&);
MD5& operator=(const MD5&);
private:
ulong _state[4]; /* state (ABCD) */
ulong _count[2]; /* number of bits, modulo 2^64 (low-order word first) */
byte _buffer[64]; /* input buffer */
byte _digest[16]; /* message digest */
bool _finished; /* calculate finished ? */
static const byte PADDING[64]; /* padding for calculate */
static const char HEX[16];
static const size_t BUFFER_SIZE = 1024;
};
#endif/*MD5_H*/
md5.cpp
#include "md5.h"
using namespace std;
/* Constants for MD5Transform routine. */
#define S11 7
#define S12 12
#define S13 17
#define S14 22
#define S21 5
#define S22 9
#define S23 14
#define S24 20
#define S31 4
#define S32 11
#define S33 16
#define S34 23
#define S41 6
#define S42 10
#define S43 15
#define S44 21
#define UINT4 unsigned int
/* F, G, H and I are basic MD5 functions.
*/
#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define I(x, y, z) ((y) ^ ((x) | (~z)))
/* ROTATE_LEFT rotates x left n bits.
*/
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
Rotation is separate from addition to prevent recomputation.
*/
#define FF(a, b, c, d, x, s, ac) { \
(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
#define GG(a, b, c, d, x, s, ac) { \
(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
#define HH(a, b, c, d, x, s, ac) { \
(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
#define II(a, b, c, d, x, s, ac) { \
(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
const byte MD5::PADDING[64] = { 0x80 };
const char MD5::HEX[16] = {
'0', '1', '2', '3',
'4', '5', '6', '7',
'8', '9', 'a', 'b',
'c', 'd', 'e', 'f'
};
/* Default construct. */
MD5::MD5() {
reset();
}
/* Construct a MD5 object with a input buffer. */
MD5::MD5(const void *input, size_t length) {
reset();
update(input, length);
}
/* Construct a MD5 object with a string. */
MD5::MD5(const string &str) {
reset();
update(str);
}
/* Construct a MD5 object with a file. */
MD5::MD5(ifstream &in) {
reset();
update(in);
}
/* Return the message-digest */
const byte* MD5::digest() {
if (!_finished) {
_finished = true;
final();
}
return _digest;
}
/* Reset the calculate state */
void MD5::reset() {
_finished = false;
/* reset number of bits. */
_count[0] = _count[1] = 0;
/* Load magic initialization constants. */
_state[0] = 0x67452301;
_state[1] = 0xefcdab89;
_state[2] = 0x98badcfe;
_state[3] = 0x10325476;
}
/* Updating the context with a input buffer. */
void MD5::update(const void *input, size_t length) {
update((const byte*)input, length);
}
/* Updating the context with a string. */
void MD5::update(const string &str) {
update((const byte*)str.c_str(), str.length());
}
/* Updating the context with a file. */
void MD5::update(ifstream &in) {
if (!in)
return;
std::streamsize length;
char buffer[BUFFER_SIZE];
while (!in.eof()) {
in.read(buffer, BUFFER_SIZE);
length = in.gcount();
if (length > 0)
update(buffer, length);
}
in.close();
}
/* MD5 block update operation. Continues an MD5 message-digest
operation, processing another message block, and updating the
context.
*/
void MD5::update(const byte *input, size_t length) {
ulong i, index, partLen;
_finished = false;
/* Compute number of bytes mod 64 */
index = (ulong)((_count[0] >> 3) & 0x3f);
/* update number of bits */
if((_count[0] += ((ulong)length << 3)) < ((ulong)length << 3))
_count[1]++;
_count[1] += ((ulong)length >> 29);
partLen = 64 - index;
/* transform as many times as possible. */
if(length >= partLen) {
memcpy(&_buffer[index], input, partLen);
transform(_buffer);
for (i = partLen; i + 63 < length; i += 64)
transform(&input[i]);
index = 0;
} else {
i = 0;
}
/* Buffer remaining input */
memcpy(&_buffer[index], &input[i], length-i);
}
/* MD5 finalization. Ends an MD5 message-_digest operation, writing the
the message _digest and zeroizing the context.
*/
void MD5::final() {
byte bits[8];
ulong oldState[4];
ulong oldCount[2];
ulong index, padLen;
/* Save current state and count. */
memcpy(oldState, _state, 16);
memcpy(oldCount, _count, 8);
/* Save number of bits */
encode(_count, bits, 8);
/* Pad out to 56 mod 64. */
index = (ulong)((_count[0] >> 3) & 0x3f);
padLen = (index < 56) ? (56 - index) : (120 - index);
update(PADDING, padLen);
/* Append length (before padding) */
update(bits, 8);
/* Store state in digest */
encode(_state, _digest, 16);
/* Restore current state and count. */
memcpy(_state, oldState, 16);
memcpy(_count, oldCount, 8);
}
/* MD5 basic transformation. Transforms _state based on block. */
void MD5::transform(const byte block[64]) {
ulong a = _state[0], b = _state[1], c = _state[2], d = _state[3], x[16];
decode(block, x, 64);
/* Round 1 */
FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
/* Round 2 */
GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */
GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
/* Round 3 */
HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */
HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
/* Round 4 */
II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
_state[0] += a;
_state[1] += b;
_state[2] += c;
_state[3] += d;
}
/* Encodes input (ulong) into output (byte). Assumes length is
a multiple of 4.
*/
void MD5::encode(const ulong *input, byte *output, size_t length) {
for(size_t i=0, j=0; j<length; i++, j+=4) {
output[j]= (byte)(input[i] & 0xff);
output[j+1] = (byte)((input[i] >> 8) & 0xff);
output[j+2] = (byte)((input[i] >> 16) & 0xff);
output[j+3] = (byte)((input[i] >> 24) & 0xff);
}
}
/* Decodes input (byte) into output (ulong). Assumes length is
a multiple of 4.
*/
void MD5::decode(const byte *input, ulong *output, size_t length) {
for(size_t i=0, j=0; j<length; i++, j+=4) {
output[i] = ((ulong)input[j]) | (((ulong)input[j+1]) << 8) |
(((ulong)input[j+2]) << 16) | (((ulong)input[j+3]) << 24);
}
}
/* Convert byte array to hex string. */
string MD5::bytesToHexString(const byte *input, size_t length) {
string str;
str.reserve(length << 1);
for(size_t i = 0; i < length; i++) {
int t = input[i];
int a = t / 16;
int b = t % 16;
str.append(1, HEX[a]);
str.append(1, HEX[b]);
}
return str;
}
/* Convert digest to string value */
string MD5::toString() {
return bytesToHexString(digest(), 16);
}
二.网络模块
inet.h 与服务器是相同的
tcpinet.h
#pragma once
#include "inet.h"
//#include"tcpkernel.h"
#include"packdef.h"
#include "ikerkel.h"
class tcpinet :
public inet
{
public:
tcpinet(Ikernel* pkernel);
~tcpinet();
public:
bool InitNetWork() ;//初始化网络
void UnInitNetWork();//关闭网络
bool SendData(char* szbuf, int nlen);//发送数据
void RecvData();//接收数据
static unsigned _stdcall ThredProc(void *);
public:
SOCKET m_sock;
HANDLE m_hThread;
bool m_flag;
Ikernel* m_pkernel;
};
tcpinet.cpp
#include "tcpinet.h"
#include<process.h>
//#include<WS2tcpip.h>
tcpinet::tcpinet(Ikernel* pkernel)
{
m_sock = NULL;
m_hThread = NULL;
m_flag = true;
m_pkernel = pkernel;
}
tcpinet::~tcpinet()
{
if (m_sock != NULL)
closesocket(m_sock);
tcpinet::UnInitNetWork();
}
bool tcpinet::InitNetWork()//常规初始化tcp网络
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
printf("WSAStartup failed with error: %d\n", err);
return 0;
}
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
printf("Could not find a usable version of Winsock.dll\n");
WSACleanup();
return 0;
}
printf("The Winsock 2.2 dll was found okay\n");
m_sock = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in sockline;
sockline.sin_family = AF_INET;
sockline.sin_port = htons(8890);//与服务器端口号相同
sockline.sin_addr.S_un.S_addr = inet_addr("192.168.3.238");//服务器的IP地址
//InetPtonW(AF_INET, L"127.0.0.1", &sockline.sin_addr);
if (SOCKET_ERROR == connect(m_sock, (sockaddr*)&sockline, sizeof(sockline)))
{
return 0;
}
/*char buf[1200] = "请求";
send(m_sock, buf, sizeof(buf), 0);*/
m_hThread = (HANDLE)_beginthreadex(0, 0, &ThredProc, this, 0, 0);//创建线程
return 1;
}
void tcpinet::UnInitNetWork()//断开网络
{
m_flag = false;
if (m_hThread)
{
if (WAIT_TIMEOUT == WaitForSingleObject(m_hThread, 100))
{
TerminateThread(m_hThread, -1);
}
CloseHandle(m_hThread);
m_hThread = NULL;
}
if (m_sock)
{
closesocket(m_sock);
m_sock = 0;
}
WSACleanup();
}
unsigned tcpinet::ThredProc(void*lop)//在线程里执行接收函数
{
tcpinet* pthis = (tcpinet*)lop;
pthis->RecvData();
return 0;
}
bool tcpinet::SendData(char* szbuf, int nlen)//发送函数,先发文件大小再发包内容
{
if (szbuf == NULL || nlen <= 0)
return 0;
if (send(m_sock, (char *)&nlen, sizeof(nlen), 0) <= 0)
return 0;
if (send(m_sock, szbuf, nlen, 0) <= 0)
return 0;
return 1;
}
void tcpinet::RecvData()
{
int nPackSize;
int nRecvNum;
char* pszbuf = NULL;
int noffset;
while (m_flag)
{
nRecvNum = recv(m_sock, (char*)&nPackSize, sizeof(int), 0);
if (nRecvNum <= 0)
{
//如果下线,则退出
if (WSAGetLastError() == WSAECONNRESET)
break;
continue;
}
if (nPackSize <= 0)continue;//接收到文件大小在继续
pszbuf = new char[nPackSize];
noffset = 0;
while (nPackSize)//按文件大小读取信息
{
nRecvNum = recv(m_sock, pszbuf + noffset, nPackSize, 0);
noffset += nRecvNum;
nPackSize -= nRecvNum;
}
//处理数据
m_pkernel->dealtext(pszbuf);
cout << pszbuf << endl;
// delete[]pszbuf;
// pszbuf = NULL;
}
}
三.处理模块
tcpkernel.h
#pragma once
#include"tcpinet.h"
// : public qobject , public Ikernel
#include<QObject>
#include<QWidget>
class tcpkernel : public QObject, public Ikernel
{
Q_OBJECT
public:
//tcpkernel(Qobject *parent) : qobject parent
tcpkernel();
~tcpkernel();
public:
bool Opensqlandnet();//启动数据库和网络
void Closesqlandnet();//关闭
void dealtext(char*); // 处理
bool SendData(char* sbuf, int nlen);
public:
inet* m_pnet;
signals:
void signal_char(char *);
void signal__DEF_PROTOCOL_LOGIN_RS(char *);
void signal__DEF_PROTOCOL_GETFILELIST_RS(char *);
void signal__STRU_UPLOAD_FILEINFO_RS(char *);
};
tcpkernel.cpp
#include "tcpkernel.h"
//#include"packdef.h"
tcpkernel::tcpkernel()
{
m_pnet = new tcpinet(this);
}
tcpkernel::~tcpkernel()
{
}
bool tcpkernel::Opensqlandnet()
{
m_pnet->InitNetWork();
return 1;
}
void tcpkernel::Closesqlandnet()
{
m_pnet->UnInitNetWork();
}
void tcpkernel::dealtext(char*a)//对应不同类型发送信号
{
switch (*a)
{
case _DEF_PROTOCOL_REGISTER_RS:
emit signal_char(a);
break;
case _DEF_PROTOCOL_LOGIN_RS:
emit signal__DEF_PROTOCOL_LOGIN_RS(a);
break;
case _DEF_PROTOCOL_GETFILELIST_RS:
emit signal__DEF_PROTOCOL_GETFILELIST_RS(a);
break;
case _DEF_PROTOCOL_UPLOAD_FILEINFO_RS:
emit signal__STRU_UPLOAD_FILEINFO_RS(a);
break;
}
}
bool tcpkernel::SendData(char* sbuf, int nlen)
{
m_pnet->SendData(sbuf,nlen);
return 0;
}
四.页面
登录界面
dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include"ikerkel.h"
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = nullptr);
~Dialog();
private slots:
void on_button_queding_clicked();//注册按钮
void on_pushButton_2_clicked();//登录按钮
private:
Ui::Dialog *ui;
Ikernel *m_ikernel;
public:
void getkernel(Ikernel * kernel);//获得处理类
};
#endif // DIALOG_H
dialog.cpp
#include "dialog.h"
#include<string>
#include"packdef.h"
#include"ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::getkernel(Ikernel * kernel)
{
m_ikernel = kernel;
}
void Dialog::on_button_queding_clicked()//注册
{
QString name,tel,password;
tel = ui->lineEdit->text();//获得电话号
name = ui->lineEdit_2->text();//获得用户名
password = ui->lineEdit_3->text();//获得密码
STRU_REGISTER_RQ rq;
rq.m_nType = _DEF_PROTOCOL_REGISTER_RQ;
rq.m_ltel = tel.toLongLong();
strcpy(rq.m_szName,name.toStdString().c_str());
strcpy(rq.m_szPassword,password.toStdString().c_str());
m_ikernel->SendData((char*)&rq,sizeof(rq));//发送注册请求包
}
void Dialog::on_pushButton_2_clicked()//登录
{
QString name,tel,password;
tel = ui->lineEdit->text();//获得电话号
name = ui->lineEdit_2->text();//获得用户名
password = ui->lineEdit_3->text();//获得密码
STRU_LOGIN_RQ rq;
rq.m_nType = _DEF_PROTOCOL_LOGIN_RQ;
strcpy(rq.m_szName,name.toStdString().c_str());
strcpy(rq.m_szPassword,password.toStdString().c_str());
m_ikernel->SendData((char*)&rq,sizeof(rq));//发送登录请求包
}
主界面
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include"dialog.h"
#include<QMessageBox>
#include"ikerkel.h"
#include<QFile>
#include<QFileDevice>
#include"md5.h"
#include"packdef.h"
#include<qlist.h>
#include<list>
#include"cmytheadpool.h"
struct uploadfileinfo{
char m_szFileMD5[MAXSIZE];
char m_szFilepath[MAXSIZE];
long long filesize;
long long fileid;
long long m_position;
QFile * m_file;
};
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
QList<uploadfileinfo*> m_lisfileinfo; //储存上传的文件信息
public:
Ui::Widget *ui;
Dialog *m_dia;//登陆界面
Ikernel *m_ikernel;//处理模块
int m_user_id;//用户id
CMyTheadPool m_threadpool;//线程池
string FileDigest(const string &file);//计算md5值的函数
signals:
void signal_uploadfilesuccess(uploadfileinfo*);
public slots:
void slot_char(char*);
void slot__DEF_PROTOCOL_LOGIN_RS(char*);
void slot__DEF_PROTOCOL_GETFILELIST_RS(char*);
void slot__STRU_UPLOAD_FILEINFO_RS(char *);
void slot_uploadfilesuccess(uploadfileinfo*);
private slots:
void on_pushButton_clicked();//上传文件按钮
};
class filedir : public Itask//继承的任务结构体,存储文件的信息
{
public:
filedir(STRU_UPLOAD_FILEINFO_RS* rs,uploadfileinfo *pfileinfo,Widget *pwidght)//构造函数
{
m_rs = rs;
m_pfileinfo = pfileinfo;
m_pwidght = pwidght;
}
public:
STRU_UPLOAD_FILEINFO_RS* m_rs; //上传文件信息回复包
uploadfileinfo *m_pfileinfo;//想要上传的文件信息
Widget *m_pwidght;
public:
void Run();//上传文件内容的函数
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include"tcpkernel.h"
#include<qfiledialog.h>
//打开文件用的库
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
m_dia = new Dialog;
m_dia->show();
m_ikernel = new tcpkernel;
m_dia->getkernel(m_ikernel);
// 连接网络失败弹窗
//获得系统cpu个数 CPU* 2
SYSTEM_INFO s1;
GetSystemInfo(&s1);
m_threadpool.CreateThreadPool(1,s1.dwNumberOfProcessors * 2);
QObject::connect((tcpkernel*)m_ikernel,SIGNAL(signal_char(char *)),this,SLOT(slot_char(char*)));
QObject::connect((tcpkernel*)m_ikernel,SIGNAL(signal__DEF_PROTOCOL_LOGIN_RS(char *)),this,SLOT(slot__DEF_PROTOCOL_LOGIN_RS(char*)));
QObject::connect((tcpkernel*)m_ikernel,SIGNAL(signal__DEF_PROTOCOL_GETFILELIST_RS(char *)),this,SLOT(slot__DEF_PROTOCOL_GETFILELIST_RS(char*)));
QObject::connect((tcpkernel*)m_ikernel,SIGNAL(signal__STRU_UPLOAD_FILEINFO_RS(char *)),this,SLOT(slot__STRU_UPLOAD_FILEINFO_RS(char*)));
QObject::connect(this,SIGNAL(signal_uploadfilesuccess(FileInfo*)),this,SLOT(slot_uploadfilesuccess(uploadfileinfo*)));
if(!m_ikernel->Opensqlandnet())
QMessageBox::information(m_dia,"shibai","jixu");
}
Widget::~Widget()
{
delete ui;
m_threadpool.DestroyThreadPool();
}
void Widget::slot_char(char*buf)//处理注册回复包,发一个弹窗
{
STRU_REGISTER_RS *rs = (STRU_REGISTER_RS*) buf;
QString qstr;
if(rs->m_szResult == _register_failed )
qstr = "_register_failed";
else
qstr = "_register_success";
QMessageBox::information(this,"fsd",qstr);
delete[]buf;
buf = NULL;
}
void Widget::slot__DEF_PROTOCOL_LOGIN_RS(char*buf)//处理登录回复包
{
STRU_LOGIN_RS *rs = (STRU_LOGIN_RS*)buf;
QString qstr;
if(rs->m_szResult == _login_user_noexist)
qstr = "未注册";
else if(rs->m_szResult == _login_password_err)
qstr = "密码错误";
else
{
qstr= "成功登录";
//转换窗口
m_user_id = rs->m_luserid;
m_dia->hide();
this->show();
//获取文件列表请求
STRU_GETFILELIST_RQ rq;
rq.m_nType = _DEF_PROTOCOL_GETFILELIST_RQ;
rq.m_luserid = m_user_id;
m_ikernel->SendData((char*)&rq,sizeof(rq));//向服务器发送获取文件列表请求
return ;
}
QMessageBox::information(this,"fsd",qstr);
delete[]buf;
buf = NULL;
}
void Widget::slot__DEF_PROTOCOL_GETFILELIST_RS(char*buf)//处理获取文件列表回复包
{
STRU_GETFILELIST_RS *rs = (STRU_GETFILELIST_RS*)buf;
int i = 0;
QString qstr;
while(i < rs->m_nFileNum)
{
qstr = QString::fromStdString( rs->m_aryFile[i].m_szFileName);
ui->tableWidget_1->setItem(i,0,new QTableWidgetItem(qstr));
qstr = QString::number(rs->m_aryFile[i].m_fileSize);
ui->tableWidget_1->setItem(i,1,new QTableWidgetItem(qstr));
i++;
this->show();
}
//单行选中
ui->tableWidget_1->setSelectionBehavior(QAbstractItemView::SelectRows);
ui->tableWidget_1->setSelectionMode(QAbstractItemView::SingleSelection);
}
string Widget::FileDigest(const string &file) //计算md5值的函数
{
ifstream in(file.c_str(), std::ios::binary);
if (!in)
return "";
MD5 md5;
std::streamsize length;
char buffer[1024];
while (!in.eof()) {
in.read(buffer, 1024);
length = in.gcount();
if (length > 0)
md5.update(buffer, length);
}
in.close();
return md5.toString();
}
void Widget::slot_uploadfilesuccess(uploadfileinfo *fileinfo)//上传文件成功
{
int i = 0;
QString qstr = fileinfo->m_szFilepath;
qstr.section('/',-1);
ui->tableWidget_1->setItem(i,0,new QTableWidgetItem(qstr));
qstr = fileinfo->filesize;
ui->tableWidget_1->setItem(i,1,new QTableWidgetItem(qstr));
QMessageBox::information(this,"111","successful");
}
void Widget::slot__STRU_UPLOAD_FILEINFO_RS(char *buf)//处理文件上传请求回复包
{
STRU_UPLOAD_FILEINFO_RS* rs = (STRU_UPLOAD_FILEINFO_RS*)buf;
auto ite = m_lisfileinfo.begin();
QList<uploadfileinfo*>::iterator itetemp = NULL;
uploadfileinfo *pfileinfo = NULL;
while(ite != m_lisfileinfo.end())
{
if(strcmp(rs->m_szFileMD5,(*ite)->m_szFileMD5) == 0)
{
pfileinfo = *ite;
itetemp = ite;
break;
}
ite ++;
}
if(rs->bresult == _upload_file_exists)
{
QMessageBox::information(m_dia,"fasd","成功了");
// 完成传递,告诉已经成功
}
else if(rs->bresult == _upload_file_continue ||
rs->bresult == _upload_file_normal)
{
//断点续传和正常传
Itask *pitask = new filedir(rs,pfileinfo,this);
m_threadpool.Push(pitask);//加入到任务队列中
}
else if(rs->bresult == _upload_file_success)
{
//秒传
//拿出文件信息
QString filepath = pfileinfo->m_szFilepath;
QString filename = filepath.section('/',-1);
QString filemd5 = pfileinfo->m_szFileMD5;
long long filesize = pfileinfo->filesize;
QString strfilesize = QString::number(filesize);
//显示文件信息
int nrow = 0;
ui->tableWidget_1->setItem(nrow,0,new QTableWidgetItem(filename));
ui->tableWidget_1->setItem(nrow,1,new QTableWidgetItem(strfilesize));
delete pfileinfo;
m_lisfileinfo.erase(itetemp);
}
}
void Widget::on_pushButton_clicked()//点击上传文件按钮
{
QString qstrfilepath = QFileDialog::getOpenFileName(this,"文件",".");
//打开文件列表
long long filesize;
if(!qstrfilepath.isEmpty())
{
// 获取文件名字
QString qstr = qstrfilepath.section('/',-1);//找什么,1从左,2从右
//qt类型转为string
string stdstr = qstrfilepath.toStdString();
//需要导入md5文件,使用md5函数
string md5 = FileDigest(stdstr);
QFile file(qstrfilepath);//文件路径
file.open(QIODevice::ReadOnly);
if(file.isOpen())
{
filesize = file.size();
}
file.close();
//上传文件信息包
STRU_UPLOAD_FILEINFO_RQ rq;
rq.m_nType = _DEF_PROTOCOL_UPLOAD_FILEINFO_RQ;
rq.m_lUserId = m_user_id;
rq.m_lFileSize = filesize;
strcpy(rq.m_szFileMD5,md5.c_str());
strcpy(rq.m_szFileName,qstr.toStdString().c_str());
m_ikernel->SendData((char*)&rq,sizeof(rq));
//将信息记录在链表当中
uploadfileinfo *pnew = new uploadfileinfo;
pnew->fileid = 0;
pnew->m_file = 0;
pnew->filesize = filesize;
pnew->m_position = 0;
strcpy(pnew->m_szFileMD5,md5.c_str());
strcpy(pnew->m_szFilepath,qstrfilepath.toStdString().c_str());
m_lisfileinfo.push_back(pnew);
}
}
void filedir::Run()//上传文件的运行函数
{
STRU_UPLOAD_FILECONTENT_RQ rq;
rq.m_nType = _DEF_PROTOCOL_UPLOAD_FILECONTENT_RQ ;
rq.m_lUserId = m_pwidght->m_user_id;
rq.m_lFileId = m_rs->m_lFileId;
QFile fil(m_pfileinfo->m_szFilepath);
fil.open(QIODevice::ReadOnly);
if(m_rs->bresult == _upload_file_continue && m_rs->m_lPosition > 0)//从之前上传的断点开始传
{
fil.seek(m_rs->m_lPosition);
}
while(1)//向服务器发送数据
{
rq.m_nNum = fil.read(rq.m_szFileContent,sizeof(rq.m_szFileContent));
if(rq.m_nNum > 0)
m_pwidght->m_ikernel->SendData((char*)&rq,sizeof(rq));
else
break;
}
fil.close();
emit m_pwidght->signal_uploadfilesuccess(m_pfileinfo);//文件上传成功,发送信号
}
五.线程池
cmytheadpool.h
#ifndef CMYTHEADPOOL_H
#define CMYTHEADPOOL_H
#include <windows.h>
#include <list>
#include <queue>
#include <process.h>
#include <stdio.h>
class Itask{
public:
Itask()
{}
virtual ~ Itask()
{}
public:
virtual void Run() = 0;
};
class CMyTheadPool
{
public:
CMyTheadPool();
public:
//1.创建线程池
bool CreateThreadPool(long lMinThreadNum,long lMaxThreadNum);
void DestroyThreadPool();//2.销毁线程池
bool Push(Itask *); //3.投递任务
static unsigned _stdcall ThreadProc(void *); //4.线程执行函数
private:
std::list<HANDLE> m_lstThread;//线程存放的链表
std::queue<Itask*> m_qItask;//任务数组
bool m_bFlagQuit;//推出标志
HANDLE m_hSemaphore;//信号
long m_lMaxThreadNum; //最多的线程个数 5
long m_lCreateThreadNum; //当下存在的线程个数 2
long m_lRunThreadNum; // 正在工作的线程数 1
HANDLE m_hMutex;//信号量
};
#endif // CMYTHEADPOOL_H
cmytheadpool.cpp
#include "cmytheadpool.h"
CMyTheadPool::CMyTheadPool()
{
m_bFlagQuit = true;
m_lMaxThreadNum = 0;
m_lCreateThreadNum = 0;
m_lRunThreadNum = 0;
m_hMutex = CreateMutexW(0,0,0);
}
bool CMyTheadPool::CreateThreadPool(long lMinThreadNum,long lMaxThreadNum)
{
//1.校验参数
if(lMinThreadNum <=0 || lMaxThreadNum <lMinThreadNum)
return false;
//线程资源
m_hSemaphore = CreateSemaphoreW(0,0,lMaxThreadNum,0);
//2.创建一些线程
for(long i =0;i <lMinThreadNum;i++)
{
HANDLE hThread =(HANDLE)_beginthreadex(0,0,&CMyTheadPool::ThreadProc,this,0,0);
if(hThread)
m_lstThread.push_back(hThread);
}
m_lMaxThreadNum = lMaxThreadNum;
m_lCreateThreadNum = lMinThreadNum;
return true;
}
bool CMyTheadPool::Push(Itask *pItask)
{
//1.校验参数
if(!pItask)
return false;
//2.将任务加入队列中
WaitForSingleObject(m_hMutex,INFINITE);
m_qItask.push(pItask);
ReleaseMutex(m_hMutex);
//3.释放信号量
// ReleaseSemaphore(m_hSemaphore,1,0);
//1.有空闲的线程
//2.没有空闲的线程
if(m_lRunThreadNum ==m_lCreateThreadNum &&
m_lCreateThreadNum <m_lMaxThreadNum )
{
HANDLE hThread =(HANDLE)_beginthreadex(0,0,&CMyTheadPool::ThreadProc,this,0,0);
if(hThread)
m_lstThread.push_back(hThread);
m_lCreateThreadNum++;
}
//3.爆满
ReleaseSemaphore(m_hSemaphore,1,0);
return true;
}
unsigned _stdcall CMyTheadPool::ThreadProc(void * lpvoid)
{
CMyTheadPool *pthis = (CMyTheadPool*)lpvoid;
Itask *pItask = NULL;
while(pthis->m_bFlagQuit)
{
//阻塞等信号
WaitForSingleObject(pthis->m_hSemaphore,INFINITE);
//阻塞--就绪--运行
//pthis->m_lRunThreadNum++;
InterlockedIncrement(&pthis->m_lRunThreadNum);
//从队列中取任务
while(!pthis->m_qItask.empty())
{
WaitForSingleObject(pthis->m_hMutex,INFINITE);
if(pthis->m_qItask.empty())
{
ReleaseMutex(pthis->m_hMutex);
break;
}
pItask = pthis->m_qItask.front();
pthis->m_qItask.pop();
ReleaseMutex(pthis->m_hMutex);
//执行任务
pItask->Run();
delete pItask;
pItask = NULL;
}
//pthis->m_lRunThreadNum--;
InterlockedDecrement(&pthis->m_lRunThreadNum);
}
return 0;
}
void CMyTheadPool::DestroyThreadPool()
{
m_bFlagQuit = false;
//检查线程是否退出,如果没有退出,则强制杀死
auto ite = m_lstThread.begin();
while(ite != m_lstThread.end())
{
if(WAIT_TIMEOUT == WaitForSingleObject(*ite,100))
TerminateThread(*ite,-1);
CloseHandle(*ite);
*ite = NULL;
ite++;
}
if(m_hSemaphore)
{
CloseHandle(m_hSemaphore);
m_hSemaphore = NULL;
}
Itask *pItask = NULL;
while(!m_qItask.empty())
{
pItask = m_qItask.front();
m_qItask.pop();
delete pItask;
pItask = NULL;
}
if(m_hMutex)
{
CloseHandle(m_hMutex);
m_hMutex = NULL;
}
}