C语言网络编程基础1.1

来源:互联网 发布:彩票数据api接口 编辑:程序博客网 时间:2024/06/05 04:54

在有了前一章的基础知识后,我想我们该进入下一步的学习了。

一、socket的说明

      socket是使用 Unix 文件描述符 (fiel descriptor) 和其他程序通讯的方式。 Unix 程序在执行任何形式的 I/O 的时候, 程序是在读或者写一个文件描述符。一个文件描述符只是一个和打开的文件相关联的整数。 但是(注意后面的话),这个文件可能是一个网络连接,FIFO,管道,终端,磁盘上的文件 或者什么其他的东西。Unix 中所有的东西文件!因此,你想和 Internet 上别的程序通讯的时候,你将要通过文件描述符。如果它是个文件描述符,那么为什么不用一般的调用 read()write() 来通过套接口通讯?”简单的答案是:“你可以使用一般的函数!”。详细的答案是:“你可以,但是使用 send()recv() 让你更好的控制数据传输。”

二、Internet套接口的两种类型

     一种是 "Stream Sockets",另外一种是 "Datagram Sockets"。我们以后谈到他们的时候也会用到 "SOCK_STREAM" 和 "SOCK_DGRAM"。

     流式套接口是可靠的双向通讯的数据流。如果你向套接口安顺序输出“1,2”,那么他们 将安顺序“1,2”到达另一边。他们也是无错误的传递的,有自己的错误控制。

     数据报也使用 IP 作路由,但是他不选择 TCP。他使用“用户数据报协议 (User Datagram Protocol)”。

三、网络理论知识

     网络分层模型可以分为应用层 (Application) 表示层 (Presentation) 会话层 (Session) 传输层 (Transport) 网络层 (Network) 数据链路层 (Data Link) 物理层 (Physical) 这几个层,其实他们就像穿衣脱衣一样,当你从你主机上向其他主机发送一个消息的时候,就开始不断的穿衣,等在网络传输到其他主机时,又开始脱衣,然后其他主机就解读到了你发送的消息。这里不做太多的讲解。

四、网络编程常用结构体

     第一个结构(TM)--struct sockaddr. 这个数据结构 为许多类型的套接口储存套接口地址信息: struct sockaddr {
        unsigned short    sa_family;    /* address family, AF_xxx          */
        char              sa_data[14];     /* 14 bytes of protocol address   */
    };

sa_family 能够是各种各样的事情,但是在这篇文章中是 "AF_INET"。 sa_data 为套接口储存目标地址和

端口信息。

     为了对付 struct sockaddr,程序员创造了一个并列的结构: struct sockaddr_in ("in" 代表 "Internet".) 

struct sockaddr_in {
        short int          sin_family;      /* Address family                     */
        unsigned short int sin_port;     /* Port number                        */
        struct in_addr     sin_addr;       /* Internet address                  */
        unsigned char      sin_zero[8];  /* Same size as struct sockaddr */
    };

这个数据结构让可以轻松处理套接口地址的基本元素。注意 sin_zero (他 被加入到这个结构,并且长度struct sockaddr 一样) 应该使用函数 bzero() memset() 来全部置零。即使 socket() 想要的是 struct sockaddr *你仍然可以使用 struct sockaddr_in同时,注意 sin_family struct sockaddr 中的 sa_family 一致并能够设置为 "AF_INET"。最后, sin_portsin_addr 必须是网络字节顺序 (Network Byte Order)。

 你也许会反对道:"但是,怎么让整个数据结构 struct in_addr sin_addr 按照网络字节顺序呢?" 要知道这个问题的答案,我们就要仔细的看一 看这个数据结构: struct in_addr, 有这样一个联合 (unions): 

 /* Internet address (a structure for historical reasons) */
    struct in_addr {
        unsigned long s_addr;
    };

曾经是个最坏的联合,但是现在那些日子过去了。如果你声明 "ina" 是 数据结构 struct sockaddr_in 的实例,那么 "ina.sin_addr.s_addr" 就储存4字节的 IP 地址(网络字节顺序)。如果你不幸的 系统使用的还是恐怖的联合 struct in_addr ,你还是可以放心4字 节的 IP 地址是和上面我说的一样(这是因为 #define。)

五、Convert the Natives(本机转换)

     我们又开始学习新的知识,也许听说过不同的主机可能在机器中存储信息的字节顺序是不一样的,这样我们在网络传输中也会遇到这样的问题,那么就需要我们对网络到本机和本机到网络字节顺序的转换。你能够转换两种类型:short(两个字节)和long(四个字节)。如果你想将short从本机字节顺序转换为网络字节顺序,用"h"表示本机("host"),接着是"to",然后用"n"表示网络("nerwork"),最后用"s"表示"short":h-to-n-s或者htons()。当然这样我可以想到还有其他的,如下:

htons()   --"host to network short"

htonl()    --"host to network long"

ntohs()   --"network to host short"

ntohl()    --"network to host long"

现在,你可能认为你已经知道它们了,也许你会想到你的主机上已经是使用了网络字节顺序,没有必要去使用htonl()转换IP地址。这个你没有错,可是程序将要移植到其他主机上去呢,那就可能存在着隐患了。所以记住:在你将数据放到网络上的时候,确信它们是网络字节顺序的。

     最后一点,为什么在数据结构struct sockaddr_in中,sin_addr和sin_port需要转换为网络字节顺序,而sin_family是不是也需要呢?sin_addr和sin_port分别封装在包IP和UDP层,因此它们必须是网络字节顺序。但是sin_family域只是被内核使用来决定在数据结构中包含什么类型的地址,所以它必须是本机字节顺序。同时,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: %sn",a1);
printf("address 2: %sn",a2);
输出如下:
address 1: 132.241.5.10
address 2: 132.241.5.10
假如你需要保存这个IP地址,使用strcopy()函数来指向你自己的字符指针。

原创粉丝点击