注意:结构体之间不能直接进行强制转换, 必须先转换成指针类型才可以进行结构体间的类型转换, 这里需要明确的定义就是什么才叫强制转换.

强制转换是将内存中一段代码以另一种不同类型的方式进行解读, 因此转换的空间必须与源空间一一对应.

而结构体则是由不固定的多种类型变量组合而成, 因此强制转换时并不确定原格式与目标格式确定的对应关系, 例如一个结构体为3个变量, 而另一个则为2个, 那么就无法确定如何分配.

因此最简单的让计算机可以理解的方式就是先将结构体转换成指针, 然后将指针转换成目标格式, 再读取转换后的指针, 这样计算机就会按照指针的格式读取特定目标代码段了.

 1、sockaddr 、sockaddr_in 、addrinfo比较

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

sa_family是地址家族,一般都是“AF_xxx”的形式。好像通常大多用的是都是AF_INET。

sa_data是14字节协议地址。

此数据结构用做bind、connect、recvfrom、sendto等函数的参数,指明地址信息。

但一般编程中并不直接针对此数据结构操作,而是使用另一个与sockaddr等价的数据结构sockaddr_in,在netinet/in.h中定义:

struct  sockaddr_in
{
short int sin_family; /* Address family */
unsigned short int sin_port; /* Port number */
struct in_addr sin_addr; /* Internet address */
unsigned char sin_zero[8]; /* Same size as struct sockaddr */
};
struct in_addr
{
unsigned long s_addr;
}; typedef struct in_addr
{
union
{
struct
{
unsigned char s_b1,
s_b2,
s_b3,
s_b4;
} S_un_b;
struct
{
unsigned short s_w1,
s_w2;
} S_un_w;
unsigned long S_addr;
} S_un;
} IN_ADDR;

sin_family指代协议族,在socket编程中只能是AF_INET

sin_port存储端口号(使用网络字节顺序)

sin_addr存储IP地址,使用in_addr这个数据结构

sin_zero是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。

s_addr按照网络字节顺序存储IP地址。

addrinfo结构是getaddrinfo函数用来保存主机地址信息的结构体。在头文件Ws2tcpip.h中定义。

typedef struct addrinfo
{
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
size_t ai_addrlen;
char* ai_canonname;
struct sockaddr* ai_addr;
struct addrinfo* ai_next; } ADDRINFOA, *PADDRINFOA;

ai_flags:表示getaddrinfo要使用的选项,可赋的值在Winsock2.h中有定义,且可以是几个值的组合。

ai_family:地址族。一般为AF_INET。

ai_socktype:套接字类型。SOCK_STREAM,SOCK_DGRAM,SOCK_RAW,SOCK_RDM,SOCK_SEQPACKET。

ai_protocol:协议类型。可取的值取决于ai_address和ai_socktype的值。IPPROTO_TCP,IPPROTO_UDP,IPPROTO_RM。

如果ai_family置为AF_IRDA,则ai_protocol必须为0。

ai_addrlen:ai_addr指向的缓冲区的字节数。

ai_canonname:主机的规范化名称(canonical name)。

ai_addr:指向sockaddr结构的指针。

2、IP地址的网络字节顺序和机器字节顺序

210.25.132.181属于IP地址的ASCII表示法,也就是字符串形式。英语叫做IPv4 numbers-and-dots notation。

如果把210.25.132.181转换为整数形式,是3524887733,这个就是整数形式的IP地址。英语叫做binary data。(其实binary是二进制的意思)

Internet地址用“.”间隔的地址可有下列几种表达方式: a.b.c.d,a.b.c,a.b,a   当四个部分都有定值时,每个都解释成一个字节数据,从左到右组成Internet四字节地址。Internet地址以网络字节顺序返回(字节从左到右排列。请注意,当一个Internet地址在Intel机器上表示成一个32位整型数时,则上述的字节为“d.c.b.a”。这是因为Intel处理器的字节是从右向左排列的。
上述中的“a.b.c.d”就为网络字节顺序,而“d.c.b.a”则为机器字节顺序。

 

3、ntohs, ntohl, htons,htonl的比较和详解

ip地址是32位的,端口号是16位的 。

ntohs =net to host short int 16位

htons=host to net short int 16位

ntohs =net to host long int 32位

htonl=host to net long int  32位

u_short PASCAL FAR ntohs( u_short netshort);

netshort:一个以网络字节顺序表达的16位数。本函数将一个16位数由网络字节顺序转换为16位主机字节顺序。

u_short PASCAL FAR htons( u_short hostshort);

hostshort:一个主机字节顺序表达的16位数。 本函数将一个16位数从主机字节顺序转换成16位网络字节顺序。

u_long PASCAL FAR htonl( u_long hostlong);

hostlong:一个主机字节顺序表达的32位数。本函数将一个32位数从主机字节顺序转换成32位网络字节顺序。

u_long PASCAL FAR ntohl( u_long netlong);

 netlong:一个以网络字节顺序表达的32位数。
本函数将一个32位数由网络字节顺序转换为32位的主机字节顺序。

4、 inet_network()、inet_addr()、inet_aton()、inet_ntoa()

int inet_aton(const char *string, struct in_addr *addr);

in_addr_t inet_addr(const char *cp);

in_addr_t inet_network(const char *cp);

char FAR* PASCAL FAR inet_ntoa( struct in_addr in);

函数inet_addr():将IP地址从 点数格式转换成无符号长整型。使用方法如下:

ina.sin_addr.s_addr = inet_addr("132.241.5.10");

注意,inet_addr()返回的地址已经是网络字节格式,所以你无需再调用 函数htonl()。

函数inet_network():inet_network和inet_addr函数都是用于将字符串形式转换为整数形式用的,两者区别很小,inet_addr返回的整数形式是网络字节序,而inet_network返回的整数形式是主机字节序。

函数int inet_aton(const char *string, struct in_addr *addr):是一个改进的方法来将一个字符串IP地址转换为一个32位的网络序列IP地址。输入参数string包含ASCII表示的IP地址。  输出参数addr是将要用新的IP地址更新的结构。   返回值: 如果这个函数成功,函数的返回值非零。如果输入地址不正确则会返回零。使用这个函数并没有错误码存放在errno中,所以他的值会被忽略。
inet_aton函数和上面这俩小子的区别就是在于他认为255.255.255.255是有效的,他不会冤枉这个看似特殊的IP地址。inet_aton函数返回的是网络字节序的IP地址。如:inet_aton("127.0.0.1",&adr_inet.sin_addr))

函数inet_ntoa():将网络地址转换成“.”点隔的字符串格式。本函数将一个用in参数所表示的Internet地址结构转换成以“.” 间隔的诸如“a.b.c.d”的字符串形式。请注意inet_ntoa()返回的字符串存放在WINDOWS套接口实现所分配的内存中。应用程序不应假设该内存是如何分配的。在同一个线程的下一个WINDOWS套接口调用前,数据将保证是有效。返回值:
  若无错误发生,inet_ntoa()返回一个字符指针。否则的话,返回NULL。其中的数据应在下一个WINDOWS套接口调用前复制出来。函数 inet_ntoa()("ntoa"的含义是"network to ascii"),就像这样: printf("%s",inet_ntoa(ina.sin_addr)); 它将输出IP地址。

5、inet_pton() 和inet_ntop()

Linux下这2个IP地址转换函数,可以在将IP地址在“点分十进制”和“整数”之间转换   而且,inet_pton和inet_ntop这2个函数能够处理ipv4和ipv6。算是比较新的函数了。

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中 。

inet_pton 是inet_addr的扩展,支持的多地址族有下列:   af = AF_INET   src为指向字符型的地址,即ASCII的地址的首地址(ddd.ddd.ddd.ddd格式的),函数将该地址   转换为in_addr的结构体,并复制在*dst中   af =AF_INET6   src为指向IPV6的地址,,函数将该地址   转换为in6_addr的结构体,并复制在*dst中   如果函数出错将返回一个负值,并将errno设置为EAFNOSUPPORT,如果参数af指定的地址族和src格式不对,函数将返回0。

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。

6、gethostbyname和gethostbyaddr

这两个函数仅仅支持IPv4,getaddrinfo函数能够处理名字到地址以及服务到端口这两种转换,返回的是一个sockaddr结构的链表而不是一个地址清单。这些sockaddr结构随后可由套接口函数直接使用。如此以来,getaddrinfo函数把协议相关性安全隐藏在这个库函数内部。应用程序只要处理由getaddrinfo函数填写的套接口地址结构。该函数在 POSIX规范中定义了。



#include<netdb.h>

int getaddrinfo( const char *hostname, const char *service, const struct addrinfo *hints, struct addrinfo **result );

返回0:  成功

返回非0:  出错



hostname:一个主机名或者地址串(IPv4的点分十进制串或者IPv6的16进制串)

service:一个服务名或者10进制端口号数串。

hints:可以是一个空指针,也可以是一个指向某个addrinfo结构的指针,调用者在这个结构中填入关于期望返回的信息类型的暗示。举例来说:如果指定的服务既支持TCP也支持UDP,那么调用者可以把hints结构中的ai_socktype成员设置成SOCK_DGRAM使得返回的仅仅是适用于数据报套接口的信息。



本函数通过result指针参数返回一个指向addrinfo结构链表的指针,而addrinfo结构定义在头文件netdb.h中:

struct addrinfo{

    int       ai_flags; 

    int       ai_family;

    int       ai_socktype;

    int       ai_protocol;

    socklen_t ai_addrlen;

    char    *ai_canonname;

    struct sockaddr *ai_addr;

    struct addrinfo *ai_next;

};



如果本函数返回成功,那么由result参数指向的变量已被填入一个指针,它指向的是由其中的ai_next成员串联起来的addrinfo结构链表。可以导致返回多个addrinfo结构的情形有以下2个:

    1.    如果与hostname参数关联的地址有多个,那么适用于所请求地址簇的每个地址都返回一个对应的结构。

    2.    如果service参数指定的服务支持多个套接口类型,那么每个套接口类型都可能返回一个对应的结构,具体取决于hints结构的ai_socktype成员。



我们必须先分配一个hints结构,把它清零后填写需要的字段,再调用getaddrinfo然后遍历一个链表逐个尝试每个返回地址。



getaddrinfo解决了把主机名和服务名转换成套接口地址结构的问题。



其中,如果getaddrinfo出错,那么返回一个非0的错误值。

socket编程相关的结构体和字节序转换、IP、PORT转换函数的更多相关文章

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

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

  2. c#中关于结构体和字节数组转化

    最近在使用结构体与字节数组转化来实现socket间数据传输.现在开始整理一下.对于Marshal可以查阅msdn,关于字节数组与结构体转代码如下: using System; using System ...

  3. Golang面向对象编程-struct(结构体)

    Golang面向对象编程-struct(结构体) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.什么是面向对象编程 面向对象编程(Object Oriented Program ...

  4. C语言结构体的字节对齐原则

    为什么要对齐? 现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特 定的内存地址访问,这就需要各种类型数据 ...

  5. C#结构体和字节数组的转换函数

    在通信过程中,一般我们都会操作到字节数组.特别是希望在不同语言编程进行操作的时候. 虽然C#提供了序列化的支持,不用字节数组也行.但操作字节数组肯定会碰到.   一般都会采用结构来表示字节数组.但结构 ...

  6. C与C# socket 跨平台通讯传输结构体

    最近需要写一个C组成的服务器端与C#的客户端进行交互的软件,刚开始写的时候发现C#端解析时候出现了故障,经过仔细研究后发现原因是发送方传输太快,出现了所谓粘包的现象.也就是在C#端的Receive() ...

  7. 【C语言】结构体占用字节数及存储与空间分配

    我们都知道在数据类型中,char类型占1个字节,short占2个字节,int占4个字节,long占8个字节等等. 在计算结构体大小时需要考虑其内存布局,结构体在内存中存放是按单元存放的,每个单元多大取 ...

  8. socket编程基础-字节序/IP/PORT转换/域名

    socket编程基础 网络IP操作函数 字符串的IP和32位的IP转换 #include <sys/socket.h> #inlcude <netinet/in.h> #inc ...

  9. Go -- 中结构体与字节数组能相互转化

    编码时如下,假设默认你的结构体为data func Encode(data interface{}) ([]byte, error) { buf := bytes.NewBuffer(nil) enc ...

随机推荐

  1. EntityFramework 实体映射到数据库

    EntityFramework实体映射到数据库 在Entity Framework Code First与数据表之间的映射方式实现: 1.Fluent API映射 通过重写DbContext上的OnM ...

  2. springMVC返回json

    <mvc:annotation-driven> <mvc:message-converters register-defaults="true"> < ...

  3. ✡ leetcode 167. Two Sum II - Input array is sorted 求两数相加等于一个数的位置 --------- java

    Given an array of integers that is already sorted in ascending order, find two numbers such that the ...

  4. Latex转换之PDF

    近期一直在做如何使用latex将模板转换成PDF.现在写下在项目中如何实现. 1.首先你先进官网下载http://www.miktex.org/download.我用的是如下图所示. 在下载好的Mik ...

  5. linux之tmpwatch命令

    系统使用时间长后会产生临时文件(/tmp下),需要清理.但清理的时候不推荐使用rm -rf.这样有时会引起程序的僵死. tmpwatch的说明: [root@AY121231034820cd91077 ...

  6. VISIBLE、INVISIBLE、GONE的区别

    VISIBLE:设置控件可见 INVISIBLE:设置控件不可见 GONE:设置控件隐藏 而INVISIBLE和GONE的主要区别是:当控件visibility属性为INVISIBLE时,界面保留了v ...

  7. MFC resizer封装

    用法: #include "resizer.h" 在mfc对话框头文件里面添加成员: CResizer m_Resizer; mydialog.cpp里面: OnInitDialo ...

  8. Updatepanel 注册javascript 方法

    ScriptManager.RegisterStartupScript(this.UpdatePanel1, this.GetType(), "test", "alert ...

  9. ssl_error_rx_record_too_long

    Linux下安装SSL 当使用https访问时出现: SSL 接收到一个超出最大准许长度的记录. (错误码: ssl_error_rx_record_too_long) 网上说修改虚拟机 vi /et ...

  10. 报错:HTTP Status 404 - There is no Action mapped for namespace [/] and action name [product-save] associated with context path [/20161101-struts2-2].

    运行:index.jsp---->input.jsp----->details.jsp,但是在input.jsp到details.jsp的时候报错误. 异常如下: 严重: Could no ...