本文主要是介绍在linux socket编程时常用的函数以及结构体类型。
一、主机字节序和网络字节序介绍
1.大端和小端
1)小端字节序:低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
2)大端字节序:高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。
通常Inter x86、ARM核采用的是小端模式,Power PC、MIPS UNIX和HP-PA UNIX采用大端模式。
2.主机字节序:不同的CPU有不同的字节序类型,这些字节序是指整数在内存中保存的顺序,这个叫做主机序。
3.网络字节序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。网络字节序采用big endian排序方式。字节序,顾名思义字节的顺序,就是大于一个字节类型的数据在内存中的存放顺序,一个字节的数据没有顺序的问题了。
小结:
主机序的顺序与CPU设计有关,数据的顺序是由cpu决定的,而网络序采用大端序。
二、主机序和网络序的转换
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
htons和ntohs完成16位无符号数的相互转换,htonl和ntohl完成32位无符号数的相互转换。
示例:
#include <arpa/inet.h>
unsigned int a = 0x12345678;
unsigned char *b = (unsigned char *)&a;
printf("host: %02x:%02x:%02x:%02x\n", b[0], b[1], b[2], b[3]);
a = htonl(a);
b = (unsigned char *)&a;
printf("network: %02x:%02x:%02x:%02x\n", b[0], b[1], b[2], b[3]);
host: 78:56:34:12
network: 12:34:56:78
三、socket字节序转换函数
1.常用函数说明
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
typedef uint32_t in_addr_t;
struct in_addr {
in_addr_t s_addr;
};
struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */
in_port_t sin_port; /* port in network byte order */
struct in_addr sin_addr; /* internet address */
};
#include <sys/socket.h>
struct sockaddr {
sa_family_t sin_family;//地址族
char sa_data[14]; //14字节,包含套接字中的目标地址和端口信息
};
//注:sockaddr的缺陷是:sa_data把目标地址和端口信息混在一起
//网络编程中我们会对sockaddr_in结构体进行操作,使用sockaddr_in来建立所需的信息,最后使用类型转化就可以了。
//一般先把sockaddr_in变量赋值后,强制类型转换后传入用sockaddr做参数的函数:sockaddr_in用于socket定义和赋值;sockaddr用于函数参数
//ipv4
in_addr_t inet_addr(const char *cp);
char *inet_ntoa(struct in_addr in);
int inet_aton(const char *cp, struct in_addr *inp);
//上面为常用的转换函数
in_addr_t inet_network(const char *cp);
struct in_addr inet_makeaddr(in_addr_t net, in_addr_t host);
in_addr_t inet_lnaof(struct in_addr in);
in_addr_t inet_netof(struct in_addr in);
//ipv6
//IPv4地址和IPv6地址都适用
#include <arpa/inet.h>
int inet_pton(int af, const char *src, void *dst);
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
//af 可以为AF_INET 或者 AF_INET6;
//inet_pton 函数表示由字符串src转为字节序dst;
//而inet_ntop表示由字节序src 转为字符串dst;
//inet_ntop 中size定义如下:
<netinet/in.h>
#define INET_ADDRSTRLEN 16 //ipv4
#define INET6_ADDRSTRLEN 46 //ipv6
//另外inet_ntop中dst参数不可以是一个空指针,调用者必须为其分配内存空间并指定大小,
//调用成功时该指针为该函数的返回值
示例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
#if 1
unsigned long l1,l2;
char str[16];
struct in_addr addr1, addr2;
l1 = inet_addr("192.168.1.2");
l2 = inet_addr("192.168.10.2");
memcpy(&addr1, &l1, 4);
memcpy(&addr2, &l2, 4);
printf("%s : %s\n", inet_ntoa(addr1), inet_ntoa(addr2)); //注意
printf("%s\n", inet_ntoa(addr1));
printf("%s\n", inet_ntoa(addr2));
#endif
#if 1
addr1.s_addr = inet_addr("192.168.100.2");
inet_aton("192.168.100.100", &addr2);
printf("%s\n", inet_ntoa(addr1));
printf("%s\n", inet_ntoa(addr2));
#endif
#if 1
inet_pton(AF_INET, "192.168.22.21", &addr1.s_addr);
inet_ntop(AF_INET, &addr1.s_addr, str,16);
printf("%s\n", str);
#endif
return 0;
}
192.168.1.2 : 192.168.1.2
192.168.1.2
192.168.10.2
192.168.100.2
192.168.100.100
192.168.22.21
注:
inet_ntoa返回一个char *,而这个char *的空间是在inet_ntoa里面静态分配的,所以inet_ntoa后面的调用会覆盖上一次的调用。再加上printf里面的可变参数的求值是从右到左的。