允许远程调试的简单易用的 debug 工具 AConsole

jopen 10年前

因为需要实现了一个可以远程调试的工具 AConsole,现在来分享给大家。

请去 https://www.gitHub.com/iKCourage/iSunday 获取 iSunday.exe 和 ingz 目录下的 imoon_plugins.ingz 和 AConsole.ingz,并依次安装。然后就可以打开桌面上的 AConsole.ing 了。

特性支持:

        通过 Socket (TCP) 连接进行调试:

                但过程中可以断开连接,使用一个名字标识会话,即使调试过程中连接意外断开也可以继续调试。

                使用 JSON 字符串来发送消息(尽可能的简单高效)。

        多标签调试:

                支持多个应用程序同时进行调试,每一个都会打开新的标签页。

                可以是多个不同的应用程序。

        日志信息可以设置类别,会显示成不同的颜色:

                可以通过自定义配置来区分消息,自定义的配置相当简单,只是一个数字和对应的颜色。

                发送的消息里也需要描述一个数字(但这足够简单,因为使用 JSON,所以只是一个字段数值)。

        执行对象标记:

                同一个应用程序中的每一条消息都可以指定一个执行对象(用来描述是谁发送了这条消息,如:类名或方法名),然后可以使用快速过滤。

                推荐使用此项,因为这会使显示更整齐,并且过滤起来更快。

                发送的消息里需要描述一个字符串。

        过滤系统:

                通过字符串或正则进行匹配消息,可以指定查找部分(执行对象部分,或消息部分)。

                还可以根据类别来过滤,如:普通消息,警告,错误等。

                一直觉得日志内容就应该支持查找的嘛。

        自动打开应用程序:

                这一项有些泛泛。可以通过配置自己的执行文件进行自动化编译或调试,然后会每隔一定时间检查一次是否有文件的状态发生变化,如果发生,则调用系统 api 打开自定义的执行文件(并可添加执行参数),从而实现自动化。(此功能比较鸡肋,很多人都实现了,不过还是带着吧,有些时候确实很好用)

                可以同时支持多个执行文件和参数。

                可以自己配置合适的检查时间。

大家可以通过自定义类别来设定一个适合自己的显示规则,从而方便调试,就像给 IDE 配色一样(总有自己喜欢的)。

然后可以直接看示例部分了。

另外赠送的功能(不是人人都会用到,希望可以帮到需要的人):

        在 Socket 里实现了一个简单的辅助服务器,可以用来进行一些简单的测试。

                如:需要模拟真实多用户登录的时候,可以每个应用程序都连接服务器,然后会获得一个 id,然后通过此 id 定位已经配置好的数组进行用户名和密码的登录。

                实现的功能有:

                                建立连接时返回用户 id,id 是可以回收的,当有连接断开时,该 id 将被回收。通过此可以模拟多个真实用户。

                                建立连接时可以指定用户所在的组 id,然后会返回组内的用户 id。通过此可以将这个单服务器变成多服务器,每个组都互不影响,从而可以支持多个互不相关的程序同时进行调试。

                                当有新用户进入时,广播给所有已经建立连接的用户(包含组 id 和用户 id)。

                                当有新用户断开时,广播给所有已经建立连接的用户(包含组 id 和用户 id)。

                                通过服务器广播回来的其他用户的 id,应用程序间就可以进行通信啦。如:转发,或者广播。并且支持转发,广播给一个或多个用户,甚至是不同组的一个或多个用户。(调侃一下:貌似是没必要有的功能)

                                可以进行简单的数据写入和读取,从而进行用户间的数据共享。如:id 为 1 的用户将共享数据写入到服务器,然后 id 为 2 的用户可以向服务器索取 id 为 1 的用户的数据。(实现此功能时,我正好在写 p2p 的东西,而外网服务器每次都会返回一个随机的 64 位的 id,当两个用户进行连接时需要通过这个 64 位长的 id,烦扰之余就实现了这么个功能。我可以通过一个数字进行对话了。)当然,不一定大家都会用到,不过如果你凑巧需要的话,希望可以帮到你。

                                注:所有服务器返回的消息(或广播)都可以忽略不进行处理。你只需要实现自己需要的功能就好了(发送或者接收)。更方便大家进行测试。

        一个简单的 JSON 编辑和校验工具。

        一个批量文件复制工具。即使从事文职工作也能使用。(复制文件时可以通过正则表达式进行过滤来复制一部分内容。通过正则表达式可以实现非常强大的复制规 则,不过有些学习成本。幸好大家不太可能会有这种需求,这种需求很少见。在这里写出来不是告诉你这个功能多么好,而是希望在大家需要的时候可以帮到大 家。)

示例:

        消息格式:

                {"name": "player", "title": "播放器", "executor": "[WHERE]", "type": "print", "data": “message”, "_type": 0}

                        name: 一定要有(就像网页里的 url,会通过这里来打开一个标签页)

        title: 标题

        executor: 执行者(什么地方执行的,可以没有,但会使用一个默认的。通过这里也可以快速查找消息)

        type: "print" 打印消息(内容恒为 print)

        data: 消息内容(可以是字符串,也可以是数组,会自动转换为字符串。在外部设计的时候可以使用变参方式,而不用拼接字符串)

        _type: 消息的类型(可以自定义任何类型,然后就可以通过过滤来查看指定类型的消息了。如果没有,则使用默认 0)

        好了,这就是通过 Scoket 发送的消息内容,很简单明了吧。

        Socket 发送格式:

                4 字节的包头,一个 int。(标识后面这个包的字节大小,避免粘包)

                4 字节的通信标识 。(恒为数字 7)(大家不要纠结这里哈,没必要用 4 个字节,但这样大家用起来都方便不是么。)

                UTF 的字符串字节。

                注:4 字节的包头只包含后面数据内容的大小,不包含这里的 4 个字节的包头。

        这样就可以进行调试了,还算简单吧。

你也看到了,调试时的通信标识恒为 7,因为 0 - 6 已经用来实现 Socket 服务器的其他功能了。调试的时候只需要发送消息就可以了,不用管服务器的返回值(希望能简单一点,其实也没有必要哈)。不过服务器依然会返回标识为 7 的数据,用来通知你反送成功了(如果你需要的话)。

好了,来简单介绍下所有的标识吧,大家可以直接看标识为 7 的内容。

说明:以下 >> 为发送的数据,<< 为服务器返回的数据。

        >> 0:客户端注册用户,即让服务器返回一个 id

                bytes.writeInt(4 + 4);

                bytes.writeInt(CSEnum.INIT);              // 此数值为 0

                bytes.writeInt(group);                         // 用户所在组的 id,由用户自己指定,可以不发送(默认为 0)只发送标识

                socket.writeBytes(bytes);

                socket.flush();

        << 0:服务器返回给用户一个 id

                bytes.writeInt(4 + 4);                           // 服务器也返回一个包头

                bytes.writeInt(CSEnum.INIT);               // 0

                bytes.writeInt(id);                                // 用户的 id(之所以没有返回用户的组 id 是因为用户自己知道)

                socket.writeBytes(bytes);

                socket.flush();

        << 3:服务器广播给所有人新注册用户的组 id 和用户 id

                bytes.writeInt(4 + 4 + 4);

                bytes.writeInt(CSEnum.INIT_ALL);

                bytes.writeInt(id);

                bytes.writeInt(group);

        >> 1:客户端读取其他用户 id 的数据(也可以是自己的)

                bytes.writeInt(4 + 4 + 4);

                bytes.writeInt(CSEnum.READ);

                bytes.writeInt(id);

                bytes.writeInt(group);

        << 1:服务器返回给用户读取的数据

                bytes.writeInt(4 + data.length);

                bytes.writeInt(CSEnum.READ);

                bytes.writeBytes(data);

        >> 2:客户端写入自己的数据

                bytes.writeInt(4 + data.length);

                bytes.writeInt(CSEnum.WRITE);

                bytes.writeBytes(data);

        << 2:服务器返回写入成功

                bytes.writeInt(4);

                bytes.writeInt(CSEnum.WRITE);

        << 4:服务器在有客户端连接断开时广播给其他用户

                bytes.writeInt(4 + 4 + 4);

                bytes.writeInt(CSEnum.CLOSE_ALL);

                bytes.writeInt(id);

                bytes.writeInt(group);

        >> 5:客户端通过服务器来将消息转发给其他人(这里有点复杂,因为转发功能有点多,不需要就不用看了)

                接下来就有点麻烦了(其实很简单,因为好多都用不到),先描述下转发规则:

                0 所有组的所有人

                                bytes.writeInt(4 + 4 + data.length);

                                bytes.writeInt(CSEnum.RELAY);

                                bytes.writeInt(0);                                                       // 转发规则标识

                                bytes.writeBytes(data);

1 默认组的几个人

                                bytes.writeInt(4 + 4 + (4 + 4 * 3) + data.length);       // 4 * 3 为用户的 id 占的字节,如:这里为 3,所以还得乘 4

                                bytes.writeInt(CSEnum.RELAY);

                                bytes.writeInt(1);                                                       // 转发规则标识

                                bytes.writeInt(id.length);                                            // 这里为 3(用户个数)

                                bytes.writeInt(2);                                                       // id 为 2 的用户

                                bytes.writeInt(3);                                                       // id 为 3 的用户

                                bytes.writeInt(4);                                                       // id 为 4 的用户

                                bytes.writeBytes(data);

2 默认组的所有人

                                bytes.writeInt(4 + 4 + data.length);

                                bytes.writeInt(CSEnum.RELAY);

                                bytes.writeInt(2);                                                      // 转发规则标识

                                bytes.writeBytes(data);

3 指定组的几个人

                                bytes.writeInt(4 + 4 + (4 + 4 + 4 * 3) + data.length);

                                bytes.writeInt(CSEnum.RELAY);

                                bytes.writeInt(3);                                                      // 转发规则标识

                                bytes.writeInt(id.length);

                                bytes.writeInt(group);

                                bytes.writeInt(2);                                                      // id 为 2 的用户

                                bytes.writeInt(3);                                                      // id 为 3 的用户

                                bytes.writeInt(4);                                                      // id 为 4 的用户

                                bytes.writeBytes(data);

4 指定组的几个人(每个人都分组)

                                bytes.writeInt(4 + 4 + (4 + 4 * 3 * 2) + data.length);

                                bytes.writeInt(CSEnum.RELAY);

                                bytes.writeInt(4);                                                      // 转发规则标识

                                bytes.writeInt(id.length);

                                bytes.writeInt(2);                                                      // id 为 2 的用户

                                bytes.writeInt(group_2);                                            // id 为 2 的用户的组的 id

                                bytes.writeInt(3);                                                      // id 为 3 的用户

                                bytes.writeInt(group_3);                                            // id 为 3 的用户的组的 id

                                bytes.writeInt(4);                                                      // id 为 4 的用户

                                bytes.writeInt(group_4);                                            // id 为 4 的用户的组的 id

                                bytes.writeBytes(data);

5 指定组的所有人

                                bytes.writeInt(4 + 4 + data.length);

                                bytes.writeInt(CSEnum.RELAY);

                                bytes.writeInt(5);                                                      // 转发规则标识

                                bytes.writeInt(group);                                               // 指定的组的 id

                                bytes.writeBytes(data);

        << 6:服务器转发给指定的用户

                bytes.writeInt(4 + 4 + 4 + data.length);

                bytes.writeInt(CSEnum.RELAY_BACK);

                bytes.writeInt(index);

                bytes.writeInt(group);

                bytes.writeBytes(data);

        >> 7:客户端发送给服务器的日志信息(大家用这里就好了,上面的我写着都好烦啊)

                bytes.writeInt(4 + strBytes.length);

                bytes.writeInt(CSEnum.SEND);

                bytes.writeBytes(strBytes);

        << 7:服务器返回发送成功

                bytes.writeInt(4);

                bytes.writeInt(CSEnum.SEND);

终于把 Socket 写完了。之所以写这么多,是希望可以帮到需要的人。

Socket 默认侦听 9000 和 9001。9001 是安全策略文件的侦听,会返回一个安全策略 XML,某些应用程序可能会用到,大部分不需要。

还有一个是关于自动打开应用程序的配置的,大家可以去看配置文件,里面有详细的注释,这里已经说很多了,就不说了。

然后上几张截图给大家哈:

允许远程调试的简单易用的 debug 工具 AConsole

允许远程调试的简单易用的 debug 工具 AConsole

允许远程调试的简单易用的 debug 工具 AConsole

允许远程调试的简单易用的 debug 工具 AConsole

来自:http://my.oschina.net/courage/blog/369946