基本结构

(这部分的地址均为网络地址<网络字节序>)



1、struct sockaddr:通用套接字地址结构

此结构用于存储通用套接字地址。

数据结构定义:

typedef unsigned short  sa_family_t;
struct sockaddr {
sa_family_t sa_family; /* address family, AF_xxx */
char sa_data[14]; /* 14 bytes of protocol address */
};

   sa_family:实际使用地址——依据不同的协议族,採用不同的地址类,最经常使用的三种:

1)本地地址族:AF_UNIX或AF_LOCAL;

#include <sys/un.h>

struct sockaddr_un {
sa_family_t sun_family; /*AF_UNIX*/
char sun_path[108]; /*地址数据*/
};

2)网络地址族:AF_INET;

#include <netinet/in.h>

struct sockaddr_in {
sa_family_t sin_family; /* AF_INET */
uint16_t sin_port; /*端口号*/
struct in_addr sin_addr; /*Internet地址*/
unsigned char sin_zero[8] /*占位字节*/
};

3)红外地址类:AF_IRDA;

#include <sys/types.h>

struct sockaddr_irda {
sa_family_t sir_family; /* AF_IRDA */
u_int8_t sir_lsap_sel; /* LSAP selector */
u_int32_t sir_addr; /* Device address */
char sir_name[25]; /*:IrDA:TinyTP ,OBEX,etc.*/
};

  sa_data[]:包括远程主机的地址、port号和套接字的数目,这些信息包括于字符串中。

而在实际应用中,我们常常使用AF_INET。为了处理struct sockaddr,Unix建立了另外一个相似结构struct sockaddr_in。

其结构例如以下所看到的。

struct sockaddr_in {
sa_family_t sin_family; /* AF_INET */
uint16_t sin_port; /*端口号*/
struct in_addr sin_addr; /*Internet地址*/
unsigned char sin_zero[8] /*占位字节*/
};

此结构提供了简洁的方法用于訪问socket address(struct sockaddr)结构中的每个元素。

sin_zero[8]是为了使两个结构在内存中具有同样的尺寸,使用sockaddr_in时要把sin_zero[]所有设置为零值(使用bzero()函数或memset()函数)。

此结构能够觉得是IPv4套接字地址结构。

2、struct in_addr:因特网地址结构

该结构用于表示Internet Address(因特网地址)。

/*Internet address*/
typedef uint32_t in_addr_t;
struct in_addr
{
in_addr_t s_addr;
};

在这里。我们由s_addr的类型定义可知,此处的因特网地址长度为32bit。也就是说此处的地址为IPv4地址。同一时候,当使用struct sockaddr_in类型的ina变量时,ina.sin_addr.s_addr即为32bit的IP地址。

当然,此处,我们应该注意这里的IP地址是网络字节序。

3、IPv6套接字地址结构

在上文。我们介绍了IPv4套接字地址结构,那么。对于IPv6套接字,其地址结构又怎样呢?

IPv6套接字地址结构在<netinet/in.h>头文件里定义:

struct in6_addr{
uint8_t s6_addr[16]; /*128bits IPv6 address network byte ordered*/
}; struct sockaddr_in6{
sa_family_t sin6_family; /*AF_INET6 */
in_port_t sin6_port; /*transport layer port network byte ordered */
uint32_t sin6_flowinfo; /*flow information, undefined*/
struct in6_addr sin6_addr; /*IPv6 address network byte ordered */
uint32_t sin6_scope_id; /*set of interfaces for a scope*/
};

对于IPv6套接字地址结构,我们要注意这样几点:

*IPv6的地址族(sa_family)是AF_INET6,而IPv4的地址族是AF_INET;

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

*sin6_flowinfo字段分为两个字段:低序20位流标(flow table) + 高序12位保留。

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

4、struct sockaddr_storage:新的通用套接字地址结构

struct sockaddr_storage{
uint8_t ss_len; //length of the struct
sa_family_t ss_family; //address family: AF_xxx value
};

在上文。我们能够知道struct sockaddr为通用套接字地址结构,可是因为因特网地址结构的限制,该结构仅支持IPv4地址。

而新的通用套接字地址结构struct sockaddr_storage克服了现有struct sockaddr的一些缺点。足以容纳系统所支持的不论什么不论什么套接字地址结构。

sockaddr_storage类型提供的通用套接字地址结构相比sockaddr存在下面两点区别:

(1)、假设系统支持的不论什么套接字地址结构有对齐须要。那么sockaddr_storage可以满足最苛刻的对齐要求。

(2)、sockaddr_storage足够大,可以容纳系统支持的不论什么套接字地址结构。

5、套接字地址结构的比較:

基本转换函数

1、值-结果參数

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

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

struct sockaddr_in serv;

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

既然指针和指针所指内容的大小都传递给了内核,内核就知道究竟须要从进程复制多少数据。

下图展示了这个情形。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvemhhb2JyeWFudA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

(2)、从内核到进程传递套接字地址结构的函数有4个:accept、recvfrom、getsockname和getpeername。这四个函数的当中两个參数是指向某个套接字地址结构的指针和指向表示该结构大小的整数变量的指针。比如:

struct sockaddr_un cli; /*Unix domain*/

socklen_t len;
len = sizeof(cli);
getpeername(unixfd, (struct sockaddr *)&cli, &len);

把套接字地址结构大小这一參数从一个整数改为指向某个整数变量的指针,其原因在于:

当函数被调用时,结构大小是一个值(value)。它告诉内核该结构的大小,这样内核在写该结构时不至于越界。当函数返回时,结构大小又是一个结果(result)。它告诉进程内核在该结构中到底存储了多少信息。这样的类型的參数称为“值-结果(value-result)”參数。下图展示了这个情形。

关于值-结果參数。在后面介绍套接字调用函数时会进一步解析。

2、字节排序函数

IP地址的三种表示格式:

1)ASCII(点分十进制字符串)

2)网络地址(网络字节序,Network Byte Order)

3)主机地址(主机字节序。Host Byte Order)

那么。网络字节序和主机字节序有何不同?

内存在存储数据时有两种处理方法:一种是将低序字节存储在起始地址,此称为小端(little-endian)字节序。还有一种是将高序字节存储在起始地址,此称为大端(big-endian)字节序。

採用大端方式进行数据存放符合人类的正常思维。而採用小端方式进行数据存放利于计算机处理。

对于2个字节的数据存储。大端存储与小端存储的差别例如以下图:

在套接字地址表示方面。网络地址(网络字节序)一般採用大端字节序。而主机地址(主机字节序)主要取决于系统。

那么主机地址和网络地址(或者说主机字节序和网络字节序)各自主要应用于什么场合?

主机地址主要用于主机处理时,由于计算机更加擅好处理小端字节序(对採用小端存储的主机)。而在网络协议中,发送协议栈和接受协议栈必须就多字节字段的各个字节的传送顺序达成一致,为了便于人类思维过程,一般採用大端字节序,即网络字节序。

主机字节序和网络字节序之间相互转化的过程即为字节排序的过程。这两种字节序之间的转换使用下面4个函数:

#include <netinet/in.h>

/*均返回网络字节序的值*/
uint16_t htons(uint16_t host16bitvalue);
uint32_t htonl(uint32_t host32bitvalue); /*均返回主机字节序的值*/
uint16_t ntonhs(uint16_t net16bitvalue);
uint32_t ntonhl(uint32_t net32bitvalue);

在这些函数的名字中,h代表host,n代表network。s代表short,l代表long。

现在,我们把s视为16位的值(比如TCP/UDPport号),把l视为32位的值(比如IPv4地址)。

这些函数在处理时。会依据主机系统究竟是小端存储还是大端存储来相应地调整函数的处理过程。假设主机系统支持小端存储。则相应实现字节反转过程,反之,则这些函数为空宏。

在什么时候应该使用这些函数呢?当我们存在内核与进程之间的套接字地址结构訪问时就必须使用对应的字节转换函数来实现地址的正常传递。

3、字节操纵函数

操纵多字节字段的函数有两组(Berkeley&ANSIC)。它们既不正确数据作解释。也不如果数据是以空字符结束的C字符串。

名字以b(表示字节)开头的第一组函数起源于4.2BSD,现今支持套接字函数的系统仍然提供它们。名字以mem(表示内存)开头的第二组函数起源于ANSIC标准,支持ANSIC函数库的全部系统都提供它们。

Berkeley:

#include <string.h>

void bero(void *dest, size_t, nbytes);
void bcopy(const void *src, void *dest, size_t nbytes);
int bcmp(const void *ptr1, const void *ptr2, size_t nbytes);
/*相等返回0,否则返回非0值*/
/*此处使用const限定词,表示所限制的指针所指的内容不会被函数更改。换句话说。函数仅仅是读而不改动由const指针所指的内存单元。*/

ANSIC:

#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。或小于0*/

4、地址转换函数

地址转换函数实现ASCII字符串(点分十进制)与网络字节序的二进制(存放在套接字地址结构中的值)之间的网际地址的转换。

   地址转换函数有两组,各自是:

(1)、inet_aton、inet_addr和inet_ntoa函数

完毕点分十进制(如”192.168.1.1”)与它长度为32位的网络字节序二进制间转换IPv4地址。

#include <arpa/inet.h>

int inet_aton(const char *strptr, struct in_addr *addrptr);
/*
此函数将strptr所指的C字符串转换成一个32位的网络字节序二进制,并通过指针addrptr来存储。 若成功。返回1,否则返回0。
*/ in_addr_t inet_addr(const char *strptr);
/*
此函数与inet_aton函数运行同样的操作,返回32位的网络字节序二进制值,否则返回INADDR_NONE。注意,此函数不能处理255.255.255.255点分十进制数串。
*/ char *inet_ntoa(struct in_addr inaddr);
/*
此函数将一个32位的网络字节序二进制IPv4地址转换成对应的点分十进制数串。
由该函数的返回值所指向的字符串驻留在静态内存中。 另外。该函数是以一个结构而不是以指向该结构的一个指针作为參数。
*/

注:inet_addr()函数已被废弃,建议採用inet_aton()函数。

(2)、inet_pton和inet_ntop函数

这两个函数式随IPv6出现的新函数,对于IPv4和IPv6地址均适用。

函数名中的p代表表达(presentation)和数值(numeric)。地址的表达格式一般是ASCII字符串。数值格式则是存放到套接字地址结构中的二进制值。

#include <arpa/inet.h>

int inet_pton(int family, const char *strptr, void *addrptr);
/*
此函数转换由strptr指针所指的字符串。并通过addrptr指针存放二进制结果。 若成功,则返回值为1,否则返回0。 */ const char *inet_ntop(int family, const void *addrptr, size_t len);
/*
此函数运行从数值格式(addrptr)到表达格式(strptr)的转换。
strptr參数不能是空指针,调用者必须为目标存储单元分配内存并指定大小。 调用成功时,返回此指针。
*/

对于这两个函数,family字段能够是AF_INET或AF_INET6。

假设以不被支持的地址族作为family參数,这两个函数会返回错误,并将errno置为EAFNOSUPPORT。

另外,在inet_ntop()函数中,len參数是目标存储单元的大小。以免该函数溢出其调用者的缓冲区。

为了有助于指定此大小,在<netinet/in.h>头文件里有例如以下定义:

#define INET_ADDRSTRLEN 16 /*IPv4 dotted-decimal*/
#define INET6_ADDRSTRLEN 46 /*IPv6 hex-string*/

假设len太小。不足以容纳表达格式结果,那么返回空指针。并置errno为ENOSPC。

Unix网络编程 之 socket基础的更多相关文章

  1. Unix网络编程(1)——socket一窥

    套接口地址结构 IPv4的套接口地址结构为: struct sockaddr_in { uint8_t sin_len; sa_family_t sin_family; struct in_addr ...

  2. UNIX网络编程——关于socket阻塞与非阻塞情况下的recv、send、read、write返回值

    1.阻塞模式与非阻塞模式下recv的返回值各代表什么意思?有没有 区别?(就我目前了解阻塞与非阻塞recv返回值没有区分,都是 <0:出错,=0:连接关闭,>0接收到数据大小,特别:返回 ...

  3. 【Linux/unix网络编程】之使用socket进行TCP编程

    实验一 TCP数据发送与接收 [实验目的] 1.熟练掌握套接字函数的使用方法. 2.应用套接字函数完成基本TCP通讯,实现服务器与客户端的信息交互. [实验学时] 4学时 [实验内容] 实现一个服务器 ...

  4. java基础-网络编程(Socket)技术选型入门之NIO技术

    java基础-网络编程(Socket)技术选型入门之NIO技术 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.传统的网络编程 1>.编写socket通信的MyServer ...

  5. 记录一次配置unix网络编程环境的过程和遇到的问题

    记录一次搭建unix网络编程环境过程中遇到的问题和总结 计算机环境虚拟机 linuxmint-18-xfce-64bit 1.打开unix网络编程.iso 把目录下的文件复制到某一目录,修改权限,可命 ...

  6. iOS 网络编程:socket

    @import url(http://i.cnblogs.com/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/c ...

  7. UNIX网络编程——select函数的并发限制和 poll 函数应用举例

    一.用select实现的并发服务器,能达到的并发数,受两方面限制 1.一个进程能打开的最大文件描述符限制.这可以通过调整内核参数.可以通过ulimit -n来调整或者使用setrlimit函数设置,  ...

  8. unix网络编程 str_cli epoll 非阻塞版本

    unix网络编程 str_cli epoll 非阻塞版本 unix网络编程str_cli使用epoll实现讲了使用epoll配合阻塞io来实现str_cli,这个版本是配合非阻塞io. 可以看到采用非 ...

  9. 《Unix 网络编程》05:TCP C/S 程序示例

    TCP客户/服务器程序示例 系列文章导航:<Unix 网络编程>笔记 目标 ECHO-Application 结构如下: graph LR; A[标准输入/输出] --fgets--> ...

随机推荐

  1. 判断Exe(DLL)和符号文件是否匹配---验证模块和符号文件是否匹配的工具和方法

    当我们进行程序调试时,有时调试器会直接告诉你符号文件不对,或则显示出的调用栈不对,当你怀疑符号文件不匹配时,如何确定呢? 如果是用windbg调试,请用 !chksym 模块名比如,匹配的时候  不匹 ...

  2. mysql创建中文编码库

    [2013/5/27 16:06:08] LuoXingchen: Create MySQL Database with Chinese supported:        mysql> cre ...

  3. 雷林鹏分享:PHP 错误处理

    在 PHP 中,默认的错误处理很简单.一条错误消息会被发送到浏览器,这条消息带有文件名.行号以及描述错误的消息. PHP 错误处理 在创建脚本和 Web 应用程序时,错误处理是一个重要的部分.如果您的 ...

  4. SQL SERVER系统表和常用函数介绍

    sysaltfiles 主数据库 保存数据库的文件 syscharsets 主数据库 字符集与排序顺序sysconfigures 主数据库 配置选项syscurconfigs 主数据库 当前配置选项s ...

  5. live555简介

    live555 编辑   目录 1live555简介 2Live555 Streaming Media整体框架 3openRTSP客户端流程     1live555简介编辑 Live555 是一个为 ...

  6. popToViewController

    看到群里有人问popToViewController的用法 就写了下了 希望能帮到有需要的人 [self.navigationController popToViewController:[self. ...

  7. css float属性详解

    定义和用法 float 属性定义元素在哪个方向浮动.以往这个属性总应用于图像,使文本围绕在图像周围,不过在 CSS 中,任何元素都可以浮动.浮动元素会生成一个块级框,而不论它本身是何种元素.如果浮动非 ...

  8. js中sync、defer、async的区别

    <script src="script.js"></script> 没有 defer 或 async,浏览器会默认为同步sync,会立即加载并执行指定的脚本 ...

  9. linux中PHP安装扩展包(mongodb为例)

    相对于windows中的PHP扩展,只需要在下载相应的dll资源,并且添加配置在php.ini之后即可. 但是在linux安装扩展时,需要进行编译安装. 这里以lnmp一键安装包为例(php.ini位 ...

  10. sed输出指定行

    and line ,8p to line ,8p -e 20p - and line -n:取消默认输出.注意:sed命令会默认把输入行打印到屏幕上,所以如果想精准的控制输出,就需要-n. -e:进行 ...