用了C#再回来用C++写真的有一种我已经不属于这个世界的感觉。C++的下载就没有C#那么方便了,我用的是libcurl.dll,官网上下载的源码自己cmake出来编译的,c++的库引用有debug和release之分,所以刚开始写的时候用的是debug版,然后后来写完了之后改成了release版。
先上代码再来讲吧。
curl_global_init(CURL_GLOBAL_DEFAULT);curl_global_cleanup();
这个是必须要有的全局初始化和清除,所以我把它放在了下载类的构造和析构里。
下载函数:
CURL * GetConfigCurlHandle = curl_easy_init(); //初始化一个curl,注意线程之间不能共享这个curl,多线程的话就每个线程自己设置一个curl FtpFile ftpfile = { FileName.data(), NULL }; string strpath = Config::GetInstance()->m_sDownloadFile + "\\" + FileName; fopen_s(&ftpfile.stream, strpath.data(), "ab+"); //一定要有+不然下一次写入就把之前的覆盖了 curl_easy_setopt(GetConfigCurlHandle, CURLOPT_URL, url.data()); //注册url curl_easy_setopt(GetConfigCurlHandle, CURLOPT_WRITEFUNCTION, DownloadCallback); //注册一个回调函数用于写入文件 curl_easy_setopt(GetConfigCurlHandle, CURLOPT_WRITEDATA, &ftpfile); curl_easy_setopt(GetConfigCurlHandle, CURLOPT_BUFFERSIZE, CURL_MAX_WRITE_SIZE); //设置缓冲区大小 ConfigRetcCode = curl_easy_perform(GetConfigCurlHandle); if (ftpfile.stream) { string str = ftpfile.filename; //Config::GetInstance()->WriteLog("关闭文件"+str); fclose(ftpfile.stream); } if (ConfigRetcCode != CURLE_OK) { int co = ConfigRetcCode; string str = FileName + "下载失败errorcode:"+Config::GetInstance()->FloatToString(co); Config::GetInstance()->WriteLog(str); if (ConfigRedownloadTime<10) { DownloadConfigFile(url, FileName); } ConfigRedownloadTime++; } else { return; }
下载全部结束之后要清除这个curl:
curl_easy_cleanup(GetConfigCurlHandle);
有两个比较重要的坑了我的点,一个就是注册url的那里,我的url是string类型,直接传入string类型是不会报错的,但是这个下载会返回一个错误,错误码我不记得了大概就是找不到这个url的意思。而传入url.data()就没错了,也就是相当于把string转成了char*。其实C++中很多的函数都是不能识别string类型的参数的,在c++中传参还是少用string的好。至于原因哪个大牛能告诉我一下好想知道!!!写c#的时候只想怎么用,但是写c++就好想知道原理是怎么回事!
第二个点是我把打开文件和关闭文件的函数放在了回调函数中,但是如果是一个大文件的话他是没有办法一次就写完的,所以就会不断地去调用回调函数写入文件,也就会不断地打开和关闭,然后就出现了文件被占用导致写入失败的问题。这个我真的查了很久的原因,因为c++不像C#,C++的报错总是在很底层,我常说c++总是断在神奇的位置。于是我把下载线程屏蔽掉然后在主线程中循环的打开关闭这个文件夹,在打开和关闭中做一些耗时操作,结果并没有报错那说明文件并没有被其他线程占用,那么也就只可能是下载线程的问题。但是网上所有的关于这个libcurl下载大文件的问题都没有解释。后来有个大牛朋友跟我说了一个直写和后写的问题,他说文件写入磁盘分为直写和后写,直写就是调用写入函数的时候就写入磁盘,后写就是过一会再写入磁盘。我不知道具体的原因但是这个给了我启发,是不是文件写入磁盘的时候还没有写完而我就进行了下一次打开写入操作,虽然每一次打开文件之后我都关闭了文件,但是他仅仅只是对文件的操作成功了,而操作系统还在继续把文件写入磁盘中,所以导致下一次写入的时候报错的错误码是文件被占用。而我把打开和关闭操作放在了回调函数外也就是一个文件只进行一次打开和关闭就没有问题了。不知道自己的想法对不对希望有大牛能给我解释一下具体原因。跪谢~~~
还有一个点是我在C#中用http下载没有遇到过的就是下载失败的问题,下载失败原因很多,看错误码基本都是网络问题啊什么的,所有加入了一个循环递归,如果下载不成功就循环下载十次直到成功,如果十次还不成功那就只能告诉玩家你网不好待会再来吧。。。。。。这个在C#中没有遇到过可能是http自己封装了下载失败的处理程序吧。