C++ Socket 编程_bool socket(int nsockettype=sock_stream, long leve

看看建立一个简单的数据包有多少工作?哎呀,你将不得不使用 “cat” 来建立数据包头!这仅仅是个玩笑。对于流式套接字你要作的是 send() 发 送数据。对于数据报式套接字,你按照你选择的方式封装数据然后使用 sendto()。内核将为你建立传输层和 Internet 层,硬件完成网络访问层。 这就是现代科技。 现在结束我们的网络理论速成班。哦,忘记告诉你关于路由的事情了。 但是我不准备谈它,如果你真的关心,那么参考 IP RFC。

4)结构体

终于谈到编程了。在这章,我将谈到被套接字用到的各种数据类型。 因为它们中的一些内容很重要了。

首先是简单的一个: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”。)

5)本机转换

我们现在到了新的章节。我们曾经讲了很多网络到本机字节顺序的转 换,现在可以实践了! 你能够转换两种类型: 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 没有发送到网络上,它们可以是本机字节顺序。

6)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地址(查阅域名服务,稍 后)。

7)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 帮助。)

8)bind()函数

一旦你有一个套接字,你可能要将套接字和机器上的一定的端口关联 起来。(如果你想用listen()来侦听一定端口的数据,这是必要一步–MUD 告 诉你说用命令 “telnet x.y.z 6969”。)如果你只想用 connect(),

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值