文件传输主要是学习文件处理的windows API函数、数据结构操作和 string操作。
首先应用库来建立一个服务器和一个客户端,都是单线程。
服务器说明:建立一个结构体tTransFile
struct tTransFile
{
int nLen;
int nIdx;
char szFileName[255];
UINT32 uFileSize;
char* GetData()
{
return (char*) (this + 1);
}
}
我建立的是一个比较简单的文件传输,要传入一个存储该文件的路径,然后分析这个路径,需要应用API函数FindFirstFile
HANDLE FindFirstFile(
LPCTSTR lpFileName, //文件或文件夹路经r
LPWIN32_FIND_DATA lpFindFileData
);
BOOL FindNextFile(
HANDLE hFindFile,
LPWIN32_FIND_DATA lpFindFileData
);
BOOL FindClose(HANDLE hFindFile );
typedef struct _WIN32_FIND_DATA {
DWORD dwFileAttributes; //文件属性
FILETIME ftCreationTime; // 文件创建时间
FILETIME ftLastAccessTime; // 文件最后一次访问时间
FILETIME ftLastWriteTime; // 文件最后一次修改时间
DWORD nFileSizeHigh; // 文件长度高32位
DWORD nFileSizeLow; // 文件长度低32位
DWORD dwReserved0; // 系统保留
DWORD dwReserved1; // 系统保留
TCHAR cFileName[ MAX_PATH ]; // 长文件名
TCHAR cAlternateFileName[ 14 ]; // 8.3格式文件名
} WIN32_FIND_DATA, *PWIN32_FIND_DATA;
lpFileNameString,欲搜索的文件名。参数可如下:
c:\Windows\*.* //在c:\Windows目录中查找所有文件
c:\Windows\System32\*.dll //在c:\Windows\System32目录中查找所有dll文件
c:\Windows\System.ini ;在c:\Windows目录中查找System.ini文件
c:\Windows\a???.* // 在c:\Windows目录中查找所有以a开头的文件名长度.为4个字符的文件
Test.dat //在当前目录查找Test.dat文件
*.* //在当前目录查找所有文件
也可以使用下面这个方法获取文件信息:
BOOL GetFileInformationByHandle(
HANDLE hFile, // 文件的句柄
LPBY_HANDLE_FILE_INFORMATION lpFileInformation
);
typedef struct _BY_HANDLE_FILE_INFORMATION {
DWORD dwFileAttributes;
FILETIME ftCreationTime;
FILETIME ftLastAccessTime;
FILETIME ftLastWriteTime;
DWORD dwVolumeSerialNumber; // 文件所在的磁盘的序列号
DWORD nFileSizeHigh;
DWORD nFileSizeLow;
DWORD nNumberOfLinks; //链接的数目
DWORD nFileIndexHigh;
DWORD nFileIndexLow;
} BY_HANDLE_FILE_INFORMATION;
我主要应用的是FindFirstFile,WIN32_Find_DATA 应用里面的cFileName,然后获得HANDLE后就可以接着查询应用FINDNXETFILE,里面应用的结构体相同,也就是同样我们要取的都是文件的name,当然第一次和第二次取出来的都是”。“,”。。“,这是文件的两级目录,然后才能取到第一个文件的名称,然后一直循环取FINDNEXTFILE,当然是成功的情况下,如果失败返回INVALID_HANDLE_VALID。我在File_Server类中,还建立一个list<string>安放取到的文件名称。
为处理list<string>我还建立一个iterator it,用他来取用队列中的string字符串。下面就应该是接到信息然后分析这个信息操作。
这个信息就是协议,也是结构体。我收到的可能不是协议类型,如此我们强制转换该数据,因为这就是一个下载服务器(当然我是这么认为的,内网中可用),主要监听的就是要下载第几个文件,知道nIdx后,就可以操作知道it的指向队列中第几个字符串,然后通过FindAndOpenFile函数(这个是我自己起的名不是API),FindAndOpenFile主要是字符串操作把该文件的路径拼完全,然后用此路径调用API函数CreateFile获取文件句柄hFile,用hFile先调用GetFileSize,获取文件的大小,然后再掉SetFilePointor,确定文件每次开始读取都从文件开始处开始。
回信息操作:1、获取文件大小赋值给结构体中uFileSize
2、获取文件名称复制给szFileName
3、分析读取信息ReadFile中GetLeng,把第一读取的大小记录下来。
4、填充整个数据的大小nLen,他的大小包括第一次读取到的文件的大小+结构体的大小。
5、这个最重要差点忘了,就是可能数据比较到,只应用结构体操作可能他的存放空间不够,也就是在栈上操作空间不够,如此我们需要在堆上操作这个要发送出去的数据。new char[MAX_SIZE_SEND],然后应用强制转换等等操作,就可以把这个数据发出去,(重点是send中数据的长度,不可以用sizeof+指针,要用nLen的长度).
6、后面的操作就是在文件大小和读取大小之和比较的条件下,开始不断的循环发生数据。直到完成发送,完成后,记得把it还原。关闭HANDLE,回收堆内存。
7、忘啦一条,就是关闭HANDLE,应用完FindNextFile后要关闭HANDLE。
客户端操作:
这个比较简单,1、获取文件存储的地址
2、建立和服务器连接的条件端口和IP
3、建立连接后,发送信息,就是结构体,也是协议,主要填写的是nIdx,就是要下那个文件。发送这个数据操作和服务器中5相同的操作。发送完后要回收堆内存。
4、接收信息,首先要分析这个数据,分析协议,取出文件大小、文件名称、还有数据和结构体分离。
5、根据结构中的文件名称和1的操作通过字符串操作CreateFile,并且记录下第一次收到的数据的大小。
6、开始应用WriteFile写入文件,两方面来保证收到的数据完成,一是recv,二是收到的数据和文件大小比较,如果相等就写入完成。
7、处理关闭不需要的HANDLE,SOCKET和各种堆内存。