您的位置:首页 > 理论基础 > 计算机网络

unix网络编程前奏

2015-09-01 08:14 585 查看
一 与网络编程相关的数据类型:

数据类型 说明 头文件

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_family_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>

二 套接字结构:通用套接字地址结构 IPV4套接字地址结构 IPV6套接字地址结构 新的通用套接字地址结构

通用套接字地址结构:
所有的套接字函数都需要传入套接字地址结构参数,这个参数的传入都是以引用(指针)的形式进行传入,但是遇到一个问题就是:任何套接字函数必须处理来自所支持的任何协议族的套接字地址结构,那如何声明所传递指针的数据类型呢?有了ANSI C后办法很简单:声明为void * 类型即可,但套接字函数是在ANSI C前定义的,在1982年采取的办法是定义一个通用的套接字地址结构:

<sys/socket.h>

struct sockaddr {
uint8_t sa_len;
sa_family_t sa_family; /* address family: AF_xxx */
char sa_data[14]; /* 14-bit protocol address */

};

然后将传入的套接字地址结构指针类型声明为通用套接字地址结构即可,其实和声明为void *是一回事,嘿嘿!所以从应用程序开发人员的角度来看,通用套接字地址结构的唯一用途就是对指向特定于协议的套接字地址结构的指针执行类型强制转换。

IPV4套接字地址结构
<netinet/in.h>

struct in_addr {
in_addr_t s_addr; /* 32-bit IPv4 address and network byte ordered */

};

struct sockaddr_in {
uint8_t sin_len; /* length of structure (16) */

sa_family_t sin_family; /* AF_INET */

in_port_t sin_port; /* 16-bit TCP or UDP port number and network byte ordered */

struct in_addr sin_addr; /* 32-bit IPv4 address and network byte ordered */

char sin_zero[8]; /* unused, set zero */
};

说明:

1.长度字段sin_len是为增加对OSI协议的支持而随4.3BSD-Reno添加的,在此之前第一个成员是sin_family。并不是所有的厂家都支持套接字地址结构的长度字段,而且POSIX规范也不要求有这个成员。正是因为有了长度字段,才简化了长度可变套接字地址结构的处理。

2.即使有长度字段,我们也无需设置和检查它,除非涉及路由套接字。它是由处理来自不同协议族的套接字地址结构的例程(例如路由表处理代码)在内核中使用的。

3.sa_family_t可以是任何无符号整数类型,在支持长度字段的实现中,sa_family_t通常是一个8位的无符号整数,而在不支持长度字段的实现中,它则是一个16位的无符号的整数。

4.IPv4地址和TCP或UDP端口号在套接字地址结构中总是以网络字节来存储。

5.sin_addr 字段是一个结构,而不仅仅是一个in_addr_t类型的无符号长整数,这是有历史原因的。在A B C类地址时期把in_addr结构定义为多种结构的联合,然而随着子网划分技术和无分类编址技术的来临,各种地址正在消失,那个联合已不再需要,被废弃了,因此现在in_addr定义为仅有一个in_addr_t字段的结构。

6.sin_zero 一般置0,但按照惯例,我们总是在填写前将整个结构置为0,而不但但是将sin_zero置为0。

7 .套接字地址结构仅在给定主机上使用:虽然结构中的某些字段(例如IP地址和端口号)用在不同主机之间的通信中,但是结构本身并不在主机之间传递。

IPV6套接字地址结构(以下的ipv6地址结构暂时只是照着书上抄下来了,很多地方自己理解的不深,后面大面积开始使用后再往深处研究。)
<netinet/in.h>

struct in6_addr {
uint8_t s6_addr[16]; /* 128-bit IPv6 address and network byte ordered */

};

#define SIN6_LEN /* required for compile-time tests */

struct sockaddr_in6 {
uint8_t sin6_len; /* length of this struct(28) */
sa_family_t sin6_family; /* AF_INET6 */

in_port_t sin6_port; /* transport layer port and network byte ordered */

uint32_t sin6_flowinfo; /* flow information,undefined */

struct in6_addr sin6_addr; /* IPv6 address and network byte ordered */

uint32_t sin6_scope_id; /* set of interfaces for a scope */

};

说明:

1.如果系统支持套接字地址结构中的长度字段,那么SIN6_LEN常值必须定义。

2.结构中字段的先后顺序做过编排,使得如果sockaddr_in6结构本身是64位对齐的,那么128位的sin6_addr字段也是64位对齐的。

3.sin6_flowinfo字段分成两个字段:

低序20位是流标(flow label);

高序12位保留。

4.对于具备范围的地址(scoped address ),sin6_scope_id 字段标识其范围(scope),最常见的是链路局部地址(link-local address ) 的接口索引(interface index)

新的通用套接字地址结构
<netinet/in.h>

struct sockaddr_storage {
uint8_t ss_len;
sa_family ss_family;
}

新的通用套接字地址结构sockaddr_storage相比与原来通用套接字结构sockaddr存在以下两点差别:
(1). 如果系统支持的任何套接字地址结构有对齐的需要,那么sockaddr_storage能够满足最苛刻的对齐要求。

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

注意,除了ss_family和ss_len外(如果有的话),sockaddr_storage结构中的其他字段对用户来说是透明的。sockaddr_storage结构类型必须强制转换成或复制到适合于ss_family字段所给出地址类型的套接字地址结构中,才能访问其他字段。

值-结果参数

当往一个套接字函数传递一个套接字地址结构时,该结构总是以引用形式来传递,也就是说传递的是指向该结构的一个指针。该结构的长度也作为一个参数来传递,但是结构长度参数的传递方式取决于该结构的传递方向:是从进程到内核,还是从内核到进程。

(1). 从进程到内核传递套接字地址结构的函数有3个:bind connect 和 sendto。这些函数的一个参数是指向某个套接字的地址结构的指针,另一个是参数是该结构的整数大小,例如:

struct sockaddr_in serv;

connect ( sockfd, (struct sockaddr*) &serv, sizeof(serv));

(2). 从内核到进程传递套接字地址结构的函数有4个:accept recvfrom getsockname和getpeername。这4个函数的其中两个参数是指向某个套接字地址结构的指针和指向表示该结构大小的整数变量的指针。把套接字地址结构大小这个参数从一个整数改为指向某个整数变量的指针,其原因在于:当函数被调用时,结构大小是一个值,它告诉内核该结构的大小,这样内核在写该结构时不至于越界;当函数返回时,结构大小又是一个结果,它告诉进程在该结构中究竟存储了多少信息。这种类型的参数又称为值-结果参数。下图展示了这个情形:



三 基本转换函数

字节序转换函数

大端和小端的事情如果还不明白的话就请移步我的另一篇博客。下面直接上函数:

#include <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);

均返回:主机字节序的值
说明:
1. 在这些函数的名字中,h代表host,n代表network,s代表short,l代表long。这里s可以视为16位的值,l视为32位的值。

2.当使用这些函数时,我们并不关心主机字节序和网络字节序的真实值(或为大端,或为小端),我们所要做的就是调用适当的函数进行转换即可。

3.由于历史的原因和POSIX规范的规定,套接字地址结构中的某些字段必须按照网络字节序进行维护。

字节操纵函数

#include <strings.h>

void bzero( void *dest, size_t nbytes );

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

int bcmp( const void *ptr1, const viod *ptr2, size_t nbytes );

返回:若相等则为0,否则为非0

说明:

1.这组函数起源于4.2BSD,几乎所有现今支持套接字函数的系统仍然提供他们。

2.bzero把目标字节串中指定数目的字节置为0. bcopy将指定数目的字节从源字节串移到目标字节串。bcmp比较两个任意的字节串,若相同则返回0,否则返回非0.

#include <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

说明:

1.这组函数起源于ANSI C标准,支持ANSI C函数库的所有系统都提供它们。

2.memset把目标字节串指定数目的字节置为值c,memcpy类似bcopy,不过两个指针参数的顺序是相反的,当源字节串与目标字节串重叠时,bcopy能够正确处理,但memcpy的操作结果却不可知,这种情形下必须改用ANSI C的memmove函数。memcmp比较两个任意的字节串,若相同则返回0,否则返回大于0或小于0,这取决于第一个不等的字节。

地址转换函数

点分十进制ASCII字符串与网络字节序的二进制值之间需要转换,为什么捏?因为一个是给人看的,一个是给机器看的。下面给出两组函数,第一组: inet_aton 和 inet_ntoa 是用在IPv4中,另一组比较新:inet_pton 和 inet_ntop,对于IPv4和IPv6地址都适用。

#include <arpa/inet.h>

int inet_aton( const char *strptr, struct in_addr *addrptr )

返回:若字符串有效则为1,否则为0

说明:将strptr所指字符串转换成一个32位的网络字节序二进制值,并通过指针addrptr来存储,若成功则返回1,否则返回0.

char *inet_ntoa( struct in_addr inaddr );

返回:指向一个点分十进制数串的指针

说明:inet_ntoa函数将一个32位的网络字节序二进制IPv4地址转换成响应的点分十进制数串。该函数的返回值所指向的字符串驻留在静态内存中,所以该函数是不可重入的,最后要留意该函数以一个结构而不是以指向该结构的一个指针作为其参数。

#include <arpa/inet.h>

函数前的说明:这两个函数是随IPv6出现的新函数,对于IPv4地址和IPv6地址都适用,是用的最广的一组函数。函数名中的p和n分别代表表达和数值,表达格式通常是ASCII字符串,数值格式则是网络字节序二进制值。这两个函数中的family参数是AF_INET或AF_INET6,如果以不被支持的地址族作为family参数,这两个函数就返回一个错误,并将errno置为EAFNOSUPPORT。

int inet_pton( int family, const char *strptr, void *addrptr );

返回:若成功则为1,若输入不是有效的表达格式则为0,若出错则为-1

说明:尝试转换由strptr指针所指的字符串,并通过addrptr指针存放二进制结果。

const char *inet_ntop( int family, const void *addrptr, char *strptr, size_t len );

返回:若成功则为指向结果的指针,若出错则为NULL

说明:将数值格式(addrptr)转换到表达格式(strptr),strptr参数不可以是一个空指针,调用者必须为目标存储单元分配内存并指定其大小,调用成功时,这个指针就是该函数的返回值。len参数是目标存储单元的大小,以免该函数溢出其调用者的缓冲区。为有助于指定这个大小,在<netinet/in.h>头文件中有如下定义:
#define INET_ADDRSTRLEN 16 /* for IPv4 dotted-decimal */

#define INET6_ADDRSTRLEN 46 /* for IPv6 hex string */
如果len太小,不足以容纳表达格式结果(包括结尾的空字符),那么返回一个空指针,并置errno为ENOSPC。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: