正式搬迁。。。
Sat, 17 Dec 2011 13:01:13 +0800
正式搬迁到 http://ekd123.org 。。。因为i11r (iS-PROGRAMMEr的缩写,也算吐槽一下这么长的名字) 在我这里访问实在不给力。
正式搬迁到 http://ekd123.org 。。。因为i11r (iS-PROGRAMMEr的缩写,也算吐槽一下这么长的名字) 在我这里访问实在不给力。
我个人并没有全看过,看过也不一定完全懂,有问题请去相关页面留言问原作者。最近自己买了个空间,欢迎常来逛逛,如果一个月后我还有资金维持那个独立博客,我可能会把这个blog里面我自认为可以保留的文都转走。
当你把以下文章完全看透,你就可以说你已经会 GObject 了(不仅仅是入门)。
Tiger Soldier 写的:
有点可惜的是第二篇无限延长了。。 话说这是 is-Programmer.com 上第一篇有关 GObject 的文章。
Tiger Soldier 是 OSD Lyrics 的作者。
pingf 写的:
文题虽稍显不雅,但内容很好,甚至是联系源代码的。有点可惜的是第二篇还是没出来。
pingf 是 OOC-GCC 的作者。
过眼云烟写的:
Garfileo 的 GObject 系列文章,因为他自己已经给出了目录,我就不多嘴了:
有点可惜的是没有 PDF 版离线阅读……
Garfileo 是尚未面世的 Cikada 的作者。
GNOME 官方的 GObject 手册。别人都说很老了,也很晦涩, Garfileo 说你能完全看懂里面的 Concepts 的话,就不用看他的文了。
《GObject对象系统》。很简单的入门文,不过似乎并不完全参照以上文所述的方法。
以下是 Ubuntu Tweak 作者 TualatriX 所翻译的官方手册:
10、GObject的消息系统:Closure(这个是叫闭包吧?)
开始学习吧!
#include <gio/gio.h>
#include <glib.h>
int
main (int argc,
char *argv[])
{
/* 少写了这个程序就崩溃了 调试了老半天 T_T */
g_type_init ();
GFile *file;/* 文件抽象数据类型 */
GOutputStream *fos; /* 用来写的 */
GError *error = NULL;
/* 创建file对象 */
file = g_file_new_for_path ("./test_file");
/* 获取输出流 */
fos = G_OUTPUT_STREAM (g_file_replace (file, NULL, FALSE,
G_FILE_CREATE_NONE,
NULL, &error));
if (!fos)
{
g_error ("%s", error->message);
g_error_free (error);
return 1;
}
/* 写入 */
GString *str = g_string_new ("这些文字将写入 test_file。\n");
g_output_stream_write_all (fos, str->str, str->len, NULL, NULL, NULL);
g_output_stream_flush (fos, NULL, NULL);
g_string_free (str, TRUE);
/* 清理 */
g_output_stream_close (fos, NULL, NULL);
g_object_unref (file);
return 0;
}
#include <gio/gio.h>
#include <glib.h>
int
main (int argc,
char *argv[])
{
if (argc < 2)
return 1;
/* 少写了这个程序就崩溃了 调试了老半天 T_T */
g_type_init ();
GFile *file;/* 文件抽象数据类型 */
GInputStream *fis; /* 用来读的 */
GError *error = NULL;
GDataInputStream *dis; /* 抽象输入流 */
/* 创建file对象 */
file = g_file_new_for_path (argv[1]);
/* 获取输入流 */
fis = G_INPUT_STREAM (g_file_read (file, NULL, &error));
if (!fis)
{
g_error ("%s", error->message);
g_error_free (error);
return 1;
}
/* 读取 */
dis = g_data_input_stream_new (fis);
char *line;
while (TRUE)
{
line = g_data_input_stream_read_line (dis, NULL, NULL, &error);
if (error)
{
g_error ("%s", error->message);
return 1;
}
if (!line)
break;
g_print ("%s\n", line);
g_free (line);
}
/* 清理 */
g_input_stream_close (fis, NULL, NULL);
g_object_unref (file);
return 0;
}
效果
$ c89 -o reader reader.c `pkg-config --cflags --libs gio-2.0 glib-2.0` $ c89 -o writer writer.c `pkg-config --cflags --libs gio-2.0 glib-2.0` $ ./writer $ ./reader test_file 这些文字将写入 test_file。 $
该文基于我早前写 GKiu 时对 GNOME 密钥环的学习。
代码全部经过实测、优化,请放心阅读本文!
简单来说,就是存你密码的地方。假如吧,你在好多地方都办理了VIP卡,你总不能天天带在身上吧?而且还很麻烦,于是你就又买了一个保险柜,把你所有的VIP卡、钥匙、银行卡信用卡充值卡……都塞到这个保险柜里面了,你只需要一个密码就能打开找到自己需要的卡片钥匙。GNOME密钥环就是这么做的,它用一个密码锁住所有密码,于是你就不需要记住很多密码,只需要记住一个密码就全部可以了。需要放在正文里面强调的是强调的是,你用很多种不一样的密码正是因为安全原因,现在你正巧用了一个密码,所以骇客只需要获取你这一个密码你可就全线崩溃了,所以必须做好安全工作。当然,这是在本地存储的,所以要截获密码并不是那么容易。
Q:我的系统有 GNOME 密钥环吗?
A:一般来说,只要你的桌面环境是 GNOME 就有。当然如果你有 KDE,安装了一些使用了这个库的软件,也会有。
Q:我需要手动使用它吗?
A:完全不需要,这个东西是给程序用的,所以都会自动(在你的选项下,也许
)完成。
Q:如何调教密钥环?
A:呃。。。好吧,你可以用心灵感应遥控它,工具是强大到逆天的
seahorse,GNOME官方出品,质量有保证;还有出厂时候附赠的小刀一把,直拿软肋,名字就叫gnome-keyring,不过因为是小刀有些人不会用(警察也不会让你用的),所以不太受欢迎。
这就是我们要谈论的重点了!因为是首次接触 GNOME 密钥环(也许你是
),就算你以前用过 seahorse 也不一定用它的接口写过软件,所以就不会多做介绍给高级特性了!而且这部分也够绝大多数人用了,也许以后我还会多讲一些 GNOME 密钥环的 API。
因为我是 Fedora 对 Ubuntu/Debian 系的不太了解,于是只给出了 Fedora 的安装方法,Ubuntu/Debian 自己解决吧。
方法:
sudo yum install gnome-keyring-devel
在接下来的实例环节,我们要多次用到 seahorse 来验证我们的代码运行结果,所以也许你还需要安装一下 seahorse。
sudo yum install seahorse
注意 GNOME 密钥环的开发文件偏离了一般实践,它将包命名为 gnome-keyring-1(而不是1.0),因此你需要这样编译:
cc [文件名] `pkg-config --cflags --libs gnome-keyring-1 glib-2.0`
因为 GNOME 密钥环用到了 GLib,因此你也需要链接它。
现在就要开始学习使用GNOME密钥环库了!首先我们看看如何保存一个密码……
哇噢!这么长的参数列表!别着急,我们一个一个看,首先是这个 schema 参数。。。也许是的,GNOME 很多库都要这些东西,见怪不怪啦!长话短说,shema 就是一个定义,它定义了你这一个密钥环项目都保存了什么。那么。。莫非我们还要自己写一个 schema?!不瞒你说,GNOME库里面很多用schema的都非常复杂的定义!GNOME 密钥环也不例外,因此内部已经定义好了一个 schema,包含如下参数:
很显然这是一个用在网络上的 schema,而且令人一点也不意外的是,它这个名字就叫 GNOME_KEYRING_NETWORK_PASSWORD。
好了,我们已经干掉了前进路上的一个很大的障碍!继续前进!
keyring,这个很简单了,就是说要存到哪个密钥环里面嘛!不过问题是,这个类型怎么是 const gchar *?呃,其实每个密钥环是用名字标示的,比如你买了好几个一样的保险柜,要区分哪个是不是要贴上一张标签?那我们如何知道系统已有哪些密钥环呢?还要自己创建?还是枚举出一个最合适的已有的?不需要了!默认GNOME密钥环已经有了一个默认的,我们只需要在这里填上一个 NULL 就OK了,为了让代码更加可读(也更长),于是他们又定义了一个为NULL的GNOME_KEYRING_DEFAULT宏!
接下来的已经没什么比较需要讲解了,因为从名字就能看出来是什么意思。如果你细心的话。。你可能已经发现了一个后面带有 _sync 的同名函数,这是干什么的?嗯,其实默认情况下是异步运行的,也就是这个函数会立即返回并创建一个以回调函数为线程函数的线程,于是你就无须等待,这在一些需要大量存取的情况下非常有效,但是,我们一般的小程序只需要存一两次,为什么需要异步?那就用同步函数好了!也就是说,当操作完成后才会返回(当然,为了内容完备,我们把两个都讲解一下)。
但是你可能会疑惑,这个 destroy_data 又是做什么的呢?这就是另外一个需要的函数了,如果你传递了一个非 NULL 的 data(也就是GTK+里面的用户数据),那么函数会调用你指定的这个函数来释放你这个 data 的内存。
现在有了丰富的理论基础,我们先写一个异步版本的密钥环保存实例!
(在我这里似乎没有进入回调函数,这也可能成为我们使用同步版本的一个理由)
#include <stdlib.h>
#include <stdio.h>
#include <gnome-keyring.h>
static void save_cb (GnomeKeyringResult result,
gpointer data)
{
if (result != GNOME_KEYRING_RESULT_OK)
{
fprintf (stderr, "保存密码出错:%s!\n",
gnome_keyring_result_to_message (result));
}
else
{
printf ("保存密码成功!\n");
}
}
static void free_data_save_cb (gpointer data)
{
free (data);
}
void
save_password (const char *usr,
const char *pwd)
{
gpointer data = malloc (sizeof (100));
gnome_keyring_store_password (GNOME_KEYRING_NETWORK_PASSWORD,
GNOME_KEYRING_DEFAULT,
usr, /* display name */
pwd, /* password */
save_cb,
data, free_data_save_cb,
"user", usr,
"server", "http://ekd123.org",
NULL);
}
int main ()
{
save_password ("mike", "123456");
sleep (10); /* 必须sleep,否则程序退出就无法看到效果了 */
return 0;
}
在回调函数里,如果出现错误会使用 gnome_keyring_result_to_message 将结果转化为字符串的消息(如 strerror 所做一样)。
有必要强调一下,在 destroy_data 参数后面就是 schema 提供的参数了,你必须是一键一值,最后用 NULL 结尾,但我不明白的是,为什么需要这个,因为搜索查找的都是使用 display_name (也就是在 seahorse 中展示的名字)。确实是 seahorse 显示的名字,但是查找还需要这些 schema 中的参数。
可以看到,这个异步版本的非常复杂,而且要返回结果也是迂回的,所以一般实践还是喜欢用同步版本。因此在这里再给出一份存储密码的同步版本。
#include <stdio.h>
#include <gnome-keyring.h>
void
save_password (const char *usr,
const char *pwd)
{
GnomeKeyringResult r;
r=gnome_keyring_store_password_sync (GNOME_KEYRING_NETWORK_PASSWORD,
GNOME_KEYRING_DEFAULT,
usr, /* display name */
pwd, /* password */
"user", usr,
"server", "http://ekd123.org",
NULL);
if (r != GNOME_KEYRING_RESULT_OK)
{
fprintf (stderr, "不能存储密码:%s!\n",
gnome_keyring_result_to_message (r));
}
else
{
printf ("存储密码成功!\n");
}
}
int main ()
{
save_password ("mike", "123456");
return 0;
}
很明显地,这个版本的代码缩减了许多,而且更易理解,当我按下回车键后,屏幕上显示出了我意料之中的喜讯。
关于运行效率,time可以告诉我们更多:
$ time ./a.out 存储密码成功! ./a.out 0.01s user 0.02s system 3% cpu 0.878 total
对于普通人来说,这点时间根本没有注意到就溜走了,所以并不成什么问题!
如果你真的要刨根问底,一睹真实效果的话,我只能有图有真相地给你答复:

(背景图为未来 Fedora 16 的背景图,个人非常喜欢)
“切,那不过是你用seahorse伪造的而已。”呃,你这么想就大错特错了,因为我还会使用 find_password 来找回这个密码!
还是按照惯例,先上异步版本,再上同步版本,为了代码简洁,我会忽略掉用户数据参数。因为 gnome_keyring_find_password 比存储密码的函数简单多了,所以就不会再解释参数意思了。
#include <stdio.h>
#include <gnome-keyring.h>
static void find_cb (GnomeKeyringResult result,
const char *string,
gpointer data)
{
if (result == GNOME_KEYRING_RESULT_OK)
{
printf ("找到了密码!密码是:%s\n", string);
}
else
{
fprintf (stderr, "查找密码失败:%s。\n",
gnome_keyring_result_to_message (result));
}
}
void find (const char *usr)
{
gnome_keyring_find_password (GNOME_KEYRING_NETWORK_PASSWORD,
find_cb,
NULL, NULL,
"user", usr,
"server", "http://ekd123.org",
NULL);
}
int main ()
{
find ("mike");
sleep (1);
return 0;
}
注意回调函数的第二个参数,是带有 const 的!这意味着你不能任意地 free 它。
看上去还是异常复杂,反对 store_password 的理由同样适用于这里,何不试试同步版本?不论从哪方面来说,同步版本都比异步版本易用(好吧,其实在任何方面,异步都比同步复杂;线程中的同步与这里的意思不同,而且它非常复杂)。
#include <stdio.h>
#include <gnome-keyring.h>
void find (const char *usr)
{
gchar *pwd;
GnomeKeyringResult r;
r=gnome_keyring_find_password_sync (GNOME_KEYRING_NETWORK_PASSWORD,
&pwd,
"user", usr,
"server", "http://ekd123.org",
NULL);
if (r == GNOME_KEYRING_RESULT_OK)
{
printf ("找到了密码!密码是:%s\n", pwd);
gnome_keyring_free_password (pwd);
}
else
{
fprintf (stderr, "没有找到密码。。%s\n",
gnome_keyring_result_to_message (r));
}
}
int main ()
{
find ("mike");
return 0;
}
欢快的喜讯传来了,找到了密码!!哈哈,前面那个反对派呢?现在信了吧!不管你信不信,反正我信了!
如果你要删除一个密码,也是非常容易的,因为删除API是最简单的API!
看了例子就知道了!按惯例,异步在前。
#include <stdio.h>
#include <gnome-keyring.h>
static void del_cb (GnomeKeyringResult result,
gpointer data)
{
if (result == GNOME_KEYRING_RESULT_OK)
{
printf ("成功删除了密码!");
}
else
{
fprintf (stderr, "删除密码失败:%s!\n",
gnome_keyring_result_to_message (result));
}
}
void del (const char *usr)
{
gnome_keyring_delete_password (GNOME_KEYRING_NETWORK_PASSWORD,
del_cb,
NULL, NULL,
"user", usr,
"server", "http://ekd123.org",
NULL);
}
int main ()
{
del ("mike");
sleep (1);
return 0;
}
是比存储密码简单了许多!但是,对比了下面同步版本,就略显复杂了:
#include <stdio.h>
#include <gnome-keyring.h>
void del (const char *usr)
{
GnomeKeyringResult r;
r=gnome_keyring_delete_password_sync (GNOME_KEYRING_NETWORK_PASSWORD,
"user", usr,
"server", "http://ekd123.org",
NULL);
if (r == GNOME_KEYRING_RESULT_OK)
{
printf ("成功删除了密码!\n");
}
else
{
fprintf (stderr, "删除密码失败:%s\n",
gnome_keyring_result_to_message (r));
}
}
int main ()
{
del ("mike");
return 0;
}
看,这个才算简单的!有图有真相:

在此我还是向大家力荐使用同步版本,一般情况下同步版本带来的好处远大于弊端(除非你有特殊的环境),而且带来的延迟微不足道,调试起来也更加方便(会在完成后才返回)!
你可能觉得奇怪,为什么不用输入密码都能查找出来?那岂不是非常不安全?嗯,这个你大可放心,因为这是用你登录密码加密的,所以根本不用担心。但是如果你自己创建一个密钥环,那可就得需要密码啦!当然你可以让用户输入,也可以自己选定,一切由你决策!![]()
简单的说,Rawhide就是Fedora的滚动更新版,但这与 Gentoo、ArchLinux 等又不同,因为这个分支指向的是当前开发版(如同 FreeBSD 的 CURRENT 分支),所以极其不稳定。在更新前用你的脑子想想干什么?
需要注意的是,这不是测试版,是开发版,比如现在是 Fedora 15,进行中的版本是 Fedora 16,而 Rawhide 已经指向 Fedora 17 了。
某人说这是“最激进发行版的最激进分支”,窃以为非常贴切。
为 Fedora 开发者提供一个快速的反馈平台,所以一般都是开发者在用,普通用户一般用不到,当然如果你有开发经验而且有一定的解决问题能力可以尝试。
这是非常简单的,只需要
sudo yum install fedora-release-rawhide sudo yum update --disablerepo=\* --enablerepo=rawhide
即可。我强烈建议你不要用任何在X下的终端模拟器!因为我在使用的时候不知怎么 X 突然崩溃了,yum当然也崩溃掉了,因为它是一个模拟出来的终端。这样我就引来了好多麻烦的问题。更新途中突然崩溃的结果就是这个,要是恰巧包管理也坏掉了,那就囧了,所以一定要小心!
更新的时间会非常长,因为要更新电脑上所有的软件包,即使版本号一样,包名也不一样,比如 xxx.fc15 一定要更新到 xxx.fc17(没有17的话也一定会有16的)。建议你在睡前进行更新并关掉电源选项中的X分钟后睡眠、挂起,这会断网的。
在更新途中你可能会遇到各种各样的问题如“损坏依赖”“冲突”“未找到”等等,非常麻烦,一定要做好心理准备,当然一旦开始就没有什么问题了,除非突然给你说 404 错误。
那是因为 Rawhide 源默认指向一个列表,而如果你没有安装选择最快的源的插件,一般都非常慢,这里我们可以选用 163、网易等国内源来加速。不过这需要自己修改源。
打开 /etc/yum.repos.d/fedora-rawhide.repo,将所有baseurl前的#去掉,再改为你用的源的URL。附上一份我的fedora-rawhide.repo(163的,特点是更新快,不过速度比搜狐慢点,搜狐更新很慢)。
[rawhide] name=Fedora Rawhide - $basearch - 163.com failovermethod=priority baseurl=http://mirrors.163.com/fedora/development/rawhide/$basearch/os/ mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=fedora-rawhide&arch=$basearch enabled=1 metadata_expire=7d gpgcheck=0 [rawhide-debuginfo] name=Fedora Rawhide - $basearch - Debug - 163.com failovermethod=priority baseurl=http://mirrors.163.com/fedora/development/$basearch/debug/ mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=fedora-debug-rawhide&arch=$basearch enabled=0 metadata_expire=7d [rawhide-source] name=Fedora Rawhide - Source - 163.com failovermethod=priority baseurl=http://mirrors.163.com/fedora/development/rawhide/source/SRPMS/ mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=fedora-source-$releasever&arch=$basearch enabled=0 metadata_expire=7d
注,该repo是我空写出来的,所以有一点小问题,建议你自己修改安装fedora-release-rawhide后出现的repo文件。
当然可以,而且比安装 Rawhide 更加方便,你只需要安装 preupgrade 即可,这是一个图形软件,很方便,唯一需要注意的是,你的 /boot 分区至少需要 500M 空余空间。
我这里遇到了一个很郁闷的问题,就是开机没法显示图形界面,这个问题很容易知道,显然是显卡问题。
这个问题也不是很难解决,只要用一个禁用调试的内核就解决了,你可以去bodhi和koji上搜索查找(描述里面会说 disable debug build),从Koji上下载下来,安装即可解决问题。注意千万不要升级内核,除非你找到了新的禁用调试的内核。
该标题抄袭了《编程珠玑》,如有雷同,不是巧合。不过原文是算法,本文是逻辑……因为标题雷同,于是在算法分类下发了。
入正题。。嗯,我这人平时很死脑筋,认定了一个,就不会再想另一个,前日老猫同学指点我可以用DBus来服务器和客户通讯,这样可以间接调用另外一个插件里面的函数,我就很惊奇,那要写多少个XML规格文件?老猫童鞋显然有点汗,你给你服务器写一个不就可以了?。。。
好吧,上次就是这样,在跟Peas作者联络的时候,因为英语水平问题,被误解了,结果我也误解了 =_=..,最终导致了我研究了半天的GObject Introspection(这名字起的好,我现在就反省去。。)。
其实我很久之前就写了一个 call_function() ,只不过还不能用,因为 peas_extension_call 是用变参函数的(当然也有V版的。变参函数英语怎么说啊?),所以我的 call_function 也是 V 版的,因为比较容易用。
不该的是,老猫童鞋指点我用 DBus 了。。当然,DBus 早有计划学习,不过因为还是如此的复杂(据说比 CORBA 轻量多了,不过没见过,不好说。且我个人一向认为,代码应是解释程序全部,GSettings因为要求XML规格说明还没找到任何说明文档,一怒之下就又把原来的 GKeyFile 的配置系统重写出来了),一直没有学懂。这次下了狠心学习,入门了解的还是靠 Tiger Soldier 的幻灯片,看了 Roboter 的教程翻译,还是如在云里雾里,介个DBus怎么传递变参函数啊啊?在 Roboter 博客上发现了说可以用 dbus_message_get_args() 获取参数填充,最终还是在 Stack Overflow 丢丑后突然灵机一动:指针??
我真是个白痴啊!这么简单的事情居然这么就弄弄弄到现在!只需要2个指针传递就可以了!(多个可以通过结构传递)
草就不成文,纯属个人吐槽。
设置加速键:
gtk_button_new_with_mnemonic。
让按钮在box里面不要扩充:
使用Gtk[HV]ButtonBox
/* 待续 */
很多人不喜欢XML无非是因为XML过于冗杂,我最烦的就是总是要将标签重复一次。
我尝试将C的符号引入XML,最终诞生的就是这样的:
- {}是对象引用,本身归属一个无名标签。
- {之前的文字是这个标签的名称,可以用来引用。
- ()是对象传递,也就是类C里面调用函数的参数。
- 标签可以嵌套。
并从JSON和Python中吸收了一些优点。
尝试将一个HTML文件改写,原来是这样的:
<!--
这是HTML5!
-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"></meta>
<title>你好</title>
<script language="javascript">
<!--
alert ("hello world!");
-->
</script>
</head>
<body>
<h1>welcome to my page!</h1>
</body>
</html>
改写之后:
<!--
这是用CXML改写的HTML5!
-->
DOCTYPE(html)
html {
head {
title {你好}
script(language="javascript") {<!--
alert ("hello world");
-->
}
}
body {
h1 {Welcome to my page!}
p {段落}
p {}
p {段落}
}
}
(因为JS也用{},所以在使用的时候必须用<!---->来限定范围)
事实证明,这个比我上次设想的Lisp风格的HTML更加优雅~
(这种HTML叫做CHTML挺好的,可惜名字被占了)
该文比较水,纯粹是我初涉P2P一点小笔记。大牛们就不要看了。
P2P是“Point To Point”(点对点)的缩写。我们上网的时候总会感觉一些文件下载速度不给力,这时候别人的电脑里面正巧可能有你需要的文件,我们就可以从他们的电脑里面取出这些文件,加速我们的下载。
这一部分尚无统一标准。纯粹由服务端进行解释,一个比较通用的方法是“私有前缀://哈希码|文件名|文件长度”。如
这里假定uGet有了这样的功能,哈希码用的是sha512, uget://ff2840dbc0301a6272de4aeec4f62f41f5522c1900518f0c35ce3486953cc58513aa7dfacade6c3ba634fae8bd9372f36db8dca0947d2fe289b35ee2f13e32c2|zsh|680
嗯,看上去很长。所以一般用md5即可。
在下载时,客户将下载的文件、进度按一定频率发给服务器,服务器再维护一个列表,其中保存着客户的IP和下载信息,当有客户再下载哈希码相同的文件时,向服务器请求信息,服务器返回整张表,客户端逐一尝试连接,并请求文件。
在这里我们可以将用HTTP下载完的文件计算出哈希码也发送到服务器上去,并进行匹配,这样后来者就可以从这张表里自动从那个HTTP服务器下载文件,并尝试从这个用户下载文件。
电骡、BT、Magnet等。
这些还不够,因为他们都是用一种乱七八糟的方式表达的文件地址,和Windows上的吸血驴还相差甚远。
有一个刚刚立项的下载器工程,凤凰红下载器(这个名字有点……),打算改观Linux下恶心的下载速度(除了dta我看得上,其他简直无法用语言形容,而dta除了不能自动找镜像、P2P几乎完美),更何况在这个神奇的国度。不过不知道能不能发展下去。
更高级一点的P2P,就是从尚未完成下载的客户那里请求文件,这个除非那个伟大的表里面存储下载进度信息,我还不知道有什么别的办法。
当然,以上所述对服务器压力很大,因为一秒就要请求、回应几万次。