最近学习linux编程相关的东西,对sockaddr以及htons等函数有些理解,搜集了一些资料,整理如下:
创建ipv4的地址结构变量并初始化的常见代码如下:
int port = 12345;//端口号
const char* ip = "192.168.1.120";//IP
struct sockaddr_in address;
bzero(&address,sizeof(address));
address.sin_family = AF_INET;
inet_pton(AF_INET,ip,&address.sin_addr);
address.sin_port = htons(ip);
int sock = socket(AF_INET,SOCK_STREAM,0);
int ret = bind(sock,(struct sockaddr*)&address,sizeof(address));//地址绑定 或 sock命名
该代码几乎成为所有网络代码的基础,里面主要涉及字节序和sockaddr结构体相关的两个知识点。
其中字节序:
大端序:又称网络序
小端序:PC上主流的字节序
不同PC(或者说CPU)所采用的字节序是不一样的,这样两台PC通信的过程中,会出现字节序不符而导致通信失败,因此API提供相关函数去处理字节序相关的问题,最简单的方法以及本代码所表现出来的,就是统一将机器字节序转化为网络序即大端序,这样通信就可以独立于机器限制了。
初始化struct sockaddr结构体时,用到了两个函数inet_pton,htons,这两个函数均是处理输入并转化为网络序,inet_pton将ip字符串转化为网络序,是inet_aton的改进(string to network的简写),inet_pton能同时处理IPV4,IPV6两种。htons函数(host to network short)将字符串转化为short类型的网络字节序。两个函数的作用这里已经很清楚了,用于字节序的转化。
关于sockaddr结构体,在大部分的API函数中需要的是sockaddr结构体,而我们定义的地址结构体一般为sockaddr_in或者sockaddr_in6来代表IPV4/IPV6地址。其定义如下:
#include <netinet/in.h> // All pointers to socket address structures are often cast to pointers // to this type before use in various functions and system calls: struct sockaddr { unsigned short sa_family; // address family, AF_xxx char sa_data[14]; // 14 bytes of protocol address }; // IPv4 AF_INET sockets: struct sockaddr_in { short sin_family; // e.g. AF_INET, AF_INET6 unsigned short sin_port; // e.g. htons(3490) struct in_addr sin_addr; // see struct in_addr, below char sin_zero[8]; // zero this if you want to }; struct in_addr { unsigned long s_addr; // load with inet_pton() }; // IPv6 AF_INET6 sockets: struct sockaddr_in6 { u_int16_t sin6_family; // address family, AF_INET6 u_int16_t sin6_port; // port number, Network Byte Order u_int32_t sin6_flowinfo; // IPv6 flow information struct in6_addr sin6_addr; // IPv6 address u_int32_t sin6_scope_id; // Scope ID }; struct in6_addr { unsigned char s6_addr[16]; // load with inet_pton() }; // General socket address holding structure, big enough to hold either // struct sockaddr_in or struct sockaddr_in6 data: struct sockaddr_storage { sa_family_t ss_family; // address family // all this is padding, implementation specific, ignore it: char __ss_pad1[_SS_PAD1SIZE]; int64_t __ss_align; char __ss_pad2[_SS_PAD2SIZE]; };
sockaddr_in和sockaddr其地址开始的部分是相同的,即可以相互转化。为何要转化,因为IP4,IP6两种地址并不相同,为了使API能共通用化,所以使用sockaddr能同时接受两种地址的输入。
关于sockaddr参考文章可见:https://beej.us/guide/bgnet/output/html/multipage/sockaddr_inman.html (2016/3/2)