QQ登录

只需一步,快速开始

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 990|回复: 8

初次的感动——初读libpcap的一点感慨。原创

[复制链接]
发表于 2004-1-2 15:11:06 | 显示全部楼层 |阅读模式
也许是我才疏学浅,没见过什么市面吧。但是当我第一次读到libpcap的代码时,真的有种莫名的感动:优美的格式,清晰的结构,没有冗余,没有累赘,像一个窈窕淑女,徐徐向我走来……
       
        记得曾经读过一篇文章,具体内容我记不清了,但是其中一句话却始终铭刻在我的头脑中:"天哪!你让一个函数做了三个函数的事情!"
       
        仔细想想:人类之所以有如今的成就,就是因为人类能够从大量繁杂信息中提取有用部分,进行高度的抽象、概括,并应用在类似场合中……这就是所谓的举一反三吧。
       
        举个例子:在我们刚刚学说话的时候,大人们会教我们叫“妈妈”,他们会用夸张的口形冲着我们清楚的说出“妈妈”两个字。此时,我们的大脑进行着复杂的活动:排除外界的干扰——也许此时邻居家正在装修,那么我们就要排除装修带来的噪音;即使是绝对理想的声音环境下(即,我们只听到了大人说的“妈妈”),依然有很多信息干扰着我们——比如我们看着大人夸张的嘴形,然而大人们的脸形却也同样夸张,因此我们就需要通过分析得出结论:需要关注的是口形,而非眼睛或者其他东西。接着,我们的大脑分析从眼睛获得的图像,排除各种背景和无用信息,而只关注有用的信息:口形。同时,大脑也许会试着操控面部肌肉和舌头、声带,去模仿大人……
       
        这是一项多么伟大的工作啊!
       
        首先,我们从外界获得了原始信息。然而,这份原始信息里面却存在着大量无关内容,我们要根据我们所关心的来将原始信息中的无关内容过滤掉,接着对有用的内容进行分析,将分析结果高度抽象后保存,然后试着将保存的内容应用到其他场合……
       
        编程亦是如此。
       
        首先,需要明确目的:做什么样的软件,软件需要有什么功能,软件的使用者是什么类型……
       
        接着,将问题分解成不同的小问题,小问题继续向下分……
       
        当决定写一个函数的时候,问问自己:这个函数是否需要?是不是还能继续向下划分成更小的函数?是不是拥有很好的可移植性?函数是不是足够小?……
       
        没错,函数需要向外部提供简便的入口(参数),明了的出口(返回值),函数内部的实现,要对外部透明,这样,调用函数的外部所关心的仅仅是参数和返回值,而无需关心更多。
       
        初次读到libpcap的代码,看到了这么一个函数:
[code:1]
char *
pcap_strerror(int errnum)
{
#ifdef HAVE_STRERROR
        return (strerror(errnum));
#else
        extern int sys_nerr;
        extern const char *const sys_errlist[];
        static char ebuf[20];

        if ((unsigned int)errnum < sys_nerr)
                return ((char *)sys_errlist[errnum]);
        (void)sprintf(ebuf, "Unknown error: %d", errnum);

        return(ebuf);
#endif
}
[/code:1]

这个不过是一个简单的输出错误信息的函数。在很多书上都有类似的实现。然而,又有多少人在乎它呢?至少我在网上读的到很多代码上,很多写的都是:

[code:1]

char *str;
str=(char *)malloc(1024);
if(str==NULL)
        printf("Err:can not allocate enough memery space\n");
[/code:1]

…………这样实现看起来似乎简便易行,但是:
1 printf函数是向标准输出(stdout)输出内容,而非标准错误输出(stderr)——尽管一般情况下,结果都是显示在屏幕上。
2 如果想了解更多的信息,那么就没有办法了。而前面介绍的函数,则可以根据errno的值来获得错误信息的更多内容。

那么,让我们再次明确一下什么是函数:
1 函数是将具体的操作高度抽象的
2 函数是能够完成特定任务的
3 函数完成的任务要相对的简单——即:一个函数只作一件事
4 一个函数可以通过调用其他函数,来完成一个特定事
5 函数的内部实现对于外部要相对透明,调用函数的外部仅仅需要关心函数的入口和出口

以上便是对函数的一点点领悟。肯定有不全面的地方,还请指教。

下面再来看看libpcap中的一段代码:

[code:1]

/*
* Convert a port name to its port and protocol numbers.
* We assume only TCP or UDP.
* Return 0 upon failure.
*/
int
pcap_nametoport(const char *name, int *port, int *proto)
{
        struct servent *sp;
        char *other;

        sp = getservbyname(name, (char *)0);
        if (sp != NULL) {
                NTOHS(sp->s_port);
                *port = sp->s_port;
                *proto = pcap_nametoproto(sp->s_proto);
                /*
                 * We need to check /etc/services for ambiguous entries.
                 * If we find the ambiguous entry, and it has the
                 * same port number, change the proto to PROTO_UNDEF
                 * so both TCP and UDP will be checked.
                 */
                if (*proto == IPPROTO_TCP)
                        other = "udp";
                else
                        other = "tcp";

                sp = getservbyname(name, other);
                if (sp != 0) {
                        NTOHS(sp->s_port);
#ifdef notdef
                        if (*port != sp->s_port)
                                /* Can't handle ambiguous names that refer
                                   to different port numbers. */
                                warning("ambiguous port %s in /etc/services",
                                        name);
#endif
                        *proto = PROTO_UNDEF;
                }
                return 1;
        }
#if defined(ultrix) || defined(__osf__)
        /* Special hack in case NFS isn't in /etc/services */
        if (strcmp(name, "nfs") == 0) {
                *port = 2049;
                *proto = PROTO_UNDEF;
                return 1;
        }
#endif
        return 0;
}
[/code:1]

这里还有一段:

[code:1]

/*
* Each packet in the dump file is prepended with this generic header.
* This gets around the problem of different headers for different
* packet interfaces.
*/
struct pcap_pkthdr {
        struct timeval ts;        /* time stamp */
        bpf_u_int32 caplen;        /* length of portion present */
        bpf_u_int32 len;        /* length this packet (off wire) */
};
[/code:1]


大可不必关心代码所要实现什么功能,我想说的,仅仅是代码中的注释。

没错!注释!

如今有多少人在写代码的时候就加入的注释?又有多少注释是清晰,易懂的?

尽管高级计算机语言已经接近自然语言,然而它依然无法替代自然语言。为了方便阅读代码的人,加入注释是极其有必要的。

注释要说明函数的参数,返回值,功能……结构体、共同体的功能,成员的意义、类中成员的意义等等。

如果一个人能够将自己的代码清晰的注释出来,那么不仅仅是自己在编码的时候轻松不少,更令读到代码的人有种很清爽的感觉。我觉得能够清晰注释自己的代码,可以说是对自己的负责,也同样是对他人的负责。说得严重些,这反映了一个人的做事态度,反映了一个人对他人的态度。

这仅仅是我初次读到libpcap后的一点感慨而已,有不对的地方还望多多指教。
发表于 2004-1-7 23:46:14 | 显示全部楼层
写得好!!!
回复

使用道具 举报

发表于 2004-1-19 16:09:24 | 显示全部楼层
好!!!!!!!!!!1
回复

使用道具 举报

发表于 2004-1-19 23:37:55 | 显示全部楼层
支持一下
回复

使用道具 举报

发表于 2004-1-20 09:22:16 | 显示全部楼层
支持 其实我们写代码也都往这个方向努力 可就是e文写出来五花八门 谁也看不懂谁的注释
回复

使用道具 举报

发表于 2004-2-12 22:48:25 | 显示全部楼层
上个学期阅读ecos源代码
才知道自己以前写代码都是垃圾
才知道自己其实对c 的了解很是很是肤浅很是浅薄啊
建议有能力的人
还是要读一读人家写的操作系统源代码
回复

使用道具 举报

发表于 2004-2-26 21:39:55 | 显示全部楼层
写的不错啊,看来经典代码是非读不可啊
回复

使用道具 举报

发表于 2004-2-26 23:44:44 | 显示全部楼层
写的算不错吧。

我也是汇文中学的 ,可惜是10年前了。

我认为,世界上最优秀的代码是netbsd的核心代码,简直只能用完美来形容了,强力推荐。
回复

使用道具 举报

发表于 2004-2-28 11:57:02 | 显示全部楼层
楼上:你是否觉得linux下的ipv4比netbsd显得要乱一些啊?我有这个感觉,好像linux的模块太分明了,反而看起来吃力.你认为呢?
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

GMT+8, 2024-11-8 22:34 , Processed in 0.117619 second(s), 16 queries .

© 2021 Powered by Discuz! X3.5.

快速回复 返回顶部 返回列表