强劲的多线程下载器 purl.exe
[i=s] 本帖最后由 happy886rr 于 2017-8-26 08:54 编辑 [/i][quote]
控制台下强劲的多线程下载器,快如闪电。支持purl.ini配置文件。批量下载各类url资源链。
用法:
purl -i[配置文件.ini] -l[输出日志] -n[线程数] -r[根目录]\n\
配置文件书写格式
#############################################################################
[url]http://www.a.com/[/url][:0].[:1]
#############################################################################
[:0]=01-99
#############################################################################
[:1]=jpg,gif,png,bmp
[:2]=
[:3]=
...
其中[:0]到[:9]为URL通配符,启用[:0]后具有多线程效益,启用[:9]具有尾加速功能。这10个通配符可以是数字范围如:[:0]=1-9;也可以是字符范围,如[:1]=a-z;还可以是字符串枚举,如:[:2]=jpg,mp3,txt,doc
[/quote]
为节省论坛空间,不提供任何附件下载,源码发行。[code]/*
THE THREAD DOWNLOAD TOOL, COPYRIGHT@2017~2019 BY HAPPY, VERSION 1.0
PURL.EXE
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <process.h>
#include <direct.h>
#include <time.h>
#include <math.h>
#include <io.h>
#pragma comment(lib, "Ws2_32.lib")
#pragma comment (lib,"urlmon.lib")
/*************宏量定义*************/
#define SAFE_SIZE 512
#define FILE_EXIST 0
//定义配置文件
#define PURL_INI "\
#################################################################\n\
http://\n\
#################################################################\n\
[:0]=\n\
#################################################################\n\
[:1]=\n\
[:2]=\n\
[:3]=\n\
[:4]=\n\
[:5]=\n\
[:6]=\n\
[:7]=\n\
[:8]=\n\
#################################################################\n\
[:9]=\n\
#################################################################\n"
//定义帮助说明
#define HELP_INFORMATION "\
-----------------------------------------------------------------\n\
purl -i[inifile] -l[outlog] -n[threadnum] -r[rootdir]\n\
-----------------------------------------------------------------\n\
-h Show help information\n\
-i Set the purl inifile\n\
-l Set the log output directory\n\
-n Set the number of concurrent threads to download\n\
-r Set the download root directory\n\
-----------------------------------------------------------------\n\
version 1.0"
/*************全局变量*************/
static int M[10], N[10], Z[10];
static int prointThread=0, nowsThread=0, trueDownload=0, totalCYC=1;
static char murl[SAFE_SIZE], outlog[SAFE_SIZE], inifile[SAFE_SIZE]=".\\purl.ini", mpath[SAFE_SIZE]=".\\", settingini[10][SAFE_SIZE*2], *S[10][SAFE_SIZE];
static BOOL renMARK[10];
static BOOL urlMARK=FALSE;
FILE* logfp;
//进度条容器
static const char* proGRESS="[ ]\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";
/*************事件变量*************/
//创建事件对象
HANDLE ghDataEvent;
/*************功能函数*************/
//切分字串
int ArgsSpliy(char* Str, int SN)
{
int i=0;
char* pstr=strtok(Str, " \t\n,");
while(pstr !=NULL){
S[SN][i]=(char*)calloc(strlen(pstr)+1, sizeof(char));
strcpy(S[SN][i++], pstr);
pstr=strtok(NULL, " \t\n,");
}
return i;
}
//判断字母
BOOL isLetter(char* p)
{
return (p && (('A'<=*p&&*p<='Z')||('a'<=*p&&*p<='z'))&&(*(p+1)=='\0'))?TRUE:FALSE;
}
//彩显函数
int ColorString(HANDLE handle_out, char* pstrA, char* pstrB, int colorA, int colorB, int num)
{
SetConsoleTextAttribute(handle_out, colorA); //项目色
fprintf(stdout, pstrA);
SetConsoleTextAttribute(handle_out, colorB); //数据色
(num<0)?fprintf(stdout, pstrB):fprintf(stdout, "%d\n", num);
return 0;
}
//光标函数
BOOL DispyCursor(int size,bool mode)
{
CONSOLE_CURSOR_INFO cinfo ={(DWORD)size, mode};
return SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cinfo);
}
//域名IP解析
int DownloadURLtoIP(HANDLE handle_out, const char* downloadURL)
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2,2), &wsaData);
char *pstr=(char*)downloadURL, *urlweb=(char*)calloc(SAFE_SIZE, sizeof(char)), *tstr=urlweb;
while(*pstr!=':'){pstr++;}
pstr+=3;
while(*pstr!='/'&& *pstr!='\n' && *pstr!='\0'){
*tstr++=*pstr++;
}
const char* pszUrl=(const char*)urlweb;
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
//fprintf(stdout, "Init socket faile\n");
return 1;
}
struct hostent *pHost = gethostbyname(pszUrl);
//释放空间
free(urlweb);
if (pHost == NULL)
{
WSACleanup();
//fprintf(stdout, "pHost == NULL \n");
return 1;
}
//主机正式名称
ColorString(handle_out, "[URL-HOST] ", (pHost)?pHost->h_name:"NULL", 4|8, 1|2|8, -1);
fprintf(stdout, "\n");
//主机别名,可以有多个
int iIndex = 0;
while(pHost->h_aliases[iIndex])
{
ColorString(handle_out, "[URLALIAS] ", pHost->h_aliases[iIndex], 4|8, 1|2|8, -1);
fprintf(stdout, "\n");
iIndex++;
}
//地址字节数,IPV4是4,IPV6是6
if(pHost->h_length==4)
{
ColorString(handle_out, "[IPV-TYPE] ", "IPV4\n", 4|8, 4|8, -1);
}
else if(pHost->h_length==6)
{
ColorString(handle_out, "[IPV-TYPE] ", "IPV6\n", 4|8, 4|8, -1);
}
iIndex = 0;
while(pHost->h_addr_list[iIndex])
{
ColorString(handle_out, "[IP-ADDRE] ", inet_ntoa(*(struct in_addr*)pHost->h_addr_list[iIndex]), 4|8, 4|2|8, -1);
fprintf(stdout, "\n");
iIndex++;
}
WSACleanup();
return 0;
}
//开关解析
int OPTIND=1, OPTOPT;
char *OPTARG;
int GetOpt(int nargc, char *nargv[], char *ostr)
{
static char* place="";
static char* lastostr=(char *) 0;
register char* oli;
char* index();
if(ostr!=lastostr){
lastostr=ostr;
place="";
}
if(!*place)
{
if(
(OPTIND>=nargc) ||
(*(place=nargv[OPTIND])!='-')||
(!*(++place))
){
place="";
return(EOF);
}
if (*place == '-' && place[1] == 0){
++OPTIND;
return(EOF);
}
}
if ((OPTOPT=(int)*place++)==(int)':' || !(oli=strchr(ostr, OPTOPT))){
if(!*place){++OPTIND;}
}
if (oli != NULL && *(++oli) != ':'){
OPTARG=NULL;
if(!*place){++OPTIND;}
}
else{
if(*place){
OPTARG=place;
}else if(nargc<=++OPTIND){
place="";
}else{
OPTARG=nargv[OPTIND];
}
place="";
++OPTIND;
}
return(OPTOPT);
}
/*************获取配置*************/
int ReadSetting(char* pfile)
{
//读取脚本文件
FILE* fp;
if( (fp=fopen(pfile, "r"))==NULL ){
fprintf(stdout, "Reading settings failed!\n");
exit(1);
}
int SN;
//分配行容器
char* LCache=(char*)calloc(1025, sizeof(char));
//辅助行指针
char* Line=NULL;
while(!feof(fp)){
memset(LCache, 0, 1024*sizeof(char));
fgets(LCache, 1024, fp);
//指针置换
Line=LCache;
//将尾部换行符注掉
while(*Line!='\n'&& *Line!='\0'){Line++;}
*Line='\0';
Line=LCache;
//过滤行TAB缩进或前空格
while(*Line=='\t'|| *Line==' '){Line++;}
//过滤注释符
if(Line[0]=='#'||strlen(Line)<=5){
continue;
}
//提取URL
if(
(!urlMARK) &&
(Line[0]=='h' ||Line[0]=='H') &&
(Line[1]=='t' ||Line[1]=='T') &&
(Line[2]=='t' ||Line[2]=='T') &&
(Line[3]=='p' ||Line[3]=='P') &&
(
Line[4]==':' ||
(
(Line[4]=='s'||Line[4]=='S') &&
(Line[5]==':')
)
)
){
if(strlen(Line)<10){
fprintf(stdout, "Please fill the url!\n");
exit(1);
}
strcpy(murl, Line);
while(*Line !='\0'){
if(*Line=='[' && *(Line+1)==':' && ('0'<=*(Line+2) && *(Line+2)<='9') && *(Line+3)==']'){
renMARK[*(Line+2)-48]=TRUE;
Line+=4;
}else{
Line++;
}
}
urlMARK=TRUE;
continue;
}else if(
(urlMARK) &&
(Line[0]=='[' &&Line[1]==':') &&
('0'<=Line[2] &&Line[2]<='9') &&
(Line[3]==']' &&Line[4]=='=')
){
SN=Line[2]-48;
if(!renMARK[SN]){continue;}
strcpy(settingini[SN], Line);
if(strchr(Line+5, ',')){
M[SN]=0;
N[SN]=ArgsSpliy(Line+5, SN)-1;
}else if(renMARK[SN]){
char *snumsrc=strtok(Line+5, " \t-"), *tmps=(snumsrc!=NULL)?strtok(NULL, " \t-"):NULL, *zp=snumsrc;
if(isLetter(snumsrc) && isLetter(tmps)){
int exnum1=*snumsrc, exnum2=*tmps;
Z[SN]=-1;
M[SN]=(exnum1 <= exnum2)?(N[SN]=exnum2, exnum1):(N[SN]=exnum1, exnum2);
}else if(tmps!=NULL){
if(*zp=='0'){
while('0'<= *zp && *zp <='9'){
Z[SN]++;
zp++;
}
}
M[SN]=atoi(snumsrc), N[SN]=atoi(tmps);
if(M[SN]>N[SN]){
int exnum=M[SN];
M[SN]=N[SN], N[SN]=exnum;
}
}
}
}
}
fclose(fp);
//统计总循环次数
for(int i=0; i<10; i++){totalCYC *=N[i]-M[i]+1;}
return 0;
}
/*************下载函数*************/
UINT WINAPI FastDownload(void* ptr)
{
//分配URL字串
char* ptftr=(char*)calloc(16, sizeof(char));
char* aurl =(char*)calloc(SAFE_SIZE, sizeof(char));
char* dest =(char*)calloc(SAFE_SIZE, sizeof(char));
char* durl =(char*)calloc(SAFE_SIZE, sizeof(char));
char* titl =(char*)calloc(SAFE_SIZE, sizeof(char));
durl[0]='.', durl[1]='\\';
//接受线程传参
int nThreadID = *((int*)ptr);
int snum = *((int*)ptr+1);
int lnum = *((int*)ptr+2);
SetEvent(ghDataEvent);
nowsThread++;
if(*outlog != 0){
//fprintf(logfp, "\nThe %dth thread start.\n", nThreadID+1);
}
//创建循环变量数组
int i[10]={0}, SN;
//创建下载状态变量
BOOL preMARK=FALSE, nowMARK=FALSE;
//定义URL指针
char *purl=NULL, *surl=NULL, *pdit=NULL;
//启动十层循环
for(i[0]=snum; i[0]<=lnum; i[0]++)
for(i[1]=M[1]; i[1]<=N[1]; i[1]++)
for(i[2]=M[2]; i[2]<=N[2]; i[2]++)
for(i[3]=M[3]; i[3]<=N[3]; i[3]++)
for(i[4]=M[4]; i[4]<=N[4]; i[4]++)
for(i[5]=M[5]; i[5]<=N[5]; i[5]++)
for(i[6]=M[6]; i[6]<=N[6]; i[6]++)
for(i[7]=M[7]; i[7]<=N[7]; i[7]++)
for(i[8]=M[8]; i[8]<=N[8]; i[8]++)
for(i[9]=M[9]; i[9]<=N[9]; i[9]++){
surl=murl, purl=aurl;
while(*surl !='\0'){
//替换正则标签
if(*surl=='[' && *(surl+1)==':' && '0'<=*(surl+2) && *(surl+2)<='9' && *(surl+3)==']' && renMARK[*(surl+2)-48]){
SN=*(surl+2)-48;
if(S[SN][0] !=NULL){
pdit=S[SN][i[SN]];
}else{
if(Z[SN] >0){
sprintf(ptftr, "%%0%dd", Z[SN]);
sprintf(dest, ptftr, i[SN]);
}else if(Z[SN]==-1){
sprintf(dest, "%c", (char)i[SN]);
}else{
sprintf(dest, "%d", i[SN]);
}
pdit=dest;
}
while(*pdit !='\0'){
*(purl++)=*(pdit++);
}
surl+=4;
}else{
*(purl++)=*(surl++);
}
}
//置结束符
*purl='\0';
//显示当前下载的URL
prointThread++;
sprintf(titl, "[%03dT]-[%03dF] %s", nowsThread, trueDownload, aurl);
SetConsoleTitleA(titl);
//处理下载路径
char* turl=strstr(aurl, "://")+3;
char* sp=durl+2;
//根据网址创建目录
while(*turl != '\0'){
switch(*turl)
{
case '/':
*sp='\0';
if(_access(durl, FILE_EXIST) != 0){
_mkdir(durl);
}
*sp='\\';
break;
case ':':
case '*':
case '?':
case '"':
case '>':
case '<':
case '|':
*sp='@';
break;
default:
*sp=*turl;
break;
}
//指针移位
sp++, turl++;
}
//置结束符
*sp='\0';
//调用API下载
URLDownloadToFileA(NULL, aurl, durl, 0, NULL);
//下载状态
nowMARK=(_access(durl, FILE_EXIST)==0)?TRUE:FALSE;
//增速判断
if(preMARK && !nowMARK && i[9]!=M[9]){
break;
}
//显示下载成功的文件
if(nowMARK){
trueDownload++;
if(*outlog != 0){fprintf(logfp, "[*]%s\n", aurl);}
}
//下载状态更新
preMARK=nowMARK;
}
//释放数组内存
free(aurl);
free(durl);
free(dest);
free(titl);
free(ptftr);
//线程结束
nowsThread--;
if(*outlog != 0){
//fprintf(logfp, "\nThe %dth thread end!\n\n", nThreadID+1);
}
return (UINT)0;
}
/*************MAIN函数*************/
int main(int argc, char** argv)
{
int threadNUMS=100;
char K, *outLOG=NULL;
while((K=GetOpt(argc,argv,"hi:n:p:l:"))!=-1)
{
switch(K)
{
case 'h':
case 'H':
fprintf(stdout, HELP_INFORMATION);
exit(0);
case 'i':
case 'I':
if(OPTARG !=NULL){
strcpy(inifile, OPTARG);
}
break;
case 'l':
case 'L':
if(OPTARG !=NULL){
strcpy(outlog, OPTARG);
}
break;
case 'n':
case 'N':
if(OPTARG !=NULL){
int exnum=atoi(OPTARG);
threadNUMS=(0<exnum && exnum<SAFE_SIZE*4)?exnum:SAFE_SIZE*4;
}
break;
case 'r':
case 'R':
if(OPTARG !=NULL){
strcpy(mpath, OPTARG);
}
break;
default:
fprintf(stdout, "Unknown switch '%c'\n", K);
exit(1);
}
}
//判断配置文件是否存在
if(access(inifile, FILE_EXIST)!=0){
FILE* fp=fopen(inifile, "w");
fprintf(fp, PURL_INI);
fclose(fp);
fprintf(stdout, "Please fill in the setting file\n");
fprintf(stdout, HELP_INFORMATION);
exit(1);
}
if(argc==1){
//无参数则退出
fprintf(stdout, HELP_INFORMATION);
exit(0);
}
//判断根目录是否存在
if(_access(mpath, FILE_EXIST)!=0){
_mkdir(mpath);
}
if(_access(mpath, FILE_EXIST)==0){
//切换至下载目录
_chdir(mpath);
}else{
//无法创建根目录则退出
fprintf(stdout, "Can not creat the root folder\n");
exit(1);
}
//读取purl配置文件
ReadSetting(inifile);
if(! urlMARK){
fprintf(stdout, "Please fill in the download url\n");
exit(1);
}
//获取控制台输出句柄
HANDLE handle_out=GetStdHandle(STD_OUTPUT_HANDLE);
//线程传参数组
int inum[3]={0};
//判断是否使用多线程正则符[:0]
if(! renMARK[0]){
threadNUMS=1;
}
//线程数
int nThreadCount=N[0]-M[0];
if(nThreadCount<=0 && threadNUMS!=1){
fprintf(stdout, "\nThe '[:0]' don't get incremental value\n");
exit(1);
}
//创建时间结构体
time_t startTIME, endTIME;
struct tm* timeinfo;
time(&startTIME);
timeinfo=localtime(&startTIME);
//计算线程分组数
int rThreadAdd =(int)ceil((nThreadCount + 1)*1.0/threadNUMS);
int rThreadRange =(int)ceil((nThreadCount + 1)*1.0/(rThreadAdd?rThreadAdd:1.0));
//是否开启日志输出
if(*outlog != 0){
logfp=fopen(outlog, "w");
if(logfp == NULL){
fprintf(stdout, "Open log failed\n");
exit(1);
}else{
fprintf(logfp,
"================================================================\n"
"The purl log file, Time: %s"
"================================================================\n\n"
,asctime(timeinfo)
);
fprintf(logfp, "[Threads num]\n%d\n\n", rThreadRange);
fprintf(logfp, "[Root path]\n%s\n\n", mpath);
fprintf(logfp, "[The URL regular]\n%s\n\n", murl);
fprintf(logfp, "[Setting ini]\n");
for(int i=0; i<=9; i++){
if(settingini[i][0]!=0){
fprintf(logfp, "%s\n", settingini[i]);
}
}
fprintf(logfp, "\n[Start the download time]\n%s\n", asctime(timeinfo));
fprintf(logfp, "[Successfully downloaded URL]\n");
}
}
//隐藏光标
DispyCursor((DWORD)25, FALSE);
//显示目标网站IP
DownloadURLtoIP(handle_out, murl);
//分配线程句柄数组
HANDLE* phaThread =(HANDLE*)calloc(rThreadRange, sizeof(HANDLE));
//创建事件对象
ghDataEvent =CreateEventA(NULL, FALSE, FALSE, NULL);
//错误号
int nErr=0;
for(int i=0; i<rThreadRange; i++){
inum[0]=i, inum[1]=M[0]+i*rThreadAdd, inum[2]=M[0]+i*rThreadAdd+rThreadAdd-1;
if(inum[2]>N[0]){inum[2]=N[0];}
phaThread[i]=(HANDLE)_beginthreadex(NULL, 0, FastDownload, &inum, 2048, NULL);
if(phaThread[i]==0){
nErr=GetLastError();
if(nErr==0x08){
printf("\nOpen thread failed, lack of storage space!\n");
}else{
printf("\nOpen thread failed, error number%d\n", nErr);
}
break;
}
WaitForSingleObject(ghDataEvent, INFINITE);
}
//色彩代码 紫色1|4|8,红色4|8,绿色2|8,兰色1|2|8,黄色2|4|8,白色1|2|4|8;
//显示最大线程
ColorString(handle_out, "[MAX-THRE] ", "", 4|8, 4|8, rThreadRange);
//显示开始时间
ColorString(handle_out, "[STA-TIME] ", asctime(timeinfo), 4|8, 1|4|8, -1);
//绘制进度框
ColorString(handle_out, "[DOWNLOAD] ", (char*)proGRESS, 4|8, 2|4|8, -1);
//绘制进度条
int spi=0;
do{
//进度百分数
int i=(totalCYC==0)?100:(prointThread*100/totalCYC), j=i/2;
if(spi==j){
//缓解CPU占用
Sleep(30);
continue;
}
//显示递增进度
while(spi<j){
fputc('=', stdout);
spi++;
}
//显示进度百分比
fprintf(stdout, "%3d%%\b\b\b\b", i);
//线程退出器
if(prointThread==totalCYC){
break;
}
}while(nowsThread);
//补全百分百进度
if(prointThread==totalCYC){
while(spi<50){
fputc('=', stdout);
spi++;
}
fprintf(stdout, "100%%");
}
fprintf(stdout, "\n");
//显示结束时间
time(&endTIME);
timeinfo=localtime(&endTIME);
ColorString(handle_out, "[END-TIME] ", asctime(timeinfo), 4|8, 1|4|8, -1);
//显示花费时间
ColorString(handle_out, "[USE-TIME] ", "", 4|8, 1|2|8, -1);
fprintf(stdout, "Takes %gs\n", difftime(endTIME, startTIME));
//颜色归位
SetConsoleTextAttribute(handle_out, 1|2|4);//白色
//关闭日志读写
if(*outlog != 0){
fprintf(logfp, "\n[End the download time]\n%s\n", asctime(timeinfo));
fprintf(logfp, "[Download time-spending]\n%g seconds\n\n", difftime(endTIME, startTIME));
fprintf(logfp, "[Percent of completion]\n%d%%\n\n", (prointThread==totalCYC)?100:spi*2);
fprintf(logfp, "[Successfully downloaded files]\n%d", trueDownload);
fclose(logfp);
}
return 0;
}
[/code] 是我用错了吗?怎么不行。
https://download.mozilla.org/?product=firefox-55.0.3-SSL&os=win&lang=zh-CN
http://ftp.mozilla.org/pub/firefox/releases/55.0.3/win32/zh-CN/Firefox%20Setup%2055.0.3.exe
https://ipmsg.org/archive/ipmsg470src.zip
把这三个网址分别添加到purl.ini中,也不行。弹出错误。 [i=s] 本帖最后由 happy886rr 于 2017-8-26 09:07 编辑 [/i]
[b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=202465&ptid=45178]2#[/url] [i]freesoft00[/i] [/b]
代码已经修复,这是多线程批量下载器,只支持批量地址下载,比如[url]www.a01.com[/url], [url]www.a02.com[/url], [url]www.a03.com[/url]就是批量的网址,而不是说单一下载某个文件。
单独下载某个文件请使用pget。
purl的目的是针对有规律的数字递增网址,批量多线程下载,而且必须是几千、几万的批量网址才可以成功下载。线程过少的话,主程序很快就跑完了,子线程也就结束了。我把下载量调到5000,你的文件都成功下载了[:0]=0-5000
purl是针对繁重的批量下载任务定制的,个别几个小文件的下载多线程会得不偿失,小文件请使用pget。 [b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=202466&ptid=45178]3#[/url] [i]happy886rr[/i] [/b]
哦,知道了。用途不一样呀。 支持 ssh 链接的 sftp 下载吗? 请教下要怎么编译?
gcc和vs均未通过 [i=s] 本帖最后由 523066680 于 2017-10-10 10:02 编辑 [/i]
[b]回复 [url=http://bbs.bathome.net/redirect.php?goto=findpost&pid=203742&ptid=45178]6#[/url] [i]CrLf[/i] [/b]
试了 MinGW GCC, 稍微改一下可以
添加 <stdbool.h>
注释掉两个 lib 的引入(改为手动)[code]#include <stdbool.h>
// #pragma comment(lib, "ws2_32.lib")
// #pragma comment (lib,"urlmon.lib")[/code]gcc purl.c -lws2_32 -lurlmon [i=s] 本帖最后由 happy886rr 于 2017-10-10 14:10 编辑 [/i]
[b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=203742&ptid=45178]6#[/url] [i]CrLf[/i] [/b]
大师,好久不见。(后缀一定要改为cpp)
编译现在都已经实现一键了,请参看我之前的帖子[url]http://www.bathome.net/thread-44180-1-1.html[/url]
下载那个精简的vc++2010编译器(仅22M大小),把.cpp文件直接拖拽到VC.CMD批处理脚本上,就会自动生成exe。
别看体积小,编译功能与原版VS几乎没有差别。
另外我还发布过手机版的gcc编译器,可以实现代码直接编译为 安卓上可运行的二进制文件。从而实现 win、linux、安卓 各平台通吃。 [b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=203748&ptid=45178]8#[/url] [i]happy886rr[/i] [/b]
手机版本的编译器在哪里?
页:
[1]