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. Weblogic 12c 负载均衡和session复制

    在上一篇,我们介绍了weblogic集群的部署和session的复制,如何将请求负载均衡到这个三个服务器上呢? 这里提供两种方式:(1)weblogic自带的proxy代理        (2) ng ...

  2. REFRESH删除POSTGRESQL

    sudo apt-get install python-psycopg2sudo apt-get install postgresql sudo su - postgres createuser -- ...

  3. SQL Server 执行计划操作符详解(1)——断言(Assert)

    前言: 很多很多地方对于语句的优化,一般比较靠谱的回复即使--把执行计划发出来看看.当然那些只看语句就说如何如何改代码,我一直都是拒绝的,因为这种算是纯蒙.根据本人经验,大量的性能问题单纯从语句来看很 ...

  4. 【安卓开发】Facebook工程师是如何改进他们Android客户端的

    原文出处: Facebook   译文出处:penkzhou   欢迎分享原创到伯乐头条 作为世界上最大的社交网络,Facebook的Android客户端面临着各种各样的使用环境(地理环境.Andro ...

  5. Makefile自动生成

    automake/autoconf入门作为Linux下的程序开发人员,大家一定都遇到过Makefile,用make命令来编译自己写的程序确实是很方便.一般情况下,大家都是手工写一个简单Makefile ...

  6. TCP的发送系列 — 发送缓存的管理(二)

    主要内容:从TCP层面判断发送缓存的申请是否合法,进程因缺少发送缓存而进行睡眠等待. 因为有发送缓存可写事件而被唤醒. 内核版本:3.15.2 我的博客:http://blog.csdn.net/zh ...

  7. Java基础---Java---IO流-----对象的序列化、管道流、RandomAccessFile、数据类型的流对象DataStream、ByteArrayStream

    ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化. ObjectOutputStream 和 ObjectInputStream ...

  8. android AlarmManager讲解

    Android系统闹钟定时功能框架,总体来说就是用数据库存储定时数据,有一个状态管理器来统一管理这些定时状态的触发和更新.在Andriod系统中实现定时功能,最终还是要用到系统提供的AlarmMana ...

  9. 错误:One or more post-processing actions failed. Consult the OPP service log for details

     今天在做采购出入库明细报表的时候,有的时候能正常打印,有的时候报 One or more post-processing actions failed. Consult the OPP serv ...

  10. Java:使用匿名内部类在方法内部定义并启动线程

    下面的代码展示了在一个方法中,通过匿名内部类定义一个Thread,并Override它的run()方法,之后直接启动该线程. 这样的代码可用于在一个类内部通过另起线程来执行一个支线任务,一般这样的任务 ...