网络地址
A类地址:首位以0开始。
B类地址:首位以10开始
C类地址:首位以110开始
端口号
IP用于区分计算机,端口号用于区分应用程序。传输到计算机的网络数据,操作系统通过端口号分发给对用的套接字。端口号有16位构成,范围0-65535.但0-1023是知名端口号,一般分配给特定应用程序。另外,虽然端口号不能重复,但TCP套接字和UDP套接字不会共用端口号,所以允许重复 。 例如:如果某TCP套接字使用9190号端口,则其他TCP套接字就无法使用该端口号,但UDP套接字可以使用。数据传输目标地址同时包含IP地址和端口号,只有这样,数据才会被传输到最终的目的应用程序(应用程序套接字)。
地址信息的表示
应用程序 中使用的IP地址和端口号以结构体的形式给出了定义 。
成员:
sin_family
: 地址族。IPV4:AF_INET。IPv6: AF_INET6。
sin_port
: 16位端口号(网络字节序保存)
sin_addr
: 32位IP地址,也以网络字节序保存。
sin_zero[8]
:为了与结构体 sockaddr
保持一致。
在sockaddr
结构体中:
struct sockaddr{
sa_family_t sin_family;
char sa_data[14]
}
sa_data[14]
包含了4字节IP和两字节端口号。还有8个字节应该补零。但是这样对于分配端口号和IP地址就非常麻烦。因此有了sockaddr_in
结构体。在bind
函数中强制类型转化即可。
网络字节序和地址变换
CPU 向内存保存数据的方式有2种,这意味着CPU解析数据的方式也分为2种。
- 大端序 ( Big Endian ) : 高位字节存放到低位地址 。
- 小端序 ( Little Endian ):高位字节存放到高位地址 。
在通过网络传输数据时约定统一方式,这种约定称为网络字节序(Network Byte Order
),统一为大端序 。
字节序变换
unsigned short htons(unsigned short);
unsigned short ntohs(unsigned short);
unsigned long hton1(unsigned long);
unsigned long ntohl(unsigned long)
//h代表主机(Host)字节序
//n代表网络(Network)字节序
//s代表short(2个字节)
//l代表long(4个字节)
htons
:把2字节short
类型数据从主机字节序转化为网络字节序。
除了向sockaddr_in
结构体变量填充数据外,其他情况无需考虑字节序问题。
网络地址的初始化与分配
将字符串形式的IP地址转化为32位IP地址
#include <arpa/inet.h>
in_addr_t inet_addr(const char * string)
// 成功时返回32位大端序整数型, 失败时返回 INADOR_NONE 。
#include <arpa/inet.h>
int inet_aton(const char * string, struct in_addr * addr)
//成功返回1,失败返回0
//inet_aton函数与inet_addr函数在功能上完全相同,也将字符串形式E地址转换为32位网络字
//节序整数并返回。只不过该函数利用了 in_addr结构体,且其使用频率更高 。
IP地址转化为字符串:
#include <arpa/inet.h>
char* inet_ntoa(struct in_addr addr )
//成功时返回转换的字符串地址值,失败时返回-1。
网络地址初始化
sockaddr_in addr;
char* serv_ip = "211.1.2.32";
char* serv_port = "1234";
memset(&addr,0,sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(serv_port));
addr.sin_addr.in_addr = inet_addr(serv_ip);
上述网络地址信息初始化过程主要针对服务器端而非客户端 。 给套接字分配IP地址和端口号主要是为下面这件事做准备 :
“请把进入IP 211 .217.168.13 、 9 190端口的数据传给我 ! "
反观客户端中连接请求如下 :
“请连接到 IP 211.217.168.13 、 9190 端 口 ! ”
请求方法不同意味着调用的函数也不同 。服务器端的准备工作通过bind函数完成,而客户端则通过connect
函数完成 。 因此,函数调用前需准备的地址值类型也不同 。 服务器端声明 sockaddr_in
结构体变量将其初始化为赋予服务器端IP和套接字的端口号,然后调用bind
函数 。而客户端则声明 sockaddr_in
结构体, 并初始化为要与之连接的服务器端套接字的IP和端口号, 然后调用connect
函数 。
向套接字分配网络地址
#include <sys/socket.h>
int bind(int sockfd , struct sockaddr * myaddr, socklen_t addrlen);
//成功返回0 失败返回-1
//sockfd :要分配地址信息 ( IP地址和端口号)的套接字文件描述符。
//myaddr: 存有地址信息的结构体变量地址值 。
//addrlen:第二个结构体变量的长度 。
int serv_sock;
sockaddr_in addr;
char* serv_ip = "211.1.2.32";
char* serv_port = "1234";
/*服务端创建套接字*/
serv_sock = socket(PF_INET,SOCK_STREAM,0);
/* 地址信息初始化*/
memset(&addr,0,sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(serv_port));
addr.sin_addr.in_addr = inet_addr(serv_ip);
/*分配地址信息套接字*/
bind(serv_sock, (sockaddr*)&addr, sizeof(addr));
//未加异常处理