主要说的是几个转换函数
- 网络字节与本地字节的相互转化
- 二进制ip与十进制ip的转换
- ip地址与主机名的转化
1.socket类型
socket:linux中的网络编程是用socket接口进行的,socket是一种特殊的I/O接口,也是一种文件描述符,socket是一种常用的进程间通信机制,通过他不仅可以实现本机上的进程
间的通信,还可以实现不同机器上的进程间的通信,可以通过网络实现不同机器上的不同机器上的进程进行通信
每一个socket都用一个半相关描述{协议,本地地址,本地端口}来表示,一个完整的套接字用一个相关描述{协议,本地地址,本地端口,远程地址,远程端口}来表示,socket也有类似于打开文件的操作,然后就可以通过这个socket建立连接,数据传输等操作。
2.socket类型
(1)流式socket(SOCKET_STREAM)
流式套接字提供可靠的,面向连接的通信流,使用TCP协议,保证了数据传输的正确性和顺序性
(2)数据报socket(SOCKET_DGRAM)
定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,不保证是可靠的,无差错的,使用协议UDP
(3)原始socket
原始套接字允许对底层协议如IP/ICMP进行直接访问,他功能强大但使用不方便,主要用于一些协议的开发
3.两种数据结构的介绍
两种重要的数据结构:socketaddr和socketaddr_in这两种数据结构都是用来保存socket信息的
struct sockeaddr
{
unsigned short sa_family ;/*地址族*/
char sa_data[14] ;/*14字节的协议地址,包含该socket的ip地址和端口号*/
};
struct sockeaddr_in
{
short sa_family ;/*地址族*/
unsigned short int sin_port ;/*端口号*/
struct in_addr sin_addr ;/*ip地址*/
unsigned char sin_zero[8] ;/*填充为0为了保持struct socketaddr同样的大小*/
};
这两种结构是可以相互转化的,socketaddr_in使用更加方便,在建立socketaddr或socketaddr_in,就可以对socket进行适当操作了。
结构中的sa_family可选的常见值如下:
4.数据存储的优先顺序
计算机中数据存储有两种方式:高位字节优先(大端模式),地位字节优先(小端模式,PC机通常采用小端模式),记得有次面试问我这个,我完全不知道什么东西,在那乱说,结果可想而知,但internet使用的是高位字节优先的模式,所以需要这两种模式进行转换,主要有四个函数:htons(),ntohs(),htonl(),ntohl(),四个函数分别实现网络地址和机器地址的转换,这个是很好记忆的,h代表host,n代表networks,s是short代表ip端口号,l是long代表ip地址。
下面列出了这几个函数的语法格式:
ps:这些函数只是使其得到相应的字节序,用户不需知道真的需要转换,如果不需要转换,这些系统的函数将会定义成空宏
5.地址格式转换
用户在表达地址时使用点分的进制,或者冒号分开的ipv6地址,而在socket编程中使用的是二进制的ip地址,这就需要将这两个值进行转换,ipv4中使用的函数是inet_aton(),
inet_addr(),inet_ntoa(),ipv4和ipv6兼容的函数有inet_pton(),inet_ntop().
inet_pton()------------将点分的十进制映射成二进制地址
inet_ntop()------------将二进制的地址映射成点分的十进制的地址
inet_pton()函数语法的格式:
inet_ntop()函数语法格式:
6.名字地址转换
ip地址很长,特别到了ipv6的时候就更难记住了,那我们可以使用主机名是很好的选择,linux中有一些函数可以实现主机和ip的转换,这些常见的函数如下:
gethostbyname(),gethostbyaddr(),getaddrinfo()
gethostbyname()----------将主机名转换成ip
gethostbyaddr()---------------将主机名转换成地址
getaddrinfo()---------------能够自动识别ipv4和ipv6地址
gethostbyname()和gethostbyaddr()都涉及到了一个hostent结构体,他的形式是这样的:
struct hostent
{
char *h_name ;/*正式主机名*/
char ** h_aliases ;/*主机别名*/
int h_addrtype ;/*地址类型*/
int h_length ;/*地址字节长度*/
char **h_addr_list ;/*指向ipv4或ipv6的地址指针数组*/
};
调用gethostbyname()或者gethostbyaddr()能够返回hostent结构体的相关信息
getaddrinfo()涉及到一个结构体addrinfo结构体
struct addrinfo
{
int ai_flags ;/*AI_PASSIVE,AI_CANONNAME*/
int ai_family ;/*主机别名*/
int ai_socketype ;/*地址类型*/
int ai_protocol ;/*协议类型*/
int ai_addrlen ;/*地址字节长度*/
char *ai_canonname ;/*主机名*/
struct socketaddr *ai_addr ;/*socket结构体*/
struct addrinfo *ai_next ; /*下一个指针链表*/
};
与hostent相比,addrinfo结构体包含了更多的信息.
gethostbyname()函数语法如下:
ps:调用函数时首先对hostent结构体中的h_addrtype,h_lenth进行设置,如果IPV4则可以设置成AF_INET和4,若为IPV6则可以设置成AF_INET6和16,如果不设置则默认为ipv4类型
getaddrinfo函数语法如下:
ps:在调用之前可以对hints服务线索进行设置,这里涉及到addiinfo结构体,他的常用选项如下所示:
ps:(1)服务器在调用getaddrinfo(),ai_flags通常设置成AI_PASSIVE,用于bind()函数,主机名通常设置成NULL
(2)客户端在调用getaddinfo时,ai_flags一般不设置成AI_PASSIVE,但主机名nodename和服务器名一般不为空
(3)即使不设置ai_flags为AI_PASSIVE,取出的地址也可以绑定,取出的地址也是应该可以绑定的,很多程序的ai_flags直接设置成0,即三个标志位都不设置,这种情况下只 要hostname和servname设置的没有问题也是可以正常绑定的
下面有个关于getaddrinfo的使用例子
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
int main()
{
struct addrinfo hints, *res = NULL ;
int rc ;
memset (&hints, 0, sizeof(hints)) ;
/*初始化hints结构体*/
hints.ai_flags = AI_CANONNAME ;//返回主机名称
hints.ai_family = AF_UNSPEC ;
hints.ai_socktype = SOCK_DGRAM ;
hints.ai_protocol = IPPROTO_UDP ;
rc = getaddrinfo("localhost", NULL, &hints, &res) ;
if (rc != 0)
{
perror("getaddrinfo") ;
exit(1) ;
}else
{
printf("host name is %s\n", res->ai_canonname) ;
}
exit(0) ;
}
这段程序的运行结果如下:
由此就可以得出主机名称。