本片博客主要记录我在学习过程中对网络编程的基础,包括套接字、IP地址、端口以及字节序
1、套接字概述
1.1、套接字定义:
套接字是最早由BSD在1982年引入的通信机制,目前已经被移植到主流的操作系统中。对于应用开发人员来说,套接字(Socket)是一个特殊的I/O接口,也是一种文件描述符。Socket是一种常用的进程间通信机制,不仅能实现本地不同进程之间的通信,而且通过网络能够在不同主机的进程之间进行通信。对于网络通信而言,每一个socket都可以用网络地址结构{协议、本地地址、本地端口}来表示。Socket通过一个专门的函数创建,并返回一个int型的socket描述符。随后的各种操作都是通过socket描述符来实现的。
1.2、套接字类型
常见的socket类型有如下三种:
(1)流式套接字(SOCK_STREAM)
流式套接字是提供可靠的、面向连接的通信流,保证数据的可靠性和按序收发。TCP通信使用的就是流式套接字。
(2)数据报套接字(SOCK_DGRAM)
数据报套接字实现了一种不可靠、无连接的服务。数据通过独立的报文进行传输,是无序的,并且不保证可靠的传输。UDP使用的就是数据报套接字。
(3)原始套接字(SOCK_RAW)
原始套接字允许对底层协议(如IP或ICMP)进行直接访问,它功能强大但使用不便,主要用于一些协议的开发。
2、IP地址
2.1、IP地址的作用
IP地址用来标识网络中的一台主机。根据不同的协议版本,分为Ipv4(32位)和Ipv6(128位),目前阶段使用广泛的还是Ipv4,所以本片博客主要讨论的是IPV4的网络通信。一个IP地址分为两个部分:网络号和主机号。其中网络号与主机号根据子网掩码来区分。简单的说,有了源IP和目标IP,数据包就能在不同的主机之间传输。
2.2、IP地址格式转换
IP地址有两种不同的格式:点分十进制和32位二进制形式。前者是用户所熟悉的形式,后者是网络传输中IP地址的存储方式。Ipv4地址转换函数有inet_aton()、int_addr()、和int_ntoa(),而Ipv4和Ipv6兼容的函数有inet_pton()和inet_ntop()。因为Ipv6是下一代互联网的标准协议,所以这里主要介绍Ipv4。int_addr()和inet_pton()函数是将点分十进制转换为二进制(例如,将“192.168.1.123”转换为四个字节的数据(从低字节起依次为192、168、1、123)),而inet_ntop()是inet_pton()的反向操作,将二进制地址形式转化成点分十进制的形式。
int_addr()函数的要点
所需头文件 | #include<arpa/inet.h> |
函数原型 | int inet_addr(const char*strptr); |
参数 | strptr:要转换的IP地址字符串 |
函数返回值 | 成功:32位二进制IP地址(网络字节序) 失败:-1 |
inet_pton()函数语法要点
所需头文件 | #include <arpa/inet.h> | |
函数原型 | int inet_pton(int af, const char *src, void *dst); | |
参数 | af | AF_INET:Ipv4协议 |
AF_INET6:Ipv6协议 | ||
src:要转换的IP地址字符串 | ||
dst:存放转换后的地址的缓冲区 | ||
函数返回值 | 成功:0 |
|
失败:-1 |
inet_ntop()函数语法要点
所需头文件 | #include <arpa/inet.h> | |
函数原型 | const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); | |
参数 | af | AF_INET:Ipv4协议 |
AF_INET6:Ipv6协议 | ||
src:要转换的二进制IP地址 | ||
dst:存放十进制地址字符串的缓冲区 | ||
len:缓冲区的长度 |
||
函数返回值 | 成功:返回dst |
|
失败:NULL |
2.3、地址结构相关的处理
(1)数据结构介绍
下面是两个非常重要的数据类型:sockaddr和sockaddr_in,这两个数据类型都是表示地址信息的,其定义如下:
struct sockaddr
{
sa_family_t sa_family; /*地址族*/
char sa_data[14]; /*14字节的协议地址*/
}
/*
sa_family字段值
AF_INET:IPv4协议
AF_INET6:IPv6协议
AF_LOCAL:UNIX域协议
AF_LINK:链路地址协议
AF_KEY:密钥套接字
*/
struct sockaddr_in
{
short sin_family;/*Address family一般来说AF_INET(地址族)PF_INET(协议族)*/
unsigned short sin_port;/*Port number(必须要采用网络数据格式,普通数字可以用htons()函数转换成网络数据格式的数字)*/
struct in_addr sin_addr;/*IP address in network byte order(Internet address)*/
unsigned char sin_zero[8];/*Same size as struct sockaddr没有实际意义,只是为了 跟SOCKADDR结构在内存中对齐*/
}
这两个数据类型大小相同,通常使用sockaddr_in来保存某个网络地址,在使用时强转成sockaddr类型的指针。
注意:在sockaddr_in这个结构体中的一个元素sin_addr是一个结构体,描述图下图:
3、端口
端口号,实质就是一个数字编号,用来在我们一台主机中(主机的操作系统中)唯一的标识一个能上网的进程,IP地址用来唯一标识我们的电脑。端口号和IP地址一起会被打包到当前进程发出或者接收到的每一个数据包中。每一个数据包将来在网络上传递的时候,内部都包含了发送方和接收方的信息(就是IP地址和端口号),所以IP地址和端口号这两个往往是打包在一起不分家的。
对于端口的含义和作用,下面从几个方面进行介绍
(1)端口(号)是一个无符号短整型,取值范围从0到65535
(2)端口号是系统的一种资源,0到1023一般被系统程序所使用,大多数TCP/IP实现给临时端口分配1023~5000之间的端口号,大于5000的端口号是为其他服务器预留的。
(3)TCP端口号与UDP端口号独立,互不影响
(4)如果说IP地址可以用来表示网络中的一台主机,那么端口号可以用来代表主机内部的某个套接字。换句话说,需要把他和某个IP地址及端口号绑定,这样才能实现端到端的通信。
4、字节序
字节序又称为主机字节序Host Byte Order,HBO,是指计算机中多字节整型数据的存储方式。字节序有两种:大端(高位字节存储在低位地址,低位字节存储在高位地址)和小端(和大端序相反,PC通常采用小端模式)。在网络通信中,发送方和接收方有可能使用不同的字节序,为了保证数据接收后能被正确的解析处理,同意规定:数据以高位字节优先顺序在网络上传输。因此数据在发送前和接收后都需要在主机字节序和网络字节序之间转换。
对于字节序经常使用的函数涉及到4个:htons()、ntohs()、htonl()、ntohl()。h代表host,n代表network,s表示short,l表示long。通常16bit的IP端口号用前面两个函数处理,而IP地址用后两个函数来转换。调用这些函数只是为了得到相应的字节序,用户不需要知道该系统的主机的字节序和网络字节序是否真正的相等。如果相等不需要转换的话,该系统的这些函数会定义成空宏。 这四个函数的用法都是传入对应格式的IP地址,然后返回转换字节序后的数值。