这里是本人对Linux网络编程的学习做一个记录,主要参考书籍如下:
- Linux高性能服务器编程
- Unix网络编程 卷1:套接字联网API
socket是一个实现内核空间与用户空间的通信的接口。
一、四层网络模型
二、socket地址API
(1)通用socket地址
socket地址结构体
//头文件:<bits/socket.h>
struct sockaddr {
//地址族类型(与协议族对应,如ipv4/ipv6)
sa_family_t sa_family;
//实际存储地址
char sa_data[14];
}
地址族与协议族的关系表
协议族 | 地址族 | 描述 |
---|---|---|
AF_UNIX | PF_UNIX | UNIX本地域协议族 |
AF_INET | PF_INET | TCP/IPv4协议 |
AF_INET6 | PF_INET6 | TCP/IPv6协议 |
通过以上结构体的定义,可以看出sa_data只有14位,无法保存更长的地址数据,因此,一般使用专用的socket地址来保存特定的socket地址
(2)专用socket地址
unix本地域地址:
//头文件:<sys/un.h>
struct sockaddr_un {
//地址族(这里就是AF_UNIX)
sa_family_t sin_family;
//socket文件存放的地址
char sun_path[108];
}
ipv4地址族
//头文件<netinet/in.h>
struct sockaddr_in {
//地址族(这里是AF_INET)
sa_family_t sin_family;
//地址结构体
struct in_addr sin_addr;
//端口
in_port_t sin_port;
//sockaddr结构体填充的大小
unsigned char sin_zero[sizeof (struct sockaddr) -
(sizeof (unsigned short int)) -
sizeof (in_port_t) -
sizeof (struct in_addr)];
}
ipv4地址结构体in_addr结构:
//头文件<netinet/in.h>
struct in_addr {
//整型的地址
in_addr_t s_addr;
};
ipv6地址族
//头文件<netinet/in.h>
struct sockaddr_in6 {
//地址族(这里是AF_INET6)
sa_family_t sin_family;
//端口
in_port_t sin6_port;
//ipv6流信息
uint32_t sin6_flowinfo;
//ipv6地址结构体
struct in6_addr sin6_addr;
//ipv6范围ID
uint32_t sin6_scope_id;
};
ipv6地址结构体in6_addr结构:
struct in6_addr {
union {
uint8_t__u6_addr8[16];
#ifdef __USE_MISC
uint16_t __u6_addr16[8];
uint32_t __u6_addr32[4];
#endif
} __in6_u;
#define s6_addr__in6_u.__u6_addr8
#ifdef __USE_MISC
# define s6_addr16__in6_u.__u6_addr16
# define s6_addr32__in6_u.__u6_addr32
#endif
};
三、ip地址转换函数
这里只列举ipv4的地址,ipv6的相关函数类似。
我们经常看到ip地址存储在数据库中,有人习惯存储为int型,有人习惯存储为varchar(字符串)。其实在linux底层已经内置了两种存储类型之间的转换函数。
(1)inet_addr
原型:
extern in_addr_t inet_addr (const char *cp)
参数说明:const char *cp表示传入的是一个只读字符指针(其实就是一个字符串)
作用:将一个字符串的ipv4地址转换为int型
返回值:返回转换后的结果
举个例子:
#include <arpa/inet.h>
#include <stdio.h>
/**
* 实现一个将字符串型的ipv4地址转换为整型
* @param char *address:传入的是一个字符串型的ipv4地址
* @return long
*/
long ip2long(char *address)
{
//返回的整型ip地址
long ip;
//调用系统函数计算
ip = inet_addr(address);
return ip;
}
int main(void)
{
printf("the address of 8.8.8.8:%d\n", ip2long("8.8.8.8"));
return 0;
}
(2)inet_aton
原型:
extern int inet_aton (const char *cp, struct in_addr *inp)
参数说明:
const char *cp:要传入的字符串地址
struct in_addr *inp:要传入的用来存储转换后的结果的结构体指针
作用:将一个字符串的ipv4地址转换为int型(它与inet_addr的区别就是它不直接返回结果,需要将声明一个用来存储结果的结构体指针)
返回值:如果转换成功,则返回1;否则为0
注:struct in_addr的结构:
//头文件<netinet/in.h>
struct in_addr {
//整型的地址
in_addr_t s_addr;
};
举个例子:
#include <stdio.h>
#include <arpa/inet.h>
/**
* 实现一个将字符串型的ipv4地址转换为整型
* @param char *address : 传入的是一个字符串型的ipv4地址
* @return long
*/
long ip2int(char *address)
{
//存储转换是否成功状态
int res;
//存储转换结果
long ip = 0;
//需要使用in_addr结构体
struct in_addr iaddr;
//传入原始字符串指针和用来保存结果的指针
res = inet_aton(address, &iaddr);
//处理结果1表示成功,0表示失败
if (res == 1) {
ip = iaddr.s_addr;
}
return ip;
}
int main()
{
printf("address of 8.8.8.8:%d\n", ip2int("8.8.8.8"));
return 0;
}
(3)inet_ntoa
原型:
extern char *inet_ntoa (struct in_addr in)
参数说明:
- struct in_addr in:要传入用来转换的地址结构体变量
作用:将一个整型的ipv4地址转换为字符串型的ipv4地址
返回值:char *
举个例子:
#include <stdio.h>
#include <arpa/inet.h>
/**
* 整型ipv4地址转换为字符串型的地址
* @param long ip :要转换的整型ipv4地址
* @return char *
*/
char *long2ip(long ip)
{
//用来存储结果
char *address;
//声明地址结构体变量
struct in_addr iaddr;
//赋值给结构体成员
iaddr.s_addr = ip;
//调用系统函数转换
address = inet_ntoa(iaddr);
return address;
}
int main()
{
printf("ip address of 134744072:%s\n", long2ip(134744072));
return 0;
}