《晨间日记的奇迹》就像每日总结!

看了《高效能人士的7个习惯》后回想《晨间日记的奇迹》的内容,才明白也是追求多方面平衡发展。

Posted in 读书 | Leave a comment

《晨間日記的奇蹟》真的有用么?非常怀疑!

自前年接触《GTD》以后非常喜欢它,效果非常的显著,虽然坚持的不是很好,但一直都没有放弃。所以经常去浏览一些相关的blog,在看GTD高人的BLOG时,经常会看到有人提到晨间日记。出于好奇,我就下了电子版的《晨間日記的奇蹟》这本书来看,这书是个小部头,不到100页,看完以后感觉并没有什么有用的东西,就是讲要早上起来写一种特殊格式的日记,大概意思就是在纸上或EXCEL里划9个格,写一些情绪、金钱、人际关系等等你认为重要的东西的总结、展望和注意事项,并没有觉得有什么特别的地方,我并不准备尝试早上写这种格式的日记,我觉得多看看褪墨blog上的目标设定管理和习惯养成就挺好,我现在有两个blog是必看的,一个就是褪墨的,还有一个就是GTD Life的,对我帮助非常大,尤其是褪墨坚持定期的把文章整合成PDF文档,提供下载,内容非常的棒,觉得褪墨的文章整合PDF文档对我来说比这本《晨間日記的奇蹟》有价值多了。

Posted in 读书 | 1 Comment

Emacs在Windows下的配置

EMACS在windows环境下关于HOME目录设置和中文环境设置的问题和方法

Emacs 的配置文件是一个叫 .emacs 的文件,不知道 M$ 哪两个部门的沟通出了点问题,导致 windows explorer无法直接创建 ^.* 这样的文件(而这样的文件名在windows文件系统中却又是合法的,啧啧),所以,在windows下也可以用_emacs 来代替。

下一步,让emacs找到你的 .emacs 配置文件,如果你什么都懒得做,就直接仍到 C:/ 下面好了。
但是我最强烈的推荐大家,稍微多做一点点工作,以便以后工作的更好
。设置一个“主目录”,这个目录通常就是

c:/Documentsand Settings/Administrator

你也可以设置能其它目录,以方便重装系统的时候备份。

中文支持

Emacs 是一个真正的支持多语言编辑器。试试看

M-x view-hello-file

会列出Emacs支持的所有语言。如果有些大方块在里面,那不是Emacs的错,因为你的系统里没有那种字体。

如果你下载的是那个 CVS Emacs,据说默认就支持中文了。如果是21.3,可能需要一点配置。

打开 .emacs(_emacs) 文件,你现在还不会用 Emacs, 所以还是先用notepad, ue, editplus 等你熟悉的编辑器吧。

注意:绿色部分为代码,直接copy过去就可以,’;'开头的行,是elisp的注释行,但愿你的编辑器已经支持它了。

加入以下几行。

(set-terminal-coding-system ‘chinese-iso-8bit)
(set-keyboard-coding-system ‘chinese-iso-8bit)
(set-language-environment ‘chinese-gb)
(set-clipboard-coding-system ‘chinese-iso-8bit)
(set-selection-coding-system ‘chinese-iso-8bit)
(setq locale-coding-system ‘chinese-iso-8bit)
(setq current-language-environment "Chinese-GB")

启动emacs, 试着敲几个中文,应该不会有问题了。

 

最后附上我的Emacs配置文件:
(custom-set-variables
;; custom-set-variables was added by Custom — don’t edit or cut/paste it!
;; Your init file should contain only one such instance.
‘(auto-compression-mode t nil (jka-compr))
‘(case-fold-search t)
‘(current-language-environment "Chinese-GB")
‘(default-input-method "chinese-py-punct")
‘(uniquify-buffer-name-style nil nil (uniquify)))
(custom-set-faces
;; custom-set-faces was added by Custom — don’t edit or cut/paste it!
;; Your init file should contain only one such instance.
)
 
;;设置 sentence-end 可以识别中文标点。不用在 fill 时在句号后插入两个空格。
(setq sentence-end "\\([。!?]\\|……\\|[.?!][]\"’)}]*\\($\\|[ \t]\\)\\)[ \t\n]*")
(setq sentence-end-double-space nil)
 
;;显示列号
(setq column-number-mode t)
 
;;防止页面滚动时跳动, scroll-margin 3 可以在靠近屏幕边沿3行时就开始滚动,可以很好的看到上下文。
(setq scroll-margin 3
scroll-conservatively 10000)
 
;;把缺省的 major mode 设置为 text-mode, 而不是几乎什么功能也没有的 fundamental-mode.
(setq default-major-mode ‘text-mode)
 
;;括号匹配时显示另外一边的括号,而不是烦人的跳到另一个括号。
(show-paren-mode t)
(setq show-paren-style ‘parentheses)
 
;;光标靠近鼠标指针时,让鼠标指针自动让开,别挡住视线。
(mouse-avoidance-mode ‘animate)
 
;;在标题栏显示buffer的名字,而不是 emacs@wayne.com 这样没用的提示。
(setq frame-title-format "emacs@%b")
 
;;让 Emacs 可以直接打开和显示图片。
(auto-image-file-mode)
 
;;进行语法加亮。
(global-font-lock-mode t)
 
;;设置有用的个人信息。这在很多地方有用。
(setq user-full-name "Wayne")
(setq user-mail-address "wayne@fastmail.fm")
 
;;配置中文环境
(set-terminal-coding-system ‘chinese-iso-8bit)
(set-keyboard-coding-system ‘chinese-iso-8bit)
(set-language-environment ‘chinese-gb)
(set-clipboard-coding-system ‘chinese-iso-8bit)
(set-selection-coding-system ‘cn-gb-2312)
(setq locale-coding-system ‘chinese-iso-8bit)
(setq current-language-environment "Chinese-GB")
 
;;重定义键盘
;;使用F5键跳转到行;否则需要使用goto-line命令,很麻烦的
(global-set-key [f5] ‘goto-line)
 
;;设置标记起点
(global-set-key "\C-z" ’set-mark-command)
 
;;自动补全
(global-set-key "\M-/" ‘hippie-expand)
 
;; 每当设置书签的时候都保存书签文件,否则只在你退出 Emacs 时保存。
(setq bookmark-save-flag 1)
;; 在 mode-line 上显示时间。
(setq display-time-24hr-format t)
(setq display-time-day-and-date t)
(setq display-time-use-mail-icon t)
(setq display-time-interval 10)
 
;; 在行首 C-k 时,同时删除该行;否则只删除行的内容,行本身还存在
(setq-default kill-whole-line t)
 
;; shell 中打开 ansi-color 支持。
(add-hook ‘diary-hook ‘appt-make-list)
(add-hook ’shell-mode-hook ‘ansi-color-for-comint-mode-on)
后记
Emacs其实是可以同屏显示N种语言字符的,不信用view-hello-file看,虽然有一大堆方框(那时因为机器里面没有对应的字体)。
Posted in C/C++ | Leave a comment

重装了系统,秀一下桌面。

我的机器比较老了,也只能跑跑xp了,vista就不想了,不过今天装的xp带了个vista的主题,我装了个微软的雅黑字体,感觉到也挺漂亮的。

Posted in 心情随笔 | 1 Comment

网络socket编程指南

 

网络socket编程指南
——————————————————————————–
介绍
  Socket 编程让你沮丧吗?从man pages中很难得到有用的信息吗?你想跟上时代去编Internet相关的程序,但是为你在调用 connect() 前的bind() 的结构而不知所措?等等… 
    好在我已经将这些事完成了,我将和所有人共享我的知识了。如果你了解 C 语言并想穿过网络编程的沼泽,那么你来对地方了。
——————————————————————————–
读者对象 
  这个文档是一个指南,而不是参考书。如果你刚开始 socket 编程并想找一本入门书,那么你是我的读者。但这不是一本完全的 socket 编程书。
——————————————————————————–
平台和编译器 
  这篇文档中的大多数代码都在 Linux 平台PC 上用 GNU 的 gcc 成功编译过。而且它们在 HPUX平台 上用 gcc 也成功编译过。但是注意,并不是每个代码片段都独立测试过。
——————————————————————————–
目录:
1) 什么是套接字? 
2) Internet 套接字的两种类型 
3) 网络理论 
4) 结构体
5) 本机转换
6) IP 地址和如何处理它们 
7) socket()函数
8) bind()函数
9) connect()函数
10) listen()函数
11) accept()函数
12) send()和recv()函数
13) sendto()和recvfrom()函数
14) close()和shutdown()函数
15) getpeername()函数
16) gethostname()函数
17) 域名服务(DNS)
18) 客户-服务器背景知识 
19) 简单的服务器
20) 简单的客户端
21) 数据报套接字Socket
22) 阻塞
23) select()–多路同步I/O
24) 参考资料 
——————————————————————————–
什么是 socket? 
  你经常听到人们谈论着 “socket”,或许你还不知道它的确切含义。现在让我告诉你:它是使用 标准Unix 文件描述符 (file descriptor) 和其它程序通讯的方式。什么?你也许听到一些Unix高手(hacker)这样说过:“呀,Unix中的一切就是文件!”那个家伙也许正在说到一个事实:Unix 程序在执行任何形式的 I/O 的时候,程序是在读或者写一个文件描述符。一个文件描述符只是一个和打开的文件相关联的整数。但是(注意后面的话),这个文件可能是一个网络连接,FIFO,管道,终端,磁盘上的文件或者什么其它的东西。Unix 中所有的东西就是文件!所以,你想和Internet上别的程序通讯的时候,你将要使用到文件描述符。你必须理解刚才的话。现在你脑海中或许冒出这样的念头:“那么我从哪里得到网络通讯的文件描述符呢?”,这个问题无论如何我都要回答:你利用系统调用 socket(),它返回套接字描述符 (socket descriptor),然后你再通过它来进行send() 和 recv()调用。“但是…”,你可能有很大的疑惑,“如果它是个文件描述符,那么为什 么不用一般调用read()和write()来进行套接字通讯?”简单的答案是:“你可以使用!”。详细的答案是:“你可以,但是使用send()和recv()让你更好的控制数据传输。”存在这样一个情况:在我们的世界上,有很多种套接字。有DARPA Internet 地址 (Internet 套接字),本地节点的路径名 (Unix套接字),CCITT X.25地址 (你可以将X.25 套接字完全忽略)。也许在你的Unix 机器上还有其它的。我们在这里只讲第一种:Internet 套接字。
——————————————————————————–
Internet 套接字的两种类型 
  什么意思?有两种类型的Internet 套接字?是的。不,我在撒谎。其实还有很多,但是我可不想吓着你。我们这里只讲两种。除了这些, 我打算另外介绍的 "Raw Sockets" 也是非常强大的,很值得查阅。
那么这两种类型是什么呢?一种是"Stream Sockets"(流格式),另外一种是"Datagram Sockets"(数据包格式)。我们以后谈到它们的时候也会用到 "SOCK_STREAM" 和 "SOCK_DGRAM"。数据报套接字有时也叫“无连接套接字”(如果你确实要连接的时候可以用connect()。) 流式套接字是可靠的双向通讯的数据流。如果你向套接字按顺序输出“1,2”,那么它们将按顺序“1,2”到达另一边。它们是无错误的传递的,有自己的错误控制,在此不讨论。
    有什么在使用流式套接字?你可能听说过 telnet,不是吗?它就使用流式套接字。你需要你所输入的字符按顺序到达,不是吗?同样,WWW浏览器使用的 HTTP 协议也使用它们来下载页面。实际上,当你通过端口80 telnet 到一个 WWW 站点,然后输入 “GET pagename” 的时候,你也可以得到 HTML 的内容。为什么流式套接字可以达到高质量的数据传输?这是因为它使用了“传输控制协议 (The Transmission Control Protocol)”,也叫 “TCP” (请参考 RFC-793 获得详细资料。)TCP 控制你的数据按顺序到达并且没有错
误。你也许听到 “TCP” 是因为听到过 “TCP/IP”。这里的 IP 是指“Internet 协议”(请参考 RFC-791。) IP 只是处理 Internet 路由而已。 
    那么数据报套接字呢?为什么它叫无连接呢?为什么它是不可靠的呢?有这样的一些事实:如果你发送一个数据报,它可能会到达,它可能次序颠倒了。如果它到达,那么在这个包的内部是无错误的。数据报也使用 IP 作路由,但是它不使用 TCP。它使用“用户数据报协议 (User Datagram Protocol)”,也叫 “UDP” (请参考 RFC-768。) 
    为什么它们是无连接的呢?主要是因为它并不象流式套接字那样维持一个连接。你只要建立一个包,构造一个有目标信息的IP 头,然后发出去。无需连接。它们通常使用于传输包-包信息。简单的应用程序有:tftp, bootp等等。
    你也许会想:“假如数据丢失了这些程序如何正常工作?”我的朋友,每个程序在 UDP 上有自己的协议。例如,tftp 协议每发出的一个被接受到包,收到者必须发回一个包来说“我收到了!” (一个“命令正确应答”也叫“ACK” 包)。如果在一定时间内(例如5秒),发送方没有收到应答,它将重新发送,直到得到 ACK。这一ACK过程在实现 SOCK_DGRAM 应用程序的时候非常重要。
——————————————————————————–
网络理论
  既然我刚才提到了协议层,那么现在是讨论网络究竟如何工作和一些 关于 SOCK_DGRAM 包是如何建立的例子。当然,你也可以跳过这一段, 如果你认为已经熟悉的话。 
    现在是学习数据封装 (Data Encapsulation) 的时候了!它非常非常重 要。它重要性重要到你在网络课程学(图1:数据封装)习中无论如何也得也得掌握它。主要 的内容是:一个包,先是被第一个协议(在这里是TFTP )在它的报头(也许 是报尾)包装(“封装”),然后,整个数据(包括 TFTP 头)被另外一个协议 (在这里是 UDP )封装,然后下一个( IP ),一直重复下去,直到硬件(物理) 层( 这里是以太网 )。 
当另外一台机器接收到包,硬件先剥去以太网头,内核剥去IP和UDP 头,TFTP程序再剥去TFTP头,最后得到数据。现在我们终于讲到声名狼藉的网络分层模型 (Layered Network Model)。这种网络模型在描述网络系统上相对其它模型有很多优点。例如, 你可以写一个套接字程序而不用关心数据的物理传输(串行口,以太网,连 接单元接口 (AUI) 还是其它介质),因为底层的程序会为你处理它们。实际 的网络硬件和拓扑对于程序员来说是透明的。
不说其它废话了,我现在列出整个层次模型。如果你要参加网络考试, 可一定要记住: 
应用层 (Application)
表示层 (Presentation)
会话层 (Session)
传输层(Transport)
网络层(Network)
数据链路层(Data Link)
物理层(Physical)
物理层是硬件(串口,以太网等等)。应用层是和硬件层相隔最远的–它 是用户和网络交互的地方。 
这个模型如此通用,如果你想,你可以把它作为修车指南。把它对应 到 Unix,结果是:
应用层(Application Layer) (telnet, ftp,等等)
传输层(Host-to-Host Transport Layer) (TCP, UDP)
Internet层(Internet Layer) (IP和路由)
网络访问层 (Network Access Layer) (网络层,数据链路层和物理层)
现在,你可能看到这些层次如何协调来封装原始的数据了。 
看看建立一个简单的数据包有多少工作?哎呀,你将不得不使用 "cat" 来建立数据包头!这仅仅是个玩笑。对于流式套接字你要作的是 send() 发 送数据。对于数据报式套接字,你按照你选择的方式封装数据然后使用 sendto()。内核将为你建立传输层和 Internet 层,硬件完成网络访问层。 这就是现代科技。 
现在结束我们的网络理论速成班。哦,忘记告诉你关于路由的事情了。 但是我不准备谈它,如果你真的关心,那么参考 IP RFC。
——————————————————————————–
结构体 
  终于谈到编程了。在这章,我将谈到被套接字用到的各种数据类型。 因为它们中的一些内容很重要了。 
首先是简单的一个:socket描述符。它是下面的类型: 
int 
仅仅是一个常见的 int。 
从现在起,事情变得不可思议了,而你所需做的就是继续看下去。注 意这样的事实:有两种字节排列顺序:重要的字节 (有时叫 "octet",即八 位位组) 在前面,或者不重要的字节在前面。前一种叫“网络字节顺序 (Network Byte Order)”。有些机器在内部是按照这个顺序储存数据,而另外 一些则不然。当我说某数据必须按照 NBO 顺序,那么你要调用函数(例如 htons() )来将它从本机字节顺序 (Host Byte Order) 转换过来。如果我没有 提到 NBO, 那么就让它保持本机字节顺序。
我的第一个结构(在这个技术手册TM中)–struct sockaddr.。这个结构 为许多类型的套接字储存套接字地址信息: 
struct sockaddr { 
   unsigned short sa_family; /* 地址家族, AF_xxx */ 
   char sa_data[14]; /*14字节协议地址*/ 
   }; 
sa_family 能够是各种各样的类型,但是在这篇文章中都是 "AF_INET"。 sa_data包含套接字中的目标地址和端口信息。这好像有点 不明智。 
为了处理struct sockaddr,程序员创造了一个并列的结构: struct sockaddr_in ("in" 代表 "Internet"。)
struct sockaddr_in { 
   short int sin_family; /* 通信类型 */ 
   unsigned short int sin_port; /* 端口 */ 
   struct in_addr sin_addr; /* Internet 地址 */ 
   unsigned char sin_zero[8]; /* 与sockaddr结构的长度相同*/ 
   }; 
用这个数据结构可以轻松处理套接字地址的基本元素。注意 sin_zero (它被加入到这个结构,并且长度和 struct sockaddr 一样) 应该使用函数 bzero() 或 memset() 来全部置零。 同时,这一重要的字节,一个指向 sockaddr_in结构体的指针也可以被指向结构体sockaddr并且代替它。这 样的话即使 socket() 想要的是 struct sockaddr *,你仍然可以使用 struct sockaddr_in,并且在最后转换。同时,注意 sin_family 和 struct sockaddr 中的 sa_family 一致并能够设置为 "AF_INET"。最后,sin_port和 sin_addr 必须是网络字节顺序 (Network Byte Order)!
你也许会反对道:"但是,怎么让整个数据结构 struct in_addr sin_addr 按照网络字节顺序呢?" 要知道这个问题的答案,我们就要仔细的看一看这 个数据结构: struct in_addr, 有这样一个联合 (unions): 
/* Internet 地址 (一个与历史有关的结构) */ 
   struct in_addr { 
   unsigned long s_addr; 
   }; 
它曾经是个最坏的联合,但是现在那些日子过去了。如果你声明 "ina" 是数据结构 struct sockaddr_in 的实例,那么 "ina.sin_addr.s_addr" 就储 存4字节的 IP 地址(使用网络字节顺序)。如果你不幸的系统使用的还是恐 怖的联合 struct in_addr ,你还是可以放心4字节的 IP 地址并且和上面 我说的一样(这是因为使用了“#define”。) 
——————————————————————————–
本机转换
  我们现在到了新的章节。我们曾经讲了很多网络到本机字节顺序的转 换,现在可以实践了! 
你能够转换两种类型: short (两个字节)和 long (四个字节)。这个函 数对于变量类型 unsigned 也适用。假设你想将 short 从本机字节顺序转 换为网络字节顺序。用 "h" 表示 "本机 (host)",接着是 "to",然后用 "n" 表 示 "网络 (network)",最后用 "s" 表示 "short": h-to-n-s, 或者 htons() ("Host to Network Short")。
太简单了… 
如果不是太傻的话,你一定想到了由"n","h","s",和 "l"形成的正确 组合,例如这里肯定没有stolh() ("Short to Long Host") 函数,不仅在这里 没有,所有场合都没有。但是这里有:
htons()–"Host to Network Short"
  htonl()–"Host to Network Long"
  ntohs()–"Network to Host Short"
  ntohl()–"Network to Host Long"
现在,你可能想你已经知道它们了。你也可能想:“如果我想改变 char 的顺序要怎么办呢?” 但是你也许马上就想到,“用不着考虑的”。你也许 会想到:我的 68000 机器已经使用了网络字节顺序,我没有必要去调用 htonl() 转换 IP 地址。你可能是对的,但是当你移植你的程序到别的机器 上的时候,你的程序将失败。可移植性!这里是 Unix 世界!记住:在你 将数据放到网络上的时候,确信它们是网络字节顺序的。 
最后一点:为什么在数据结构 struct sockaddr_in 中, sin_addr 和 sin_port 需要转换为网络字节顺序,而sin_family 需不需要呢? 答案是: sin_addr 和 sin_port 分别封装在包的 IP 和 UDP 层。因此,它们必须要 是网络字节顺序。但是 sin_family 域只是被内核 (kernel) 使用来决定在数 据结构中包含什么类型的地址,所以它必须是本机字节顺序。同时, sin_family 没有发送到网络上,它们可以是本机字节顺序。 
——————————————————————————–
IP 地址和如何处理它们
现在我们很幸运,因为我们有很多的函数来方便地操作 IP 地址。没有 必要用手工计算它们,也没有必要用"<<"操作来储存成长整字型。 首先,假设你已经有了一个sockaddr_in结构体ina,你有一个IP地 址"132.241.5.10"要储存在其中,你就要用到函数inet_addr(),将IP地址从 点数格式转换成无符号长整型。使用方法如下:
ina.sin_addr.s_addr = inet_addr("132.241.5.10");
注意,inet_addr()返回的地址已经是网络字节格式,所以你无需再调用 函数htonl()。
我们现在发现上面的代码片断不是十分完整的,因为它没有错误检查。 显而易见,当inet_addr()发生错误时返回-1。记住这些二进制数字?(无符 号数)-1仅仅和IP地址255.255.255.255相符合!这可是广播地址!大错特 错!记住要先进行错误检查。
好了,现在你可以将IP地址转换成长整型了。有没有其相反的方法呢? 它可以将一个in_addr结构体输出成点数格式?这样的话,你就要用到函数 inet_ntoa()("ntoa"的含义是"network to ascii"),就像这样: 
printf("%s",inet_ntoa(ina.sin_addr));
它将输出IP地址。需要注意的是inet_ntoa()将结构体in-addr作为一 个参数,不是长整形。同样需要注意的是它返回的是一个指向一个字符的 指针。它是一个由inet_ntoa()控制的静态的固定的指针,所以每次调用 inet_ntoa(),它就将覆盖上次调用时所得的IP地址。例如:
char *a1, *a2;
.
.
a1 = inet_ntoa(ina1.sin_addr); /* 这是198.92.129.1 */
a2 = inet_ntoa(ina2.sin_addr); /* 这是132.241.5.10 */
printf("address 1: %s\n",a1);
printf("address 2: %s\n",a2);
输出如下:
address 1: 132.241.5.10
address 2: 132.241.5.10
假如你需要保存这个IP地址,使用strcopy()函数来指向你自己的字符 指针。
上面就是关于这个主题的介绍。稍后,你将学习将一个类 似"wintehouse.gov"的字符串转换成它所对应的IP地址(查阅域名服务,稍 后)。
——————————————————————————–
socket()函数 
我想我不能再不提这个了-下面我将讨论一下socket()系统调用。
下面是详细介绍:
#include <sys/types.h> 
#include <sys/socket.h> 
int socket(int domain, int type, int protocol); 
但是它们的参数是什么? 首先,domain 应该设置成 "AF_INET",就 象上面的数据结构struct sockaddr_in 中一样。然后,参数 type 告诉内核 是 SOCK_STREAM 类型还是 SOCK_DGRAM 类型。最后,把 protocol 设置为 "0"。(注意:有很多种 domain、type,我不可能一一列出了,请看 socket() 的 man帮助。当然,还有一个"更好"的方式去得到 protocol。同 时请查阅 getprotobyname() 的 man 帮助。) 
socket() 只是返回你以后在系统调用种可能用到的 socket 描述符,或 者在错误的时候返回-1。全局变量 errno 中将储存返回的错误值。(请参考 perror() 的 man 帮助。) 
——————————————————————————–
bind()函数
  一旦你有一个套接字,你可能要将套接字和机器上的一定的端口关联 起来。(如果你想用listen()来侦听一定端口的数据,这是必要一步–MUD 告 诉你说用命令 "telnet x.y.z 6969"。)如果你只想用 connect(),那么这个步 骤没有必要。但是无论如何,请继续读下去。
这里是系统调用 bind() 的大概:
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, struct sockaddr *my_addr, int addrlen); 
sockfd 是调用 socket 返回的文件描述符。my_addr 是指向数据结构 struct sockaddr 的指针,它保存你的地址(即端口和 IP 地址) 信息。 addrlen 设置为 sizeof(struct sockaddr)。 
简单得很不是吗? 再看看例子: 
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#define MYPORT 3490 
main()
   {
   int sockfd;
   struct sockaddr_in my_addr;
sockfd = socket(AF_INET, SOCK_STREAM, 0); /*需要错误检查 */
my_addr.sin_family = AF_INET; /* host byte order */ 
   my_addr.sin_port = htons(MYPORT); /* short, network byte order */ 
   my_addr.sin_addr.s_addr = inet_addr("132.241.5.10"); 
   bzero(&(my_addr.sin_zero),; /* zero the rest of the struct */ 
/* don’t forget your error checking for bind(): */ 
   bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)); 
   . 
   . 
   . 
这里也有要注意的几件事情。my_addr.sin_port 是网络字节顺序, my_addr.sin_addr.s_addr 也是的。另外要注意到的事情是因系统的不同, 包含的头文件也不尽相同,请查阅本地的 man 帮助文件。
在 bind() 主题中最后要说的话是,在处理自己的 IP 地址和/或端口的 时候,有些工作是可以自动处理的。
my_addr.sin_port = 0; /* 随机选择一个没有使用的端口 */ 
  my_addr.sin_addr.s_addr = INADDR_ANY; /* 使用自己的IP地址 */ 
通过将0赋给 my_addr.sin_port,你告诉 bind() 自己选择合适的端 口。同样,将 my_addr.sin_addr.s_addr 设置为 INADDR_ANY,你告诉 它自动填上它所运行的机器的 IP 地址。
如果你一向小心谨慎,那么你可能注意到我没有将 INADDR_ANY 转 换为网络字节顺序!这是因为我知道内部的东西:INADDR_ANY 实际上就 是 0!即使你改变字节的顺序,0依然是0。但是完美主义者说应该处处一 致,INADDR_ANY或许是12呢?你的代码就不能工作了,那么就看下面 的代码:
my_addr.sin_port = htons(0); /* 随机选择一个没有使用的端口 */ 
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);/* 使用自己的IP地址 */ 
你或许不相信,上面的代码将可以随便移植。我只是想指出,既然你 所遇到的程序不会都运行使用htonl的INADDR_ANY。
bind() 在错误的时候依然是返回-1,并且设置全局错误变量errno。 
在你调用 bind() 的时候,你要小心的另一件事情是:不要采用小于 1024的端口号。所有小于1024的端口号都被系统保留!你可以选择从1024 到65535的端口(如果它们没有被别的程序使用的话)。
你要注意的另外一件小事是:有时候你根本不需要调用它。如果你使 用 connect() 来和远程机器进行通讯,你不需要关心你的本地端口号(就象 你在使用 telnet 的时候),你只要简单的调用 connect() 就可以了,它会检 查套接字是否绑定端口,如果没有,它会自己绑定一个没有使用的本地端口。
——————————————————————————–
connect()程序
  现在我们假设你是个 telnet 程序。你的用户命令你得到套接字的文件 描述符。你听从命令调用了socket()。下一步,你的用户告诉你通过端口 23(标准 telnet 端口)连接到"132.241.5.10"。你该怎么做呢? 幸运的是,你正在阅读 connect()–如何连接到远程主机这一章。你可 不想让你的用户失望。 
connect() 系统调用是这样的: 
#include <sys/types.h> 
#include <sys/socket.h>
int connect(int sockfd, struct sockaddr *serv_addr, int addrlen); 
sockfd 是系统调用 socket() 返回的套接字文件描述符。serv_addr 是 保存着目的地端口和 IP 地址的数据结构 struct sockaddr。addrlen 设置 为 sizeof(struct sockaddr)。 
想知道得更多吗?让我们来看个例子: 
#include <string.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#define DEST_IP "132.241.5.10" 
  #define DEST_PORT 23 
main() 
   { 
int sockfd; 
struct sockaddr_in dest_addr; /* 目的地址*/ 
sockfd = socket(AF_INET, SOCK_STREAM, 0); /* 错误检查 */ 
dest_addr.sin_family = AF_INET; /* host byte order */ 
dest_addr.sin_port = htons(DEST_PORT); /* short, network byte order */ 
dest_addr.sin_addr.s_addr = inet_addr(DEST_IP); 
bzero(&(dest_addr.sin_zero),; /* zero the rest of the struct */ 
/* don’t forget to error check the connect()! */ 
connect(sockfd, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr)); 
   . 
   . 
   . 
  再一次,你应该检查 connect() 的返回值–它在错误的时候返回-1,并 设置全局错误变量 errno。 
同时,你可能看到,我没有调用 bind()。因为我不在乎本地的端口号。 我只关心我要去那。内核将为我选择一个合适的端口号,而我们所连接的 地方也自动地获得这些信息。一切都不用担心。 
——————————————————————————–
listen()函数
  是换换内容得时候了。假如你不希望与远程的一个地址相连,或者说, 仅仅是将它踢开,那你就需要等待接入请求并且用各种方法处理它们。处 理过程分两步:首先,你听–listen(),然后,你接受–accept() (请看下面的 内容)。
除了要一点解释外,系统调用 listen 也相当简单。
int listen(int sockfd, int backlog); 
sockfd 是调用 socket() 返回的套接字文件描述符。backlog 是在进入 队列中允许的连接数目。什么意思呢? 进入的连接是在队列中一直等待直 到你接受 (accept() 请看下面的文章)连接。它们的数目限制于队列的允许。 大多数系统的允许数目是20,你也可以设置为5到10。
和别的函数一样,在发生错误的时候返回-1,并设置全局错误变量 errno。
你可能想象到了,在你调用 listen() 前你或者要调用 bind() 或者让内 核随便选择一个端口。如果你想侦听进入的连接,那么系统调用的顺序可 能是这样的: 
socket(); 
  bind(); 
listen(); 
  /* accept() 应该在这 */ 
因为它相当的明了,我将在这里不给出例子了。(在 accept() 那一章的 代码将更加完全。)真正麻烦的部分在 accept()。 
——————————————————————————–
accept()函数
  准备好了,系统调用 accept() 会有点古怪的地方的!你可以想象发生 这样的事情:有人从很远的地方通过一个你在侦听 (listen()) 的端口连接 (connect()) 到你的机器。它的连接将加入到等待接受 (accept()) 的队列 中。你调用 accept() 告诉它你有空闲的连接。它将返回一个新的套接字文 件描述符!这样你就有两个套接字了,原来的一个还在侦听你的那个端口, 新的在准备发送 (send()) 和接收 ( recv()) 数据。这就是这个过程!
函数是这样定义的: 
#include <sys/socket.h>
int accept(int sockfd, void *addr, int *addrlen); 
sockfd 相当简单,是和 listen() 中一样的套接字描述符。addr 是个指 向局部的数据结构 sockaddr_in 的指针。这是要求接入的信息所要去的地 方(你可以测定那个地址在那个端口呼叫你)。在它的地址传递给 accept 之 前,addrlen 是个局部的整形变量,设置为 sizeof(struct sockaddr_in)。 accept 将不会将多余的字节给 addr。如果你放入的少些,那么它会通过改
变 addrlen 的值反映出来。 
同样,在错误时返回-1,并设置全局错误变量 errno。 
现在是你应该熟悉的代码片段。 
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#define MYPORT 3490 /*用户接入端口*/ 
#define BACKLOG 10 /* 多少等待连接控制*/ 
main() 
   { 
  int sockfd, new_fd; /* listen on sock_fd, new connection on new_fd */ 
  struct sockaddr_in my_addr; /* 地址信息 */ 
  struct sockaddr_in their_addr; /* connector’s address information */ 
  int sin_size; 
sockfd = socket(AF_INET, SOCK_STREAM, 0); /* 错误检查*/ 
my_addr.sin_family = AF_INET; /* host byte order */ 
  my_addr.sin_port = htons(MYPORT); /* short, network byte order */ 
  my_addr.sin_addr.s_addr = INADDR_ANY; /* auto-fill with my IP */ 
  bzero(&(my_addr.sin_zero),; /* zero the rest of the struct */ 
/* don’t forget your error checking for these calls: */ 
  bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)); 
listen(sockfd, BACKLOG); 
sin_size = sizeof(struct sockaddr_in); 
  new_fd = accept(sockfd, &their_addr, &sin_size); 
   . 
   . 
   . 
注意,在系统调用 send() 和 recv() 中你应该使用新的套接字描述符 new_fd。如果你只想让一个连接进来,那么你可以使用 close() 去关闭原 来的文件描述符 sockfd 来避免同一个端口更多的连接。 
——————————————————————————–
send() and recv()函数
  这两个函数用于流式套接字或者数据报套接字的通讯。如果你喜欢使 用无连接的数据报套接字,你应该看一看下面关于sendto() 和 recvfrom() 的章节。
send() 是这样的:
int send(int sockfd, const void *msg, int len, int flags); 
sockfd 是你想发送数据的套接字描述符(或者是调用 socket() 或者是 accept() 返回的。)msg 是指向你想发送的数据的指针。len 是数据的长度。 把 flags 设置为 0 就可以了。(详细的资料请看 send() 的 man page)。 
这里是一些可能的例子:
char *msg = "Beej was here!"; 
  int len, bytes_sent;
  . 
  . 
  len = strlen(msg);
  bytes_sent = send(sockfd, msg, len, 0);
  . 
  . 
  . 
send() 返回实际发送的数据的字节数–它可能小于你要求发送的数 目! 注意,有时候你告诉它要发送一堆数据可是它不能处理成功。它只是 发送它可能发送的数据,然后希望你能够发送其它的数据。记住,如果 send() 返回的数据和 len 不匹配,你就应该发送其它的数据。但是这里也 有个好消息:如果你要发送的包很小(小于大约 1K),它可能处理让数据一 次发送完。最后要说得就是,它在错误的时候返回-1,并设置 errno。
recv() 函数很相似:
int recv(int sockfd, void *buf, int len, unsigned int flags);
sockfd 是要读的套接字描述符。buf 是要读的信息的缓冲。len 是缓 冲的最大长度。flags 可以设置为0。(请参考recv() 的 man page。) recv() 返回实际读入缓冲的数据的字节数。或者在错误的时候返回-1, 同时设置 errno。
很简单,不是吗? 你现在可以在流式套接字上发送数据和接收数据了。 你现在是 Unix 网络程序员了!
——————————————————————————–
sendto() 和 recvfrom()函数
  “这很不错啊”,你说,“但是你还没有讲无连接数据报套接字呢?” 没问题,现在我们开始这个内容。
既然数据报套接字不是连接到远程主机的,那么在我们发送一个包之 前需要什么信息呢? 不错,是目标地址!看看下面的:
int sendto(int sockfd, const void *msg, int len, unsigned int flags, 
  const struct sockaddr *to, int tolen); 
你已经看到了,除了另外的两个信息外,其余的和函数 send() 是一样 的。 to 是个指向数据结构 struct sockaddr 的指针,它包含了目的地的 IP 地址和端口信息。tolen 可以简单地设置为 sizeof(struct sockaddr)。 和函数 send() 类似,sendto() 返回实际发送的字节数(它也可能小于 你想要发送的字节数!),或者在错误的时候返回 -1。
相似的还有函数 recv() 和 recvfrom()。recvfrom() 的定义是这样的:
int recvfrom(int sockfd, void *buf, int len, unsigned int flags,  struct sockaddr *from, int *fromlen);
又一次,除了两个增加的参数外,这个函数和 recv() 也是一样的。from 是一个指向局部数据结构 struct sockaddr 的指针,它的内容是源机器的 IP 地址和端口信息。fromlen 是个 int 型的局部指针,它的初始值为 sizeof(struct sockaddr)。函数调用返回后,fromlen 保存着实际储存在 from 中的地址的长度。
recvfrom() 返回收到的字节长度,或者在发生错误后返回 -1。
记住,如果你用 connect() 连接一个数据报套接字,你可以简单的调 用 send() 和 recv() 来满足你的要求。这个时候依然是数据报套接字,依 然使用 UDP,系统套接字接口会为你自动加上了目标和源的信息。
——————————————————————————–
close()和shutdown()函数
  你已经整天都在发送 (send()) 和接收 (recv()) 数据了,现在你准备关 闭你的套接字描述符了。这很简单,你可以使用一般的 Unix 文件描述符 的 close() 函数:
  close(sockfd);
它将防止套接字上更多的数据的读写。任何在另一端读写套接字的企 图都将返回错误信息。
如果你想在如何关闭套接字上有多一点的控制,你可以使用函数 shutdown()。它允许你将一定方向上的通讯或者双向的通讯(就象close()一 样)关闭,你可以使用:
int shutdown(int sockfd, int how); 
sockfd 是你想要关闭的套接字文件描述复。how 的值是下面的其中之 一:
  0 – 不允许接受
  1 – 不允许发送
  2 – 不允许发送和接受(和 close() 一样)
shutdown() 成功时返回 0,失败时返回 -1(同时设置 errno。) 如果在无连接的数据报套接字中使用shutdown(),那么只不过是让 send() 和 recv() 不能使用(记住你在数据报套接字中使用了 connect 后 是可以使用它们的)。
——————————————————————————–
getpeername()函数
  这个函数太简单了。
它太简单了,以至我都不想单列一章。但是我还是这样做了。 函数 getpeername() 告诉你在连接的流式套接字上谁在另外一边。函 数是这样的:
#include <sys/socket.h>
int getpeername(int sockfd, struct sockaddr *addr, int *addrlen);
sockfd 是连接的流式套接字的描述符。addr 是一个指向结构 struct sockaddr (或者是 struct sockaddr_in) 的指针,它保存着连接的另一边的 信息。addrlen 是一个 int 型的指针,它初始化为 sizeof(struct sockaddr)。 函数在错误的时候返回 -1,设置相应的 errno。
一旦你获得它们的地址,你可以使用 inet_ntoa() 或者 gethostbyaddr() 来打印或者获得更多的信息。但是你不能得到它的帐号。(如果它运行着愚 蠢的守护进程,这是可能的,但是它的讨论已经超出了本文的范围,请参 考 RFC-1413 以获得更多的信息。) 
——————————————————————————–
gethostname()函数
  甚至比 getpeername() 还简单的函数是 gethostname()。它返回你程 序所运行的机器的主机名字。然后你可以使用 gethostbyname() 以获得你 的机器的 IP 地址。
  下面是定义:
  #include <unistd.h>
int gethostname(char *hostname, size_t size);
参数很简单:hostname 是一个字符数组指针,它将在函数返回时保存
主机名。size是hostname 数组的字节长度。
函数调用成功时返回 0,失败时返回 -1,并设置 errno。
——————————————————————————–
域名服务(DNS)
  如果你不知道 DNS 的意思,那么我告诉你,它代表域名服务(Domain Name Service)。它主要的功能是:你给它一个容易记忆的某站点的地址, 它给你 IP 地址(然后你就可以使用 bind(), connect(), sendto() 或者其它 函数) 。当一个人输入:
   $ telnet whitehouse.gov 
telnet 能知道它将连接 (connect()) 到 "198.137.240.100"。 
但是这是如何工作的呢? 你可以调用函数 gethostbyname(): 
#include <netdb.h>
  struct hostent *gethostbyname(const char *name); 
很明白的是,它返回一个指向 struct hostent 的指针。这个数据结构 是这样的:
   struct hostent {
   char *h_name;
   char **h_aliases;
   int h_addrtype;
   int h_length;
   char **h_addr_list;
   };
   #define h_addr h_addr_list[0] 
这里是这个数据结构的详细资料: 
struct hostent: 
  h_name – 地址的正式名称。
  h_aliases – 空字节-地址的预备名称的指针。
  h_addrtype -地址类型; 通常是AF_INET。 
  h_length – 地址的比特长度。
  h_addr_list – 零字节-主机网络地址指针。网络字节顺序。
  h_addr – h_addr_list中的第一地址。
gethostbyname() 成功时返回一个指向结构体 hostent 的指针,或者 是个空 (NULL) 指针。(但是和以前不同,不设置errno,h_errno 设置错 误信息。请看下面的 herror()。) 
但是如何使用呢? 有时候(我们可以从电脑手册中发现),向读者灌输 信息是不够的。这个函数可不象它看上去那么难用。
这里是个例子:
#include <stdio.h>
  #include <stdlib.h>
  #include <errno.h>
  #include <netdb.h>
  #include <sys/types.h>
  #include <netinet/in.h>
int main(int argc, char *argv[])
   {
   struct hostent *h;
if (argc != 2) { /* 检查命令行 */
   fprintf(stderr,"usage: getip address\n");
   exit(1);
   }
if ((h=gethostbyname(argv[1])) == NULL) { /* 取得地址信息 */
   herror("gethostbyname");
   exit(1);
   }
printf("Host name : %s\n", h->h_name);
  printf("IP Address : %s\n",inet_ntoa(*((struct in_addr *)h->h_addr)));
return 0;
   }
在使用 gethostbyname() 的时候,你不能用 perror() 打印错误信息 (因为 errno 没有使用),你应该调用 herror()。
相当简单,你只是传递一个保存机器名的字符串(例如 "whitehouse.gov") 给 gethostbyname(),然后从返回的数据结构 struct hostent 中获取信息。 
唯一也许让人不解的是输出 IP 地址信息。h->h_addr 是一个 char *, 但是 inet_ntoa() 需要的是 struct in_addr。因此,我转换 h->h_addr 成 struct in_addr *,然后得到数据。
——————————————————————————–
客户-服务器背景知识
  这里是个客户–服务器的世界。在网络上的所有东西都是在处理客户进 程和服务器进程的交谈。举个telnet 的例子。当你用 telnet (客户)通过23 号端口登陆到主机,主机上运行的一个程序(一般叫 telnetd,服务器)激活。 它处理这个连接,显示登陆界面,等等。


图2:客户机和服务器的关系
图 2 说明了客户和服务器之间的信息交换。 
注意,客户–服务器之间可以使用SOCK_STREAM、SOCK_DGRAM 或者其它(只要它们采用相同的)。一些很好的客户–服务器的例子有 telnet/telnetd、 ftp/ftpd 和 bootp/bootpd。每次你使用 ftp 的时候,在远 端都有一个 ftpd 为你服务。 
一般,在服务端只有一个服务器,它采用 fork() 来处理多个客户的连 接。基本的程序是:服务器等待一个连接,接受 (accept()) 连接,然后 fork() 一个子进程处理它。这是下一章我们的例子中会讲到的。
——————————————————————————–
简单的服务器
  这个服务器所做的全部工作是在流式连接上发送字符串 "Hello, World!\n"。你要测试这个程序的话,可以在一台机器上运行该程序,然后 在另外一机器上登陆: 
   $ telnet remotehostname 3490 
remotehostname 是该程序运行的机器的名字。 
服务器代码: 
#include <stdio.h>
  #include <stdlib.h>
  #include <errno.h>
  #include <string.h>
  #include <sys/types.h>
  #include <netinet/in.h>
  #include <sys/socket.h>
  #include <sys/wait.h>
#define MYPORT 3490 /*定义用户连接端口*/ 
#define BACKLOG 10 /*多少等待连接控制*/ 
main() 
   { 
   int sockfd, new_fd; /* listen on sock_fd, new connection on new_fd 
*/ 
   struct sockaddr_in my_addr; /* my address information */ 
   struct sockaddr_in their_addr; /* connector’s address information */ 
   int sin_size;
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { 
   perror("socket"); 
   exit(1); 
   } 

my_addr.sin_family = AF_INET; /* host byte order */ 
   my_addr.sin_port = htons(MYPORT); /* short, network byte order */ 
   my_addr.sin_addr.s_addr = INADDR_ANY; /* auto-fill with my IP */ 
   bzero(&(my_addr.sin_zero),; /* zero the rest of the struct */ 

if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct 
sockaddr))== -1) { 
   perror("bind"); 
   exit(1); 
   } 
if (listen(sockfd, BACKLOG) == -1) { 
   perror("listen"); 
   exit(1); 
   } 

while(1) { /* main accept() loop */ 
   sin_size = sizeof(struct sockaddr_in); 
   if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, \ 
   &sin_size)) == -1) { 
   perror("accept"); 
   continue; 
   } 
   printf("server: got connection from %s\n", \ 
   inet_ntoa(their_addr.sin_addr)); 
   if (!fork()) { /* this is the child process */ 
   if (send(new_fd, "Hello, world!\n", 14, 0) == -1) 
   perror("send"); 
   close(new_fd); 
   exit(0); 
   } 
   close(new_fd); /* parent doesn’t need this */ 
while(waitpid(-1,NULL,WNOHANG) > 0); /* clean up child processes */ 
   } 
   } 
如果你很挑剔的话,一定不满意我所有的代码都在一个很大的main() 函数中。如果你不喜欢,可以划分得更细点。
你也可以用我们下一章中的程序得到服务器端发送的字符串。
——————————————————————————–
简单的客户程序 
  这个程序比服务器还简单。这个程序的所有工作是通过 3490 端口连接到命令行中指定的主机,然后得到服务器发送的字符串。 
客户代码: 
#include <stdio.h>
  #include <stdlib.h>
  #include <errno.h>
  #include <string.h>
  #include <sys/types.h>
  #include <netinet/in.h>
  #include <sys/socket.h>
  #include <sys/wait.h>
#define PORT 3490 /* 客户机连接远程主机的端口 */ 
#define MAXDATASIZE 100 /* 每次可以接收的最大字节 */ 
int main(int argc, char *argv[]) 
   { 
   int sockfd, numbytes; 
   char buf[MAXDATASIZE]; 
   struct hostent *he; 
   struct sockaddr_in their_addr; /* connector’s address information */ 
if (argc != 2) { 
   fprintf(stderr,"usage: client hostname\n"); 
   exit(1); 
   } 
if ((he=gethostbyname(argv[1])) == NULL) { /* get the host info */ 
   herror("gethostbyname"); 
   exit(1); 
   } 

if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { 
   perror("socket"); 
   exit(1); 
   } 

their_addr.sin_family = AF_INET; /* host byte order */ 
  their_addr.sin_port = htons(PORT); /* short, network byte order */ 
  their_addr.sin_addr = *((struct in_addr *)he->h_addr); 
  bzero(&(their_addr.sin_zero),; /* zero the rest of the struct */ 
if (connect(sockfd, (struct sockaddr *)&their_addr,sizeof(struct 
sockaddr)) == -1) { 
   perror("connect"); 
   exit(1); 
   } 
if ((numbytes=recv(sockfd, buf, MAXDATASIZE, 0)) == -1) { 
   perror("recv"); 
   exit(1); 
   } 
buf[numbytes] = ‘\0′; 
printf("Received: %s",buf); 
close(sockfd); 
return 0; 
   } 
注意,如果你在运行服务器之前运行客户程序,connect() 将返回 "Connection refused" 信息,这非常有用。
——————————————————————————–
数据包 Sockets 
  我不想讲更多了,所以我给出代码 talker.c 和 listener.c。 
listener 在机器上等待在端口 4590 来的数据包。talker 发送数据包到 一定的机器,它包含用户在命令行输入的内容。 
这里就是 listener.c: 
#include <stdio.h>
  #include <stdlib.h>
  #include <errno.h>
  #include <string.h>
  #include <sys/types.h>
  #include <netinet/in.h>
  #include <sys/socket.h>
  #include <sys/wait.h>
#define MYPORT 4950 /* the port users will be sending to */ 
#define MAXBUFLEN 100 
main() 
   { 
   int sockfd; 
   struct sockaddr_in my_addr; /* my address information */ 
   struct sockaddr_in their_addr; /* connector’s address information */ 
   int addr_len, numbytes; 
   char buf[MAXBUFLEN]; 
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { 
   perror("socket"); 
   exit(1); 
   } 
my_addr.sin_family = AF_INET; /* host byte order */ 
   my_addr.sin_port = htons(MYPORT); /* short, network byte order */ 
   my_addr.sin_addr.s_addr = INADDR_ANY; /* auto-fill with my IP */ 
   bzero(&(my_addr.sin_zero),; /* zero the rest of the struct */ 
if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) 

   == -1) { 
   perror("bind"); 
   exit(1); 
   }
addr_len = sizeof(struct sockaddr); 
   if ((numbytes=recvfrom(sockfd, buf, MAXBUFLEN, 0, \ 
   (struct sockaddr *)&their_addr, &addr_len)) == -1) { 
   perror("recvfrom"); 
   exit(1); 
   } 
printf("got packet from %s\n",inet_ntoa(their_addr.sin_addr)); 
   printf("packet is %d bytes long\n",numbytes); 
   buf[numbytes] = ‘\0′; 
   printf("packet contains \"%s\"\n",buf); 
close(sockfd); 
   } 
注意在我们的调用 socket(),我们最后使用了 SOCK_DGRAM。同时, 没有必要去使用 listen() 或者 accept()。我们在使用无连接的数据报套接 字! 
下面是 talker.c: 
#include <stdio.h>
  #include <stdlib.h>
  #include <errno.h>
  #include <string.h>
  #include <sys/types.h>
  #include <netinet/in.h>
  #include <sys/socket.h>
  #include <sys/wait.h>
#define MYPORT 4950 /* the port users will be sending to */ 
int main(int argc, char *argv[]) 
   { 
   int sockfd; 
   struct sockaddr_in their_addr; /* connector’s address information */ 
   struct hostent *he; 
   int numbytes; 

if (argc != 3) { 
   fprintf(stderr,"usage: talker hostname message\n"); 
   exit(1); 
   } 

if ((he=gethostbyname(argv[1])) == NULL) { /* get the host info */ 
   herror("gethostbyname"); 
   exit(1); 
   } 

if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { 
   perror("socket"); 
   exit(1); 
   } 

their_addr.sin_family = AF_INET; /* host byte order */ 
   their_addr.sin_port = htons(MYPORT); /* short, network byte order 
*/ 
   their_addr.sin_addr = *((struct in_addr *)he->h_addr); 
   bzero(&(their_addr.sin_zero),; /* zero the rest of the struct */ 
if ((numbytes=sendto(sockfd, argv[2], strlen(argv[2]), 0, \ 
   (struct sockaddr *)&their_addr, sizeof(struct sockaddr))) == -1) { 
   perror("sendto"); 
   exit(1); 
   } 
printf("sent %d bytes to 
%s\n",numbytes,inet_ntoa(their_addr.sin_addr)); 
close(sockfd); 
return 0; 
   } 
这就是所有的了。在一台机器上运行 listener,然后在另外一台机器上 运行 talker。观察它们的通讯!
除了一些我在上面提到的数据套接字连接的小细节外,对于数据套接 字,我还得说一些,当一个讲话者呼叫connect()函数时并指定接受者的地 址时,从这点可以看出,讲话者只能向connect()函数指定的地址发送和接 受信息。因此,你不需要使用sendto()和recvfrom(),你完全可以用send() 和recv()代替。
——————————————————————————–
阻塞 
  阻塞,你也许早就听说了。"阻塞"是 "sleep" 的科技行话。你可能注意 到前面运行的 listener 程序,它在那里不停地运行,等待数据包的到来。 实际在运行的是它调用 recvfrom(),然后没有数据,因此 recvfrom() 说" 阻塞 (block)",直到数据的到来。
很多函数都利用阻塞。accept() 阻塞,所有的 recv*() 函数阻塞。它 们之所以能这样做是因为它们被允许这样做。当你第一次调用 socket() 建 立套接字描述符的时候,内核就将它设置为阻塞。如果你不想套接字阻塞, 你就要调用函数 fcntl(): 
#include <unistd.h>
  #include <fontl.h>
   . 
   . 
   sockfd = socket(AF_INET, SOCK_STREAM, 0); 
   fcntl(sockfd, F_SETFL, O_NONBLOCK); 
   . 
   . 
  通过设置套接字为非阻塞,你能够有效地"询问"套接字以获得信息。如 果你尝试着从一个非阻塞的套接字读信息并且没有任何数据,它不允许阻 塞–它将返回 -1 并将 errno 设置为 EWOULDBLOCK。 
但是一般说来,这种询问不是个好主意。如果你让你的程序在忙等状 态查询套接字的数据,你将浪费大量的 CPU 时间。更好的解决之道是用 下一章讲的 select() 去查询是否有数据要读进来。
——————————————————————————–
select()–多路同步 I/O
  虽然这个函数有点奇怪,但是它很有用。假设这样的情况:你是个服 务器,你一边在不停地从连接上读数据,一边在侦听连接上的信息。 没问题,你可能会说,不就是一个 accept() 和两个 recv() 吗? 这么 容易吗,朋友? 如果你在调用 accept() 的时候阻塞呢? 你怎么能够同时接 受 recv() 数据? “用非阻塞的套接字啊!” 不行!你不想耗尽所有的 CPU 吧? 那么,该如何是好?
select() 让你可以同时监视多个套接字。如果你想知道的话,那么它就 会告诉你哪个套接字准备读,哪个又准备写,哪个套接字又发生了例外 (exception)。
闲话少说,下面是 select():
#include <sys/time.h>
  #include <sys/types.h>
  #include <unistd.h>
int select(int numfds, fd_set *readfds, fd_set *writefds,fd_set 
*exceptfds, struct timeval *timeout);
这个函数监视一系列文件描述符,特别是 readfds、writefds 和 exceptfds。如果你想知道你是否能够从标准输入和套接字描述符 sockfd 读入数据,你只要将文件描述符 0 和 sockfd 加入到集合 readfds 中。参 数 numfds 应该等于最高的文件描述符的值加1。在这个例子中,你应该 设置该值为 sockfd+1。因为它一定大于标准输入的文件描述符 (0)。 当函数 select() 返回的时候,readfds 的值修改为反映你选择的哪个 文件描述符可以读。你可以用下面讲到的宏 FD_ISSET() 来测试。 在我们继续下去之前,让我来讲讲如何对这些集合进行操作。每个集 合类型都是 fd_set。下面有一些宏来对这个类型进行操作: 
FD_ZERO(fd_set *set) – 清除一个文件描述符集合
  FD_SET(int fd, fd_set *set) – 添加fd到集合 
  FD_CLR(int fd, fd_set *set) – 从集合中移去fd 
  FD_ISSET(int fd, fd_set *set) – 测试fd是否在集合中 
最后,是有点古怪的数据结构 struct timeval。有时你可不想永远等待 别人发送数据过来。也许什么事情都没有发生的时候你也想每隔96秒在终 端上打印字符串 "Still Going…"。这个数据结构允许你设定一个时间,如果 时间到了,而 select() 还没有找到一个准备好的文件描述符,它将返回让 你继续处理。 
数据结构 struct timeval 是这样的: 
struct timeval { 
   int tv_sec; /* seconds */ 
   int tv_usec; /* microseconds */ 
   }; 
只要将 tv_sec 设置为你要等待的秒数,将 tv_usec 设置为你要等待 的微秒数就可以了。是的,是微秒而不是毫秒。1,000微秒等于1毫秒,1,000 毫秒等于1秒。也就是说,1秒等于1,000,000微秒。为什么用符号 "usec" 呢? 字母 "u" 很象希腊字母 Mu,而 Mu 表示 "微" 的意思。当然,函数 返回的时候 timeout 可能是剩余的时间,之所以是可能,是因为它依赖于 你的 Unix 操作系统。 
哈!我们现在有一个微秒级的定时器!别计算了,标准的 Unix 系统 的时间片是100毫秒,所以无论你如何设置你的数据结构 struct timeval, 你都要等待那么长的时间。 
还有一些有趣的事情:如果你设置数据结构 struct timeval 中的数据为 0,select() 将立即超时,这样就可以有效地轮询集合中的所有的文件描述 符。如果你将参数 timeout 赋值为 NULL,那么将永远不会发生超时,即 一直等到第一个文件描述符就绪。最后,如果你不是很关心等待多长时间, 那么就把它赋为 NULL 吧。 
下面的代码演示了在标准输入上等待 2.5 秒: 
#include <sys/time.h>
  #include <sys/types.h>
  #include <unistd.h>
#define STDIN 0 /* file descriptor for standard input */ 
main() 
   { 
  struct timeval tv; 
  fd_set readfds; 
tv.tv_sec = 2; 
  tv.tv_usec = 500000; 
FD_ZERO(&readfds); 
  FD_SET(STDIN, &readfds); 
/* don’t care about writefds and exceptfds: */ 
  select(STDIN+1, &readfds, NULL, NULL, &tv); 
if (FD_ISSET(STDIN, &readfds)) 
  printf("A key was pressed!\n"); 
  else 
  printf("Timed out.\n"); 
  } 
如果你是在一个 line buffered 终端上,那么你敲的键应该是回车 (RETURN),否则无论如何它都会超时。
现在,你可能回认为这就是在数据报套接字上等待数据的方式–你是对 的:它可能是。有些 Unix 系统可以按这种方式,而另外一些则不能。你 在尝试以前可能要先看看本系统的 man page 了。
最后一件关于 select() 的事情:如果你有一个正在侦听 (listen()) 的套 接字,你可以通过将该套接字的文件描述符加入到 readfds 集合中来看是 否有新的连接。
这就是我关于函数select() 要讲的所有的东西。
  参考书目: 
  Internetworking with TCP/IP, volumes I-III by Douglas E. Comer and 
David L. Stevens. Published by Prentice Hall. Second edition ISBNs: 
0-13-468505-9, 0-13-472242-6, 0-13-474222-2. There is a third edition of 
this set which covers IPv6 and IP over ATM. 
  Using C on the UNIX System by David A. Curry. Published by 
O’Reilly & Associates, Inc. ISBN 0-937175-23-4. 
  TCP/IP Network Administration by Craig Hunt. Published by O’Reilly 
& Associates, Inc. ISBN 0-937175-82-X. 
  TCP/IP Illustrated, volumes 1-3 by W. Richard Stevens and Gary R. 
Wright. Published by Addison Wesley. ISBNs: 0-201-63346-9, 
0-201-63354-X, 0-201-63495-3. 
Unix Network Programming by W. Richard Stevens. Published by 
Prentice Hall. ISBN 0-13-949876-1. 
  On the web: 
  BSD Sockets: A Quick And Dirty Primer 
  (http://www.cs.umn.edu/~bentlema/unix/–has other great Unix 
system programming info, too!) 
Client-Server Computing 
  (http://pandonia.canberra.edu.au/ClientServer/socket.html
Intro to TCP/IP (gopher) 

(gopher://gopher-chem.ucdavis.edu/11/Index/Internet_aw/Intro_the_Inter
net/intro.to.ip/) 
Internet Protocol Frequently Asked Questions (France) 
  (http://web.cnam.fr/Network/TCP-IP/
The Unix Socket FAQ 
  (http://www.ibrado.com/sock-faq/
RFCs–the real dirt: 
  RFC-768 — The User Datagram Protocol


Posted in C/C++ | Leave a comment

C/C++头文件一览

C/C++头文件一览

关键词C,C++,头文件,                                          


 
C、传统 C++

#include <assert.h>    //设定插入点
#include <ctype.h>     //字符处理
#include <errno.h>     //定义错误码
#include <float.h>     //浮点数处理
#include <fstream.h>    //文件输入/输出
#include <iomanip.h>    //参数化输入/输出
#include <iostream.h>   //数据流输入/输出
#include <limits.h>    //定义各种数据类型最值常量
#include <locale.h>    //定义本地化函数
#include <math.h>     //定义数学函数
#include <stdio.h>     //定义输入/输出函数
#include <stdlib.h>    //定义杂项函数及内存分配函数
#include <string.h>    //字符串处理
#include <strstrea.h>   //基于数组的输入/输出
#include <time.h>     //定义关于时间的函数
#include <wchar.h>     //宽字符处理及输入/输出
#include <wctype.h>    //宽字符分类

//////////////////////////////////////////////////////////////////////////

标准 C++ (同上的不再注释)

#include <algorithm>    //STL 通用算法
#include <bitset>     //STL 位集容器
#include <cctype>
#include <cerrno>
#include <clocale>
#include <cmath>
#include <complex>     //复数类
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>      //STL 双端队列容器
#include <exception>    //异常处理类
#include <fstream>
#include <functional>   //STL 定义运算函数(代替运算符)
#include <limits>
#include <list>      //STL 线性列表容器
#include <map>       //STL 映射容器
#include <iomanip>
#include <ios>       //基本输入/输出支持
#include <iosfwd>     //输入/输出系统使用的前置声明
#include <iostream>
#include <istream>     //基本输入流
#include <ostream>     //基本输出流
#include <queue>      //STL 队列容器
#include <set>       //STL 集合容器
#include <sstream>     //基于字符串的流
#include <stack>      //STL 堆栈容器    
#include <stdexcept>    //标准异常类
#include <streambuf>    //底层输入/输出支持
#include <string>     //字符串类
#include <utility>     //STL 通用模板类
#include <vector>     //STL 动态数组容器
#include <cwchar>
#include <cwctype>

using namespace std;

//////////////////////////////////////////////////////////////////////////

C99 增加

#include <complex.h>   //复数处理
#include <fenv.h>    //浮点环境
#include <inttypes.h>  //整数格式转换
#include <stdbool.h>   //布尔环境
#include <stdint.h>   //整型环境
#include <tgmath.h>   //通用类型数学宏 

Posted in C/C++ | Leave a comment

转贴一篇ruby和python的比较!

刚才在邮件列表看到一个朋友发了这样一封邮件,让我对ruby起了好奇之心:

Tim O’Reilly 把技术图书的销量看作技术流行趋势指示剂:在过去的一年 Java 图书销量下滑了4%,C# 增长了16%,Python 增长了20%,Ruby 增长了1552% (你没看错,这里没有小数点)……;而 Gregg 把这称为程序设计语言时尚的回归:Perl == /bin/sh and C、Python == LISP、Ruby == Smalltalk,认为现在更多的是在重复历史,并预言:Ruby 之后,下一个亮点 (Web 3.0?) 将由以下一种程序设计语言来扮演:COBOL、Ada、ALGOL、x86 assembler。

其实早就听说过ruby这个东西,但因为它是鬼子写出来的东西,所以心里自然的有一种抵触情绪,一直没去看相关的东西,不过最近这个东西越来越火了,刚才找了几篇关于ruby的文章看了一下,顺便从www.woodpecker.org.cn 上转贴了一篇ruby和python的比较,一会儿打算去下载一个ruby for windows玩儿一下.

ruby和python的比较 — tomz

python和ruby的相同点:

  • 都强调语法简单,都具有更一般的表达方式。python是缩进,ruby是类basic的表达。都大量减少了符号。
  • 都是动态数据类型。都是有丰富的数据结构。
  • 都具有c语言扩展能力,都具有可移植性,比perl的可移植性更好。也都可以作为嵌入语言。
  • 都是面向对象的语言,都可以作为大项目的开发工具。
  • 都有丰富的库支持。
  • 也有最宽松的版权许可,除了一些工具属于GNU世界。
  • 都有lisp特色的eval函数,也都能把函数作为参数。
  • 也有图形界面的ruby的专门编辑器。
  • 都获得了广泛的c库的支持。如qt、gtk、tk、SDL、FOX等,ruby计划实现SWIG接口。
  • 都有完善的文档。

和python相比ruby的优点:

  • 具有正则表达式和嵌入html的功能。python也有正则表达式,但没有ruby的应用方便和广泛。python的嵌入html项目才刚起步。ruby 还有apache的mod模块。ruby本身也实现和很多unix工具,如racc,doctools。比python更亲近linux。
  • 比python功能更完整的面向对象的语法。
    • ruby的整个库都是具有类继承的结构。
    • 他的基本的数据类型和运算符都是可以重载的。
    • ruby主要的功能都是通过对象的方法调用来实现的,而不是函数。python也在向这方面发展,但没有ruby做的彻底。
    • ruby的类是更规范的单继承,还有接口等概念的实现。
  • python可以实现在列表内的条件语句、循环语句,而ruby用“块”的方式来实现这个功能,比python的更灵活,更具有通用性。
  • ruby具有类似lisp的彻底的函数方式的条件语句、循环语句等。语句的表达能力更强。
  • 附带一些unix工具,如racc等。

和python相比ruby的不足:

  • 最大的不足正是因为ruby的强大所引起的。它没有python的简单性好。比较复杂的面向对象语法、“块”语法的引入、正则表达式的引入、一些简写标记都增加了语言的复杂性。
  • python的缩进表达方式比ruby的basic的表达方式更让人悦目,ruby程序的满眼的end让人不舒服。当然,ruby认为end的方式比python更先进。
  • ruby还没有python的“自省”的能力,没有从程序文件中生成文档的能力。
  • ruby没有国际化的支持。国际化支持在ruby的计划中。这是因为ruby的历史比python要短造成的。
  • ruby没有类似jython的东西。

python和ruby的语言的选择:

    从简单的就是好的来说,选python是没错的。python适合寻找简单语言的人,这很可能造成python更流行,因此也有更多的支持。但如果要追求更强大的语法功能,则ruby是好的选择。因为ruby和python的哲学有很多相似的地方,先从python入手,尽量用python,如果 python的能力不足了,可以在找ruby。

ruby和python的比较,就像五笔和拼音输入法的比较。拼音作为入门的输入法和长久使用的输入法都没有问题。五笔适合更高要求的情况。如果追求性能的不妨学学ruby。对编程语言感兴趣,想了解各种编程概念的学ruby也会很兴奋。

Posted in Python | Leave a comment

gcc/mingw/libstdc简介

gcc/mingw/libstdc简介

10/23/2005 9:48:51 PM

GCC:GNU Compiler Collection
[介绍] 她是一个GNU编译器集合,目前包括 C, C++, Objective-C, Fortran, 和 Ada 等, 以及这些语言需要的库,比如 libstdc++。
[历史小知识] 原来的GCC就是指GUN C Compiler,因为后来加入了C++等编译器,于是更名为GNU Compiler Collection。
[提示] GCC是一个编译器集合,gcc才是C语言编译器,g++才是C++语言编译器。
[最新发布版本] GCC4.0.2 发布于 2005-09-28
[主页地址] http://gcc.gnu.org/

libstdc++:GNU Standard C++ Library
[介绍] g++只是一个编译器,而g++中C++需要的标准库文件由libstdc++提供。
[最新发布版本] libstdc++-v3.0.97 发布于 2002-03-05
[主页地址] http://gcc.gnu.org/libstdc++/

MinGW:Minimalist GNU for Windows
[介绍] 她是一个GCC的Windows移植版本,不属于GNU范畴。
[最新发布版本] 3.4.4 发布于 2005-05-30
                     5.0.0 发布于 2005-10-02
[主页地址] http://www.mingw.org/
               http://sourceforge.net/projects/mingw/

 

[附] MinGW相关的IDE:
a. MinGW Developer Studio
  [介绍] 她是一个多平台的C/C++集成开发环境,以MinGW为编译内核,也支持Linux/FreeBSD等操作系统。
  [优点] 支持很多功能,比如代码折叠等,和VC++的界面、使用方法类似,还附带一个WinAPI参考手册。
  [最新发布版本 for Windows] 2.05 发布于 2005-04-01 以 MinGW3.4.2 为编译内核
  [主页地址] http://www.parinyasoft.com/
  MinGWStudioFullSetup-2.05 官方下载地址:http://www.parinyasoft.com/download/MinGWStudioFullSetup-2.05.exe
  [ 因为访问量太大,介绍性文字可以去 http://freshmeat.net/projects/mingw-devstudio 
  查看,下载可以用 http://csys.yonsei.ac.kr/web/c/MinGWStudioFullSetup-2.05.exe ]
b. Bloodshed Dev-C++
  [介绍] 她是一个Windows下的C/C++集成开发环境,以MinGW为编译内核。
  [最新发布版本] Dev-C++ 5.0 beta 9.2 (4.9.9.2) 以 MinGW3.4.2 为编译内核
  [主页地址] http://www.bloodshed.net/dev/devcpp.html (需要境外代理服务器)
  Dev-C++ 5.0 beta 9.2 (4.9.9.2) 官方下载地址:
  http://sourceforge.net/projects/dev-cpp/

Posted in C/C++ | 1 Comment

Windows下C语言网络编程快速入门

信息来源:www.csdn.net
作者blog:http://blog.csdn.net/i_like_cpp/

C语言的学习,一般的方式是,先学C,然后是C++,最好还要有汇编语言和微机原理基础,然后才是Visual C++。这样的方式,对学习者来说,要花费很多时间和耐力。而在学校教学中,也没有时间深入学习Windows编程的实用技术了。

  其实,具有了C语言基础后,再有一些基本的C++类的概念,就可以直接学习Windows C编程了。

  一、走近Windows C语言

  很多语言都把显示一个“Hello,World!”做为第一个入门程序, C语言的第一个程序是这样的:

#include<stdio.h>
main()
{
 printf(“Hello,World!”);
}

  如果把main函数写成带参数的main函数,应该是:

#include<stdio.h>
main(int arge,char *argv[])
{
 printf(“Hello,World!”);
}

  Windows C的第一个程序和这个程序在形式和原理上都是一致的,只是有两点不同:

  1. 主函数接收的形参不只是命令行中的字符串的个数和字符串的首地址。

  2. C语言的很多函数在Windows C中都可以继续使用,但象printf()屏幕显示等函数就不能继续使用了。因为Windows是多任务操作系统,屏幕已不再为某一个应用程序所独有,Windows C应用程序要显示字符串,需要使用Windows提供的API函数,开自己的窗口

  下面是一个最简单的,显示“Hello,World!”的Windows C程序:

#include<windows.h>
APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
LPSTR lpCmdLine,int nCmdShow)
{
 MessageBox(NULL,"Hello,World!","第一个Windows C程序",MB_OK|MB_ICONASTERISK);
}

  主函数的形参有四个:

  1) Hinstance:接收程序运行时当前实例的句柄;
  2) HprivInstance:前一个实例的句柄;
  3) LpCmdLine:程序命令行指针;
  4) NcmdShow:一个用来指定窗口显示方式的整数。

  这几个参数的使用我们会在深入的学习中介绍的。

  显示Hello,Word!字符串,我们使用了一个MessageBox函数,这个函数会在屏幕上显示一个对话框,它的原型是:

int MessageBox(HWND hWnd,LPCTSTR lpText,LPCTSTR lpCaption,UNIT uType)

  四个参数分别是:

  1) HWnd:父窗口的句柄;
  2) LpText:要显示字符串的指针;
  3) LpCaption:对话框标题字符串的指针;
  4) UType:显示在对话框上的小图标的类型。

  使用这个函数要包含windows.h头文件。

  调试一下,怎么样?窗口上弹出了一个“第一个Windows C程序”对话框,上面有一行字:“Hello,World!”。

  世界真的很美好啊!!

  深入编程:

  在C语言中,函数的声明,如果没有指明返回值类型,缺省值为void,这个程序的主函数就没有返回值。不过,在Windows编程时,我们最好养成个好习惯,指明函数的返回值类型,因为在C++中,函数返回值类型是不可以缺省的。而我们在Windows C编程时,还是会用到C++的一些概念,这样做,有利于以后深入地学习。

  规范一点的程序应该是这样的:

#include<windows.h>
int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
LPSTR lpCmdLine,int nCmdShow)
{
MessageBox(NULL,"Hello,World!","第一个Windows C程序",MB_OK|MB_ICONASTERISK);
return 0;
}

  这里,我们声明的类型为int型,并且返回一个值0,这样的函数就可以使用在复杂一点的函数调用中了。

  在这一节中,我们有几处都提到了句柄的概念,句柄和指针的概念不同,它是作为操作系统内部索引表中的一个值来使用的,这样可以防止应用程序直接访问名对象的内部结构,体现了Windows资源管理的优越性。譬如说,一个窗口找开之后,好对应内存中的一个内存块,这个窗口所在的内存快地址往往会由操作系统做动态的调整,但其却不会随之变化。不过,通过它可以访问这个窗口,所以在使用的时候,可以把它当做指针一样看待。
  二、 获取本地计算机的主机名和IP地址

  和C语言一样,函数是Windows C编程的最基本的单位。不过,Windows C主要使用API函数,而网络编程则主要使用Winsock提供的API函数。

  Winsock是90年代初,为了方便网络编程,由Microsoft联合了其他几家公司共同制定的一套WINDOWS下的网络编程接口,它是通过C语言的动态链接库方式提供给用户及软件开发者的,主要由winsock.h头文件和动态链接库winsock.dll组成,目前有两个版本:Winsock1.1和Winsock2.0。

  在Win32平台上,访问众多的基层网络协议,Winsock是首选接口。

  用Visual C++6.0编译Windows C程序,使用Winsock API函数时,首先要把wsock32.lib添加到它的库模块中,否刚在链接的时候,会出现“error LNK2001”错误。添加wsock32.lib的具体步骤是:打开工程菜单,选择设置,在弹出的Project settings对话框中,点击link选项卡,然后在对象/库模块文本框中添加wsock32.lib。

  最简单的网络编程是获取本机的主机名和IP地址,这个程序使用了WSAStart()、WSAClenaup()、gethostname()、gethostbyname()四个winsock API函数,这四个函数的功能和使用方法介绍如下:

  1. WSAStartup():

  【函数原型】

int PASCAL FAR WSAStartup(WORD wVersionRequired, LPWSADATA lpWSAData);

  【使用说明】

  每一个使用winsock的应用程序,都必须进行WSAStart函数调用,并且只有在调用成功之后才能使用其它的winsock网络操作函数。

  WVersionRequired:<输入>表示欲使用的Winsock版本,这是一个WORD类型的整数,它的高位字节定义的是次版本号,低位字节定义的是主版本号。

  LpWSAData:<输出>是一个指向WSADATA资料的指针。这个资料我们一般不使用。

  返回值:调用成功返回0;否则,返回出错信息。

  2. WSAClenaup():

  【函数原型】

int PASCAL FAR WSACleanup(void);

  【使用说明】

  winsock使用后,要调用WSACleanup函数关闭网络设备,以便释放其占用的资源。

  3.gethostname()

  【函数原型】

int PASCAL FAR gethostname (char FAR * name, int namelen);

  【使用说明】

  该函数可以获取本地主机的主机名,其中:

  name:<输出>用于指向所获取的主机名的缓冲区的指针。

  Namelen:<输入>缓冲区的大小,以字节为单位。

  返回值:若无错误,返回0;否则,返回错误代吗。

  4.gethostbyname()

  【函数原型】

struct hostent FAR * PASCAL FAR gethostbyname(const char FAR * name);

  【使用说明】

  该函数可以从主机名数据库中得到对应的“主机”。

  该函数唯一的参数name就是前面调用函数gethostname()得到的主机名。若无错误,刚返回一个指向hostent结构的批针,它可以标识一个“主机”列表。

  Hostent结构定义如下:

Struct hostent
{
char FAR * h_name;
char FAR FAR ** h_aliases;
short h_addrtype;
char FAR FAR ** h_addr_list;
}

  其中:

  h_name:<输入>主机名地址(PC)。
  h_aliases:一个由主机备用名组成的空中止数组。
  H_addrtype:返回地址的类型,对于Winsock,这个域总是PF_INET。
  H_lenth:每个地址的长度(字节数),对应于PF_INET域应该为4。
  H_addr_list:应该以空指针结尾的主机地址的列表,返回的地址是以网络顺序排列的。

  其中,h_addr_list[0]存放的就是本地主机的4个字节的IP地址,即:

  h_addr_list[0][0].h_addr_list[0][1].h_addr_list[0][2].h_addr_list[0][3]

  一个简单的用消息框显示主机名和IP地址的源程序如下:

#include<winsock.h>

int WSA_return;
WSADATA WSAData;

HOSTENT *host_entry;
char host_name[256];
char host_address[256];

int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
LPSTR lpCmdLine,int nCmdShow)
{
 WSA_return=WSAStartup(0×0101,&WSAData);

 if(WSA_return==0)
 {
  gethostname(host_name,256);
  host_entry=gethostbyname(host_name);
  if(host_entry!=0)
  {
   wsprintf(host_address,"%d.%d.%d.%d",
    (host_entry->h_addr_list[0][0]&0×00ff),
    (host_entry->h_addr_list[0][1]&0×00ff),
    (host_entry->h_addr_list[0][2]&0×00ff),
    (host_entry->h_addr_list[0][3]&0×00ff));

   MessageBox(NULL,host_address,host_name,MB_OK);
  }
 }
 WSACleanup();
 return 0;
}


  深入编程:

  前面显示IP地址的时候,我们使用的是消息框,规范一点的编程应该使用对话框,如何编辑一个对话框,很多书中都有介绍,编辑的对话框可参考图5的运行界面。

  头文件Get_IP.h如下:

BOOL APIENTRY Hostname_ipDlgPro(HWND hDlg,UINT message,WPARAM wParam,LPARAM lParam);


  这个程序只使用了一个对话框过程,一般把这个过程的声明放在头文件中。

  源程序Get_IP.c:

#include<winsock2.h>
#include"Get_IP.h"
#include"resource.h" //这个头文件在创建资源的时候会自动生成,
//并会在插入资源时自动生成控件标识号.
int WSA_return;
WSADATA WSAData;

HOSTENT *host_entry;
char host_name[256];
char host_address[256];

int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
LPSTR lpCmdLine,int nCmdShow)
{
 WSA_return=WSAStartup(0×0101,&WSAData);
 if(WSA_return==0)
 {
  gethostname(host_name,256);
  host_entry=gethostbyname(host_name);
  if(host_entry!=0)
  {
   wsprintf(host_address,"%d.%d.%d.%d",
    (host_entry->h_addr_list[0][0]&0×00ff),
    (host_entry->h_addr_list[0][1]&0×00ff),
    (host_entry->h_addr_list[0][2]&0×00ff),
    (host_entry->h_addr_list[0][3]&0×00ff));
  }
 }
 WSACleanup();
 DialogBox(hInstance,"DIALOG1",NULL,(DLGPROC)Hostname_ipDlgPro);
 return 0;
}

BOOL APIENTRY Hostname_ipDlgPro(HWND hDlg,UINT message,
WPARAM wParam,LPARAM lParam)
{
 switch(message)
 {
  case WM_INITDIALOG:
   return(TRUE);
  case WM_COMMAND:
   if(LOWORD(wParam)==IDOK)
   {
    SetDlgItemText(hDlg,IDC_EDIT1,host_name);
    SetDlgItemText(hDlg,IDC_EDIT2,host_address);
    SetDlgItemText(hDlg,IDCANCEL,"确定");
   }
   if(LOWORD(wParam)==IDCANCEL)
    EndDialog(hDlg,TRUE);
    return(TRUE);
   break;
 }
 return(FALSE);
}

Posted in C/C++ | Leave a comment

得到19个gmail邀请函,想要gmail么?给我email吧!

得到19个gmail邀请函,现在google出了一款叫做google talk的通讯工具,需要gmail帐号来登陆,如果想尝试一下google talk或者gmail的风采,那就给我email吧!

w1980y@gmail.com

Posted in 心情随笔 | Leave a comment