1. 套接字地址结构

1.1 IPv4套接字地址结构

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

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

  注:in是internet缩写。

  1. struct in_addr {
  2. unsigned long s_addr;
  3. }

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

  1. /* Internet address. */
  2. typedef uint32_t in_addr_t;
  3. struct in_addr
  4. {
  5. in_addr_t s_addr;
  6. };

  2)struct sockaddr_in结构定义如下:

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

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

  1. /* Structure describing an Internet socket address. */
  2. struct sockaddr_in
  3. {
  4. __SOCKADDR_COMMON (sin_);
  5. in_port_t sin_port; /* Port number. */
  6. struct in_addr sin_addr; /* Internet address. */
  7.  
  8. /* Pad to size of `struct sockaddr'. */
  9. unsigned char sin_zero[sizeof (struct sockaddr) -
  10. __SOCKADDR_COMMON_SIZE -
  11. sizeof (in_port_t) -
  12. sizeof (struct in_addr)];
  13. };
  14.  
  15. /* Notes */
  16. /* bits/sockaddr.h */
  17. typedef unsigned short int sa_family_t;
  18.  
  19. #define __SOCKADDR_COMMON(sa_prefix) \
  20. sa_family_t sa_prefix##family
  21.  
  22. #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,如下所示:

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

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

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

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

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

  1. struct sockaddr_in myad;
  2. memset(&myad, , sizeof(myad));

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

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

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

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

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

  

1.2 IPv6套接字地址结构

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

  1. struct in6_addr {
  2. uint8_t s6_addr[]; /* 128-bit IPv6 address */
  3. /* network byte ordered */
  4. }
  5.  
  6. #define SIN6_LEN /* required for compile-time tests */
  7.  
  8. struct sockaddr_in6 {
  9. uint8_t sin6_len; /* length of this struct (28) */
  10. sa_family_t sin6_family; /* AF_INET6 */
  11. in_port_t sin6_port; /* transport layer port# */
  12. /* network byte ordered */
  13. uint32_t sin6_flowinfo; /* flow information, undefined */
  14. struct in6_addr sin6_addr; /* IPv6 address */
  15. /* network byte ordered */
  16. uint32_t sin6_scope_id; /* set of interfaces for a scope */
  17. }

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

  1. /* IPv6 address */
  2. struct in6_addr
  3. {
  4. union
  5. {
  6. uint8_t __u6_addr8[];
  7. #if defined __USE_MISC || defined __USE_GNU
  8. uint16_t __u6_addr16[];
  9. uint32_t __u6_addr32[];
  10. #endif
  11. } __in6_u;
  12. #define s6_addr __in6_u.__u6_addr8
  13. #if defined __USE_MISC || defined __USE_GNU
  14. # define s6_addr16 __in6_u.__u6_addr16
  15. # define s6_addr32 __in6_u.__u6_addr32
  16. #endif
  17. };
  18.  
  19. /* Ditto, for IPv6. */
  20. struct sockaddr_in6
  21. {
  22. __SOCKADDR_COMMON (sin6_);
  23. in_port_t sin6_port; /* Transport layer port # */
  24. uint32_t sin6_flowinfo; /* IPv6 flow information */
  25. struct in6_addr sin6_addr; /* IPv6 address */
  26. uint32_t sin6_scope_id; /* IPv6 scope-id */
  27. };

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函数原型如下[将"点分十进制" -> "整数"]

  1. #include <sys/types.h>
  2. #include <sys/socket.h>
  3. #include <arpa/inet.h>
  4. int inet_pton(int af, const char *src, void *dst);
  5. //这个函数转换字符串到网络地址,第一个参数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函数原型如下[将"点分十进制" -> "整数"]

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

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

  示例程序:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <unistd.h>
  5. #include <sys/socket.h>
  6. #include <netinet/in.h>
  7. int main (void)
  8. {
  9. char IPdotdec[]; // 存放点分十进制IP地址
  10. struct in_addr s; // IPv4地址结构体
  11. // 输入IP地址
  12. printf("Please input IP address: ");
  13. scanf("%s", &IPdotdec);
  14. // 转换
  15. inet_pton(AF_INET, IPdotdec, (void *)&s);
  16. printf("inet_pton: 0x%x\n", s.s_addr); // 注意得到的字节序
  17. // 反转换
  18. inet_ntop(AF_INET, (void *)&s, IPdotdec, );
  19. printf("inet_ntop: %s\n", IPdotdec);
  20. }

  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. AR模块常用函数

    --AR模块常用函数 FUNCTION get_fnd_user_name ( p_user_id IN NUMBER ) return VARCHAR2 IS CURSOR c_user_name ...

  2. Android简易实战教程--第二十二话《自定义组合控件模拟qq登录下拉框和其中的一些”小技巧”》

    转载此文章请注明出处:点击打开链接   http://blog.csdn.net/qq_32059827/article/details/52313516 首先,很荣幸此专栏能被CSDN推荐到主页.荣 ...

  3. 《java入门第一季》之对文件和字符串进行MD5加密工具类

    上一篇介绍了MD5加密算法,之前写的代码有些冗余,而且可读性很差.今天把对文本数据的加密,以及获取文件的md5值做一个封装类.代码如下: package com.itydl.utils; import ...

  4. JQuery实战---窗口效果

    在前面的相关博文中,小编对jquery的相关知识进行了简单的总结,关于jquery的很多小的知识点,都需要我们自己去动手和实践,一行行代码都需要我们自己亲自动手去敲,今天我们继续来学习jquery的相 ...

  5. 后端分布式系列:分布式存储-MySQL 数据库事务与复制

    好久没有写技术文章了,因为一直在思考 「后端分布式」这个系列到底怎么写才合适.最近基本想清楚了,「后端分布式」包括「分布式存储」和 「分布式计算」两大类.结合实际工作中碰到的问题,以寻找答案的方式来剖 ...

  6. xml的今生今世

    跟随小编学习的脚步,今天小编来简单总结一下xml的今生今世,xml百度百科对她这样诠释到:可扩展标记语言 (ExtensibleMarkup Language, XML),用于标记电子文件使其具有结构 ...

  7. OC可点击的两种轮播图效果

    基本上,每一个APP都有一个轮播图的效果展示,一般都是用来展示图片的一些信息,然后可以点击查看或购买,所以在此我将这种轮播图进行了一个类的封装,效果包含两种形式:第一种,来回轮转样式,第二种,一个方向 ...

  8. qualcomm memory dump 抓取方法

    Memory dump是系统出现crash时常用的分析故障原因的方法,qualcomm 各子系统运行时,为方便debug,都会开辟ram log和debug variable用于保存各系统运行信息及健 ...

  9. ffmpeg.c函数结构简单分析(画图)

    前一阵子研究转码的时候看了FFmpeg的源代码.由于ffmpeg.c的代码相对比较长,而且其中有相当一部分是AVFilter有关的代码(这一部分一直不太熟),因此之前学习FFmpeg的时候一直也没有好 ...

  10. 刀片服务器和磁盘阵列卡(RAID)技术---永和维护

    近期客户需要更换服务器,客户把买好的服务器送来了,原本感觉很小的一个服务器,可当我看到的时候是一个大个的又长又宽,类似机房服务器的那种,后来米老师给大致讲解一番:这个是刀片服务器. 刀片服务器是指在标 ...