1. 套接字地址结构

1.1 IPv4套接字地址结构

  IPv4套接字地址结构通常也称为“网际套接字地址结构”,它以sockaddr_in命名,定义在<netinet/in.h>头文件中。下边给出它的定义:

   1)struct in_addr专门用来存储IP地址,对于IPv4来说,IP地址为32位无符号整数。其定义如下:

  注:in是internet缩写。

struct in_addr {
unsigned long s_addr;
}

  具体在<netinet/in.h>的定义是这样子的:

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

  2)struct sockaddr_in结构定义如下:

struct sockaddr_in {                     /* in表示Internet */
unsigned short int sin_family; /* Internet地址族 */
unsigned short int sin_port; /* 端口号 */
struct in_addr sin_addr; /* Internet地址 */
char sin_zero[]; /* 填充0(保持与struct sockaddr 一样大小) */
};

  具体在<netinet/in.h>的定义是这样子的:

 /* Structure describing an Internet socket address.  */
struct sockaddr_in
{
__SOCKADDR_COMMON (sin_);
in_port_t sin_port; /* Port number. */
struct in_addr sin_addr; /* Internet address. */ /* Pad to size of `struct sockaddr'. */
unsigned char sin_zero[sizeof (struct sockaddr) -
__SOCKADDR_COMMON_SIZE -
sizeof (in_port_t) -
sizeof (struct in_addr)];
}; /* Notes */
/* bits/sockaddr.h */
typedef unsigned short int sa_family_t; #define __SOCKADDR_COMMON(sa_prefix) \
sa_family_t sa_prefix##family #define __SOCKADDR_COMMON_SIZE (sizeof(unsigned short int))

  其中一个值得注意的是32位IPv4地址存在两种不同的访问方法。举例来说,如果serv定义为某个网际套接字地址结构,那么serv.sin_addr将按in_addr结构引用其中的32为IPv4地址,而serv.sin_addr_s_addr将按in_addr_t(通常是一个无符号的32位整数)引用同一个32位IPv4地址。因此,我们必须正确使用IPv4地址,尤其是将它作为函数的参数时,因为编译器对传递结构和传递整数的处理是完全不一样的。

  sin_addr字段是一个结构,而不仅仅是一个in_addr_t类型的无符号长整数,这是有历史原因的。早期的版本(4.2BSD)把in_addr结构定义为多种结构的联合(union),允许访问一个32位IPv4地址中的所有4个字节,或者访问它的2个16位值。这用在地址被划分成A、B和C三类的时期,便于获取地址中的适当字节。然而随着子网划分技术的来临和无类地址编排的出现,各种地址类正在消失,那个联合已不再需要了。如今大多数系统已经废除了该联合,转而把in_addr定义为仅有一个in_addr_t字段的结构。

  3)通用套接字地址结构

  当作为一个参数传递进任何套接字函数时,套接字地址结构总是以引用形式(也就是以指针指向该结构的指针)来传递。然而以这样的指针作为参数之一的任何套接字函数必须处理来自所支持的任何协议族的套接字地址结构。

  在如何声明所传递指针的数据类型上存在一个问题。有了ANSI C后解决办法很简单:void* 是通用的指针类型。然而套接字函数是在ANSI C之前定义的,在1982年采取的办法是在<sys/socket.h>头文件中定义一个通用的套接字地址结构struct sockaddr,如下所示:

 struct sockaddr {
sa_family_t sa_family; /* 地址族, AF_xxx */
char sa_data[]; /* 14字节的协议地址 */
}

  于是套接字函数定义为以指向某个通用套接字地址结构的一个指针作为其参数之一,这正如bind函数的ANSI C函数原型所示:

 int bind(int, struct sockaddr *, socklen_t);

  这就要求对这些函数的任何调用都必须要将指向特定于协议的套接字地址结构的指针进行类型强制转换(casting),变成指向某个通用套接字地址结构的指针。下边例子展示了这个过程:

  A. 首先,定义一个sockaddr_in结构变量,并将它初始化为0,代码如下:

struct sockaddr_in myad;
memset(&myad, , sizeof(myad));

  B. 然后,给这个结构变量赋值,代码如下:

myad.sin_family = AF_INET;
myad.sin_port = htons();
myad.sin_addr.s_addr = htonl(INADDR_ANY);

  C. 在进行函数调用时,将这个结构强制转换为struct sockaddr类型,代码如下:

bind(serverFd, (struct sockaddr *) &myad, sizeof(myad));

  附:Socket常用数据类型说明:

  

1.2 IPv6套接字地址结构

  IPv6套接字地址结构定义如下:

 struct in6_addr {
uint8_t s6_addr[]; /* 128-bit IPv6 address */
/* 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# */
/* 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 */
}

  具体在<netinet/in.h>头文件定义如下:

 /* IPv6 address */
struct in6_addr
{
union
{
uint8_t __u6_addr8[];
#if defined __USE_MISC || defined __USE_GNU
uint16_t __u6_addr16[];
uint32_t __u6_addr32[];
#endif
} __in6_u;
#define s6_addr __in6_u.__u6_addr8
#if defined __USE_MISC || defined __USE_GNU
# define s6_addr16 __in6_u.__u6_addr16
# define s6_addr32 __in6_u.__u6_addr32
#endif
}; /* Ditto, for IPv6. */
struct sockaddr_in6
{
__SOCKADDR_COMMON (sin6_);
in_port_t sin6_port; /* Transport layer port # */
uint32_t sin6_flowinfo; /* IPv6 flow information */
struct in6_addr sin6_addr; /* IPv6 address */
uint32_t sin6_scope_id; /* IPv6 scope-id */
};

2. 整形字节序转换函数

  为保证“大端”和“小端”字节序的机器之间能相互通信,需在发送多字节整数时,将主机字节序(host byte order)转换成网络字节序(network byte order),或将网络字节序转换为主机字节序。下图说明了网络字节序与小端字节序、大端字节序的对照关系。字节转换主要是针对整形进行转换,字符型由于是单字节,所以不存在这个问题。整形字节序转换函数原型及其说明如下表所示:

  

  注:h: host; n: network; s: short; l: long。

  * 大端和小端 科普 *

  1)小端就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
  2)大端就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。
  举一个例子,比如数字0x12 34 56 78在内存中的表示形式为:

  A. 大端模式:

  低地址 -----------------> 高地址
  0x12  |  0x34  |  0x56  |  0x78

  B. 小端模式:

  低地址 ------------------> 高地址
  0x78  |  0x56  |  0x34  |  0x12

3. IP地址转换函数

  IP地址转换函数是指完成点分十进制IP地址与二进制IP地址之间的相互转换。IP地址转换主要由inet_addr、inet_aton和inet_ntoa这3个函数完成,但这三个函数都只能处理IPv4地址而不能处理IPv6地址。新的函数inet_ptoninet_ntop则同时处理IPv4和IPv6。

  1)inet_pton

  注:pton:presentation to numeric

  inet_pton函数原型如下[将"点分十进制" -> "整数"]

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
int inet_pton(int af, const char *src, void *dst);
//这个函数转换字符串到网络地址,第一个参数af是地址族,转换后存在dst中
//若成功则返回1;若输入不是有效的表达式则返回0;若出错则返回-1。

  inet_pton是inet_addr的扩展,支持的多地址族有下列:
  af = AF_INET
  src为指向字符型的地址,即ASCII的地址的首地址(ddd.ddd.ddd.ddd格式的),函数将该地址转换为in_addr的结构体,并复制在*dst中
  af = AF_INET6
  src为指向IPV6的地址,函数将该地址转换为in6_addr的结构体,并复制在*dst中

  2)inet_ntop

  inet_ntop函数原型如下[将"点分十进制" -> "整数"]

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt);
//这个函数转换网络二进制结构到ASCII类型的地址,参数的作用和上面相同,只是多了一个参数socklen_t cnt,
//它是所指向缓存区dst的大小,避免溢出,如果缓存区太小无法存储地址的值,则返回一个空指针,并将errno置为ENOSPC。
//若成功则返回指向结果的指针,若出错则返回NULL。

  如果以不被支持的地址族作为family参数,这两个函数就都返回一个错误,并将errno设置为EAFNOSUPPORT。

  示例程序:

 #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main (void)
{
char IPdotdec[]; // 存放点分十进制IP地址
struct in_addr s; // IPv4地址结构体
// 输入IP地址
printf("Please input IP address: ");
scanf("%s", &IPdotdec);
// 转换
inet_pton(AF_INET, IPdotdec, (void *)&s);
printf("inet_pton: 0x%x\n", s.s_addr); // 注意得到的字节序
// 反转换
inet_ntop(AF_INET, (void *)&s, IPdotdec, );
printf("inet_ntop: %s\n", IPdotdec);
}

  3)inet_aton

  注:aton:address to network

  

  4)inet_ntoa

  

  5)inet_addr(该函数无法处理255.255.255.255的IP地址,已弃用)

  

参考资料

  《UNIX网络编程 卷1》

  《深入浅出Linux工具与编程》(余国平著)

套接字编程相关函数(1:套接字地址结构、字节序转换、IP地址转换)的更多相关文章

  1. 【网络编程一】主机字节序与网络字节序以及ip地址转换函数

    在计算机设计之初,对内存中数据的处理也有不同的方式,(低位数据存储在低位地址处或者高位数据存储在低位地址处),然而,在通信的过程中(ISO/OSI模型和TCP/IP四层模型中),数据被一步步封装(然后 ...

  2. 套接字编程相关函数(2:TCP套接字编程相关函数)

    本文摘录自<UNIX网络编程 卷1>. 基本套接字函数 socket函数 为了执行网络I/O,一个进程必须做的第一件事就是调用socket函数,指定期望的通信协议类型.其定义如下: #in ...

  3. 网络编程懒人入门(九):通俗讲解,有了IP地址,为何还要用MAC地址?

    1.前言 标题虽然是为了解释有了 IP 地址,为什么还要用 MAC 地址,但是本文的重点在于理解为什么要有 IP 这样的东西.本文对读者的定位是知道 MAC 地址是什么,IP 地址是什么. (本文同步 ...

  4. 套接字编程,创建套接字socket

    1.套接字地址结构: struct sockaddr { sa_family_t sa_family; char sa_data[14]; }; 其中,成员sa_family表示套接字的协议族类型,对 ...

  5. socket编程相关的结构体和字节序转换、IP、PORT转换函数

    注意:结构体之间不能直接进行强制转换, 必须先转换成指针类型才可以进行结构体间的类型转换, 这里需要明确的定义就是什么才叫强制转换. 强制转换是将内存中一段代码以另一种不同类型的方式进行解读, 因此转 ...

  6. 套接字编程(VC_Win32)

    简介(源于维基) Berkeley套接字(也作BSD套接字应用程序接口)刚开始是4.2BSD Unix操作系统(于1983发布)的一套应用程序接口.然而,由于AT&T的专利保护着UNIX,所以 ...

  7. 【unix网络编程第三版】阅读笔记(二):套接字编程简介

    unp第二章主要将了TCP和UDP的简介,这些在<TCP/IP详解>和<计算机网络>等书中有很多细致的讲解,可以参考本人的这篇博客[计算机网络 第五版]阅读笔记之五:运输层,这 ...

  8. 【Unix网络编程】chapter3套接字编程简介

    chapter3套接字编程简介3.1 概述 地址转换函数在地址的文本表达和他们存放在套接字地址结构中的二进制值之间进行转换.多数现存的IPv4代码使用inet_addr和inet_ntoa这两个函数, ...

  9. 【Unix网络编程】chapter3 套接字编程简介

    chapter3套接字编程简介3.1 概述 地址转换函数在地址的文本表达和他们存放在套接字地址结构中的二进制值之间进行转换.多数现存的IPv4代码使用inet_addr和inet_ntoa这两个函数, ...

随机推荐

  1. bash shell while语法

    在编写脚本时,一定要注意空格 基本语法: while [ condition ] do command1 command2 command3 done condition为true时命令1到命令3将会 ...

  2. Java基本语法-----java数组(一维数组二维数组)

    嘿嘿!你们懂的,又是图片,委屈大家了. java数组(一维数组二维数组) [正在看本人博客的这位童鞋,我看你气度不凡,谈吐间隐隐有王者之气,日后必有一番作为!下面有个"顶"字,你就 ...

  3. qq侧滑

    上一篇博客带大家实现了:Android 自定义控件打造史上最简单的侧滑菜单 ,有兄弟看了以后说,你这滑动菜单过时了呀~QQ5.0的效果还不错~~嗯,的确,上一篇也承诺过,稍微修改上一篇的代码,实现QQ ...

  4. Cocos2D实现上下滚动式状态窗口

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 有时候要显示的内容太多,我们无法在iOS设备的小屏幕上显示出来 ...

  5. malloc、calloc、relloc

    1.malloc void * malloc(size_t _Size); malloc函数在堆中分配参数_Size指定大小的内存,单位:字节,函数返回void *指针. 2.calloc void ...

  6. 深入剖析Tomcat类加载机制

    1JVM类加载机制 JVM的ClassLoader通过Parent属性定义父子关系,可以形成树状结构.其中引导类.扩展类.系统类三个加载器是JVM内置的. 它们的作用分别是: 1)引导类加载器:使用n ...

  7. Docker教程:Docker镜像导出及迁移

    http://blog.csdn.net/pipisorry/article/details/51330126 Docker目录分析 安装docker时,默认的安装位置是/var/lib/docker ...

  8. Java并发——线程池原理

    "池"技术对我们来说是非常熟悉的一个概念,它的引入是为了在某些场景下提高系统某些关键节点性能,最典型的例子就是数据库连接池,JDBC是一种服务供应接口(SPI),具体的数据库连接实 ...

  9. 可视化分析工具Cytoscape使用记录

    最近项目要使用到可视化分析工具Cytoscape,所以会花费很多的时间跟精力来整理Cytoscape软件使用和开发的相关资料,希望写下的文章能减少有兴趣的同行学习跟开发所走的弯路时间.同时也是因为百度 ...

  10. 【图的DFS】图的DFS非递归算法

    在DFS的递归算法中,DFS框架如下: 1访问起点v0 2依次以v0的未访问的连接点为起点,DFS搜索图,直至图中所有与v0路径相通的顶点都被访问. 3若该图为非连通图,则图中一定还存在未被访问的顶点 ...