《UNIX网络编程 》学习笔记 (三)

来源:互联网 发布:阿里云crm 编辑:程序博客网 时间:2024/06/05 15:37
第三章  套接字编程简介

 3.1 概述
        这一章主要讲解套接字编程所用到的API和套接字地址结构。
 
3.2 套接字地址结构
       3.2.1 IPv4套接字地址结构
         (1)IPv4套接字地址结构通常也称为“网际套接字地址结构”,它以sockaddr_in 命名,在 <netinet/in>头文件中定义.
         (2)IPv4套接字地址结构如下: 
struct in_addr{
in_addr_t    s_addr;        /* 32-bit IPv4 address */
      /* network byte ordered */
};
struct sockaddr_in{
uint_t                sin_len;              /* length of struct ure (16) */
sa_family_t       sin_family;          /* AF_INET */
in_port_t           sin_port;             /* 16-bit TCP or UDP port number */
struct in_addr    sin_addr;            /* 32-bit IPv4 address */
char                  sin_zero[8];        /* unused */
};

         (3)对IPv4套接字地址结构的几点说明:

           sin_len:为了增加对OSI协议的支持而随4.3BSD-Reno添加的。在这之前sin_family是第一个字段,不是所有的系统都支持长度字段,POSIX 规范不要求这个字段。sin_len字段简化了长度可变套接字地址结构的处理。除 非涉及路由套接字,否则不需要设置sin_len字段。

 sin_family:套接字地址结构的地址族。用来标识是IPv4或者IPv6抑或其他类型。必须设置。

 sin_port: 套接字所要使用的端口号。必须设置

 sin_addr:套接字所要用到的32位的IPv4地址。必须设置

 sin_zero:未使用,捆绑非通配地址时必须置零

(4)POSIX定义的数据类型:

数据类型                   说明                                            头文件

int8_t                        带符号的8位整数                          <sys/types.h>

uint8_t                      无符号的8位整数                          <sys/types.h>

int16_t                      带符号的16位整数                        <sys/types.h>

uint16_t                    无符号的16位整数                        <sys/types.h>

int32_t                      带符号的32位整数                        <sys/types.h>

uint32_t                    无符号的32位整数                        <sys/types.h>             

sa_familu_t              套接字地址结构的地址族               <sys/socket.h>

socklen_t                 套接字地址结构的长度(uint32_t)     <sys/socket.h>

in_addr_t                 IPv4地址(uint32_t)                         <netinet/in.h>

in_port_t                  TCP或UDP端口号(uint16_t)            <netinet/in.h>

(5)IPv4地址和TCP或UDP端口号在套接字地址结构中是以网络字节序存放的。

(6)IPv4地址存在2种访问方法:

((1))按结构方式引用IPv4 地址 struct.sin_addr

          ((2))按32位数值方式引用IPv4地址 struct.sin_addr.s_addr

(7)套接字地址结构仅在本机使用,结构本身不在网络上传递。

3.2.2 通用套接字地址结构

(1)存在通用套接字地址结构的原因是:在调用任何需要套接字地址结构做为参数的函数时,套接字地址结构总是以引用的方式(指针)传递的。不同的协议有不同的套接字地址结构,函数的参数怎么声明这些套接字地址结构的指针类型是一个问题,于是就定义了一个通用套接字地址结构,所有需要套接字地址结构做参数的函数的这个形参都被声明为指向这个通用套接字地址结构的指针的类型。其他套接字地址结构的指针被强制转换为通用套接字地址结构的指针类型。(ANSI C 定义了 void * 来解决这个问题)

(2)通用套接字地址结构定义在<sys/socket.h>:

struct sockaddr {

uint8_t            sa_len;

sa_family_t     sa_family;

char                sa_data[14];

                    };

3.2.3 IPv6套接字地址结构

(1)IPv6套接字地址结构在<netinet/in.h>中定义。

(2)IPv6套接字地址结构:

struct in6_addr {

uint8_t        s6_addr[16];

          };

#define SIN6_LEN 

          struct sockaddr_in6 {

uint8_t               sin6_len;

sa_family_t        AF_INET6;

in_port_t            sin6_port;

uint32_t             sin6_flowinfo;

struct in6_addr   sin6_addr;

uint32_t             sin6_scope_id;

};

关于IPv6套接字地址结构的说明:

((1))如果当前系统支持套接字地址结构中的长度字段,则SIN6_LEN常值必须定义。

          ((2))IPv6的地址族是AF_INET6 而IPv4 是AF_INET.

3.2.4 新的通用套接字地址结构

(1)新的通用套接字地址结构定义在<netinet/in.h>中

(2)新的通用套接字地址结构:

struct sockaddr_storage {

uint8_t            ss_len;

sa_family_t     ss_family;

/* 其它字段对用户是透明的*/

};

(3)对新的通用套接字地址结构的几点说明:

((1))新的通用套接字地址结构满足对齐要求

((2))新的通用套接字地址结构足够大,能够容纳系统支持的任何套接字地址结构。

          ((3))sockaddr_storage 结构必须类型强制转换成或复制到适合于ss_family字段所给出地址类型的套接字地址结构中,才能访问其他字段。

3.4字节排序函数

(1)大端字节序(big-endian)将一个16位数的高位字节存储在起始地址称为大端字节序。

(2)小端字节序(little-endian)将一个16位数的低位字节存储在起始地址称为小端字节序。

(3)网络字节序就是大端字节序。

(4)在网络字节序和主机字节序之间转换的函数,它们在<netinet/in.h>中声明

uint16_t    htons(uint16_t  host16bitvalue); 

uint32_t    htonl(uint32_t   host32bitvalue); 

uint16_t    ntohs(uint16_t  net16bitvalue);

uint32_t    ntohl(uint32_t   net32bitvalue);

当使用这些函数时,并不用关心主机字节序和网络字节序的真实值(大端。小端),只要调用相应的函数在主机字节序和网络字节序之间转换某个给定值。在那些与网际协议所用字节序(大端)相同的系统中,这四个函数通常被定义为空宏。

3.5 字节操纵函数

(1)字节操纵函数并不假设数据是以0结尾的C字符串。操纵C字符串的函数在<string,h>中声明,名字以str开头。

(2)Berkeley版本的字节操纵函数声明在<strings.h>中

void  bzero(void dest *, size_t nbytes);

void  bcopy(const void *str, void *dest, size_t nbytes);

int    bcmp(const void *ptr1, const void *ptr2, size_t nbytes); /*相等返回0,不相等返回非0*/

(3)ANSI C版本的字节操纵函数声明在<string.h>中

                    void *memset(void *dest, int c, size_t len);

                    void *memcpy(void *dest, const void *src, size_t nbytes);

                    int     memcmp(const void *ptr1, const void *ptr2, size_t nbytes);  /*相等返回0,否则非0 是大于0还是小于0取决于第一个不等的字节*/

3.6 地址转换函数

(1)地址转换函数在地址的ASCII字符串和网络字节序的二进制值之间进行转换。

(2)地址转换函数在<arpa/inet.h>中进行声明。

(3)只适用于IPv4的地址转换函数

int  inet_aton(cosnt char *strptr, struct in_addr *addrptr);    /* 成功返回1,否则返回0*/

in_addr_r inet_addr(const char *strptr);  (废弃不用)/* 如果参数有效,成功返回32位二进制网络字节序IPv4地址,否则返回INADDR——NONE*/

char *inet_ntoa(struct in_addr  inaddr);  /* 返回值指向一个点分十进制数串,不可重入,*/

(4)同时适合IPv4和IPv6的地址转换函数

int inet_pton(int family, const char *strptr, void *addrstr);  /* 成功返回1,输入不是有效表达式返回0,错误返回-1*/

const char *inet_ntop(int family, const void *addrstr, char *strptr ,size_t len);  /* 成功返回指向结果的指针,失败指针为NULL*/

/* family 参数可以是AF_INET 或者AF_INET6 如果传入不被支持的地址族,函数返回错误,并设置errno 为EAFNOSUPPORT。

   len 参数指明了 strptr 所指向的缓冲区的大小,从而避免缓冲区溢出.如果len参数太小,不能容纳结果,函数返回空指针,并设

   置errno为ENOSPC。

   在<netinet/ih.h>头文件中定义了len的两个常量  IPv4 : #define INET_ADDRSTRLEN 16    

                                                                        IPv6 :#define INET6_ADDRSTRLEN 46 

   void *strptr 参数不能为NULL,调用者必须为这个参数分配空间。调用成功时这个指针就是函数的返回值。

*/



        
.