KEEP K.I.S.S.

tk's blog

谷歌输入法颜文字扩展 kaos 更新到 2.1.1

好久没写文章,马上也要过除夕了,准备回家中...

以前写过的谷歌颜文字扩展最近重写了,转换器用 python 3 重写,词库也有一些添加。

详情请见 kaos@Github

XP 下 GVim 工作目录问题

用GVim打开文件,工作目录就是文件所在目录。

点击桌面上快捷方式打开GVim,工作目录是桌面,用 :pwd 命令检测。

把快捷方式加到桌面任务栏的快速启动栏中,“一般”情况下,快速启动的GVim工作目录是 ~ 下,也就是用户目录下。

但是如果你打开过 Windows字体目录(C:\WINDOWS\Fonts),之后再用快速启动打开GVim,工作目录会变成 C:\WINDOWS\Fonts ,多少次都是如此。重启后会恢复正常。

因为GVim安装后默认添加的桌面快捷方式属性中只有目标位置,而起始位置为空,这里面起始位置就会被设定为起始的工作目录(一开始的,如果打开文件,工作目录就会被修改为文件所在目录)。将快捷方式属性中的起始位置设置后,快速启动的GVim的工作目录就是设置的位置。

我的猜测:在快捷方式属性起始位置为空的情况下,在未打开 Windows字体目录前,Windows 会将快速启动栏中程序默认工作目录设置为用户目录,但是在打开 Windows字体目录后,快速启动栏中程序默认的工作目录就变成了 Windows字体目录。不知道算不算一个 bug。

这里当然也学到了一点收获,那就:生成快捷方式的时候,不要留空起始位置属性

虚拟机socket通信丢失头字节奇怪问题一枚

问题描述:在虚拟机里的Linux上用socket TCP (C 编写)连接 Win 主机上 socket(python 编写) 服务器,获取的数据经常少一个字节,而且都是数据的头字节。使用 telnet 连接也是如此。在 Win 主机上用 Socket 调试工具和telnet 都是正常,不会丢失。

硬件环境:虚拟机为 Orcale VM VritualBox 4.1.2,虚拟机上系统为 Debian 6 x86-x64,虚拟机网络类型为 NAT。主机系统为 WinXP SP3 x86。

虚拟机linux下代码:

#include    "unp.h"

int
main(int argc, char **argv)
{
    int    sockfd, n;
    char    recvline[MAXLINE + 1];
    struct sockaddr_in    servaddr;

    if (argc != 2)
        err_quit("usage: a.out <IPaddress>");

    if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        err_sys("socket error");

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port   = htons(4013);    /* daytime server */
    if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
        err_quit("inet_pton error for %s", argv[1]);

    if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) < 0)
        err_sys("connect error");

    while ( (n = read(sockfd, recvline, MAXLINE)) > 0) {
        recvline[n] = 0;    /* null terminate */
        printf("%d read from the socket\n", n);
        if (fputs(recvline, stdout) == EOF)
            err_sys("fputs error");
    }
    if (n < 0)
        err_sys("read error");

    exit(0);
}

这代码其实就是《UNIX 网络编程卷1》(UNP)中的第一个程序( unpv13e/intro/daytimetcpcli.c ),用 tcp 连接服务器,读取时间信息的。上述代码做了微小改动。UNP的第三版代码 unpv13e 可以在这里这里下载。

为了测试这个代码我在 Win 主机上用 python 写了一个服务程序:

import socket
import time

host = '192.168.1.68'
port = 4013

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((host, port))
s.listen(5)

while True:
    connection, address = s.accept()
    t = time.strftime("%a %X %x", time.localtime()).encode("ascii")
    c = connection.send(t)
    print("{0:d} sent of {1:d}: ".format(c, len(t)), "\"{0}\"".format(t.decode("ascii")), "TO client {0[0]}:{0[1]}".format(address))
    connection.close()

其中 192.168.1.68 是主机在局域网中的地址,绑定到这个地址上虚拟机中程序也可以访问到服务器。

当在虚拟机中用编译好的程序或者 telnet 访问 Win 上服务器时,经常会输出如下

"ri 16:22:52 12/28/12"

少了头部一个字符 "F" ,读取只是20个字节,但是也有时候正常,但是正常的几率低于 40%(目测)。

而在 Win 主机上打印输出都是正常的,'21 sent of 21:  "Fri 16:54:40 12/28/12" TO client 192.168.1.68:18289' 这样的,的确都是把 21 个字节发送完毕。

在 Win 主机上用 telnet 或者 TCP/UDP socket 调试助手来测试,获取的数据也都正常,为21字节,没有丢失头字节的情况发生。

(⊙﹏⊙) 

感觉可能是虚拟机 NAT 模式的问题或者还是什么,但是只丢失头字节的确是好奇怪。。。。

·-·-·-·-·-·--·-·-· 2012-12-29 分割线 -·-·-·-·-·-·-·-·--·-·-·-·

今天又测试了下,这次通信获取数据大部分情况下是正常的,只有约10%的几率会丢失头字节。在虚拟机里装了tcpdump 工具来看看情况。

两次通信

tisyang@debian:~/Sources/unpv13e/intro$ ./daytimetcpcli 192.168.1.68
20 read from the socket
at 09:14:47 12/29/12

tisyang@debian:~/Sources/unpv13e/intro$ ./daytimetcpcli 192.168.1.68
21 read from the socket
Sat 09:14:51 12/29/12

说明第一次通信丢失了头字节 'S',第二次通信正常。在此同时,我已经运行 tcpdump 来截获和主机 192.168.1.68 的通信包,命令和输出如下

tisyang@debian:~$ sudo tcpdump  host 192.168.1.68
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes

09:14:46.337897 IP 10.0.2.15.43314 > 192.168.1.68.4013: Flags [S], seq 3451477296, win 14600, options [mss 1460,sackOK,TS val 398218 ecr 0,nop,wscale 6], length 0
09:14:46.341664 IP 192.168.1.68.4013 > 10.0.2.15.43314: Flags [S.], seq 46272001, ack 3451477297, win 65535, options [mss 1460], length 0
09:14:46.341712 IP 10.0.2.15.43314 > 192.168.1.68.4013: Flags [.], ack 1, win 14600, length 0
09:14:46.341747 IP 192.168.1.68.4013 > 10.0.2.15.43314: Flags [S.], seq 46272001, ack 3451477297, win 65535, options [mss 1460], length 0
09:14:46.341837 IP 10.0.2.15.43314 > 192.168.1.68.4013: Flags [.], ack 1, win 14600, length 0
09:14:46.341865 IP 192.168.1.68.4013 > 10.0.2.15.43314: Flags [FP.], seq 1:21, ack 1, win 65535, length 20
09:14:46.342276 IP 10.0.2.15.43314 > 192.168.1.68.4013: Flags [F.], seq 1, ack 22, win 14600, length 0
09:14:46.342360 IP 192.168.1.68.4013 > 10.0.2.15.43314: Flags [.], ack 2, win 65535, length 0


09:14:49.632861 IP 10.0.2.15.43315 > 192.168.1.68.4013: Flags [S], seq 3496530236, win 14600, options [mss 1460,sackOK,TS val 399041 ecr 0,nop,wscale 6], length 0
09:14:49.633743 IP 192.168.1.68.4013 > 10.0.2.15.43315: Flags [S.], seq 46720001, ack 3496530237, win 65535, options [mss 1460], length 0
09:14:49.633771 IP 10.0.2.15.43315 > 192.168.1.68.4013: Flags [.], ack 1, win 14600, length 0
09:14:49.633983 IP 192.168.1.68.4013 > 10.0.2.15.43315: Flags [P.], seq 1:22, ack 1, win 65535, length 21
09:14:49.634048 IP 192.168.1.68.4013 > 10.0.2.15.43315: Flags [F.], seq 22, ack 1, win 65535, length 0
09:14:49.634245 IP 10.0.2.15.43315 > 192.168.1.68.4013: Flags [.], ack 22, win 14600, length 0
09:14:49.634749 IP 10.0.2.15.43315 > 192.168.1.68.4013: Flags [F.], seq 1, ack 23, win 14600, length 0
09:14:49.634815 IP 192.168.1.68.4013 > 10.0.2.15.43315: Flags [.], ack 2, win 65535, length 0

输出中的分段是我添加的,为了区分第一次通信和第二次通信。显然,第一次通信中就只获取到了 20 个字节(length 20),而第二次正常(length 21)。

TCP 连接要经过3次握手,而关闭需要4次握手,从第二次通信就可以看出来。

而在第一次通信中,3次握手tcp建立后,服务器很奇怪的发来一个 SYN 包,而且 FIN 包(用于连接终止)和 PUSH 包(传送数据)是在同一个包内。而在第二次通信里,没有这种情况。

 

PPP 数据帧中字符转义设计

一个比较成熟的字符转义序列设计,记录在此备用。可以用来作为通信协议设计参考(重复造轮子?)。

  1. PPP 数据帧都以标志字符 0x7e 开始和结束。
  2. 由于标志字符的值是 0x7e,因此当该字符出现在信息字段中时,需要对它进行转义。特殊字符 0x7d 用作转义字符。转义算法为当 0x7d 出现在数据中时,紧接着的字符的第 6 个比特要取其补码,即与 0x20 做异或运算。
    1) 当遇到字符 0x7e 时,需连续编码两个字符:0x7d 和 0x5e,以实现标志字符转义(0x7e ^ 0x20 = 0x5e)。
    2) 当遇到转义字符 0x7d 时,需连续编码两个字符:0x7d 和 0x5d,以实现转义字符的转义(0x7d ^ 0x20 = 0x5d)。
    3) 默认情况下,如果字符的值小于 0x20 (ASCII 控制字符),一般都要进行转义。例如字符 0x01 会被转义为两个连续字符 0x7d 和 0x21。

当然我也在怀疑是否用的上.....

参考资料:

TCP/IP详解卷1:协议, 机械工业出版社, 2000年.

一个 Python 的 IDE

出差了很久,也就没有更新博客,残念。

/////////////////////////////分割线/////////////////////////////////////

今天看维基百科,看到 了 PyScripter 这么个东东,一个 Python 的 IDE,用 Delphi 写的。

我是个喜欢收藏小工具的淫,尤其是好看的,显然我就被这界面给征服了。。。

功能也比较全,有函数补全提示,语法错误提示等等。有兴趣的赶紧去试一下吧,传送门

PyScripter 截图

PS: Delphi 果然是做 C/S客户端界面的利器。