TCP/IP网络编程 第三章 地址族与数据序列

地址族与数据序列

目录

  1. 分配给套接字的IP地址和端口号
  2. 地址信息的表示
  3. 网络字节序与地址变换
  4. 网络地址的初始化与分配
  5. 基于Windows的实现

1 分配给套接字的IP地址和端口号

1.1 网络地址

1.1.1 两类IP地址

IPV4:4字节地址族(目前通用)
IPV6:16字节地址族(为应对2010年前后IP地址耗尽的问题而提出的标准)

IPV4地址族(E类已被预约,一般不会使用)
在这里插入图片描述
1.1.2 基于IP地址的数据传输过程

  1. 构成网络的路由器(交换机)传递数据
  2. 由路由器(交换机)根据数据中的主机地址,向目标主机传递数据

在这里插入图片描述

1.2 网络地址分类与主机地址边界

只需通过IP地址的第一个字节即可判断网络地址占用的字节数

A类地址的首字节范围:0-127(首位以0开始)
B类地址的首字节范围:128-191(前两位以10开始)
C类地址的首字节范围:192-223(前三位以110开始)

1.3 用于区分套接字的端口号

数据传输目标地址同时包含IP地址(确定目标计算机)和端口号(确定目标应用程序)
在这里插入图片描述

  1. 端口号作用:同一操作系统内,区分不同套接字
  2. 端口号由16位组成,可分配的端口号范围是0-65535(一般不含0-1023)
  3. 同一类别(TCP、UDP)套接字的端口号不能重复

2 地址信息的表示

2.1 表示IPV4地址的结构体

struct sockaddr_in
{
	sa_family_t     sin_family;      //地址族(不只为IPV4设计)
	uint16_t        sin_port;        //16位TCP/UDP端口号
	struct in_addr  sin_addr;        //32位IP地址
	char            sin_zero[8];     //不使用
};

struct in_addr
{
	in_addr_t       s_addr;          //32位IPv4地址
};

在这里插入图片描述

2.2 结构体sockaddr_in的成员分析

成员sin_family(地址族)
在这里插入图片描述

成员sin_port(16位TCP/UDP端口号)
网络字节序保存

成员sin_addr(32位IP地址)
网络字节序保存

成员sin_zero
使结构体sockaddr_in的大小与sockaddr结构体保持一致,必须填充为0

3 网络字节序与地址变换

3.1 字节序与网络字节序

3.1.1 大端序和小端序

大端序:高位字节存放到低位地址
小端序:高位字节存放到高位地址
在这里插入图片描述
在这里插入图片描述
3.1.2 主机字节序和网络字节序

主机字节序:代表CPU数据保存方式,在不同CPU中也各不相同
目前主流的Intel系列CPU以小端序方式保存数据
在这里插入图片描述
网络字节序:通过网络传输数据时约定统一方式,统一为大端序

3.1.3 字节序转换

帮助转换字节序的函数:

unsigned short htons(unsigned short);//把short型数据从主机字节序转化为网络字节序
unsigned short ntohs(unsigned short);
unsigned long htonl(unsigned long);
unsigned long ntohl(unsigned long);
unsigned short host_port = 0x1234;
unsigned short net_port;
unsigned long host_addr = 0x12345678;
unsigned long net_addr;

net_port = htons(host_port); //0x3412
net_addr = htonl(host_addr); //0x78563412

4 网络地址的初始化与分配

4.1 将字符串信息转换为网络字节序的整数型

4.1.1 inet_addr

#include <arpa/inet.h>
//成功时返回32位大端序整数型值,失败时返回INADDR_NONE
in_addr_t inet_addr(const char* string);

char* addr1 = "1.2.3.4";
char* addr2 = "1.2.3.256";

unsigned long conv_addr = inet_addr(addr1); //0x4030201
unsigned long conv_error_addr = inet_addr(addr2); //INADDR_NONE

4.1.2 inet_aton

#include <arpa/inet.h>

//string: 含有需转换的IP地址信息的字符串地址值
//addr: 将保存转换结果的in_addr结构体变量的地址值
//成功时返回1(true),失败时返回0(false)
int inet_aton(const char* string, struct in_addr* addr);

char* addr = "127.232.124.79";
struct sockaddr_in addr_inet;
if(inet_aton(addr, &addr_inet.sin_addr))
{
	printf("%#x\n", addr_inet.sin_addr.s_addr); //0x4f7ce87f
}

4.1.3 inet_ntoa

#include <arpa/inet.h>

//调用完该函数后,应立即将字符串信息复制到其他内存空间
//再次调用该函数,会覆盖之前保存的字符串信息
//成功时返回转换的字符串地址值,失败时返回-1
char* init_ntoa(struct in_addr adr);

struct sockaddr_in addr1, addr2;
char *str_ptr;
char str_arr[20];

addr1.sin_addr.s_addr = htonl(0x1020304);
addr2.sin_addr.s_addr = htonl(0x1010101);

str_ptr = inet_ntoa(addr1.sin_addr);
strcpy(str_arr, str_ptr);
printf("%s\n", str_ptr); //1.2.3.4

inet_ntoa(addr2.sin_addr);
printf("%s\n", str_ptr); //1.1.1.1
printf("%s\n", str_arr); //1.2.3.4

4.2 网络地址初始化

struct sockaddr_in addr;                      
//声明IP地址字符串                     
char* serv_ip = "221.217.168.13";             
//声明端口号字符串
char* serv_port = "9190";                     
//结构体变量addr的所有成员初始化为0(将sockaddr_in结构体的成员sin_zero初始化为0)
memset(&addr, 0, sizeof(addr));               
//指定地址族
addr.sin_family = AF_INET;                    
//基于字符串的IP地址初始化
addr.sin_addr.s_addr = inet_addr(serv_ip);
//基于字符串的端口号初始化
addr.sin_port = htons(atoi(serv_port));

4.3 客户端地址信息初始化

与服务器端的区别

  1. 调用函数不同:准备工作时,服务器端通过bind函数完成,客户端通过connect函数完成
  2. 调用函数前需准备的地址值类型不同:服务器端声明sockaddr_in结构体变量,将其初始化为赋予服务器端IP和套接字的端口号;客户端声明sockaddr_in结构体,并初始化为要与之连接的服务器端套接字的IP和端口号,然后调用connect函数

4.4 INADDR_ANY

struct sockaddr_in addr;                                  
//声明端口号字符串
char* serv_port = "9190";                     
//结构体变量addr的所有成员初始化为0(将sockaddr_in结构体的成员sin_zero初始化为0)
memset(&addr, 0, sizeof(addr));               
//指定地址族
addr.sin_family = AF_INET;                    
//利用常数INADDR_ANY分配服务器端的IP地址。利用这种方式可自动获取运行服务器端的计算机IP地址,不必亲自输入
addr.sin_addr.s_addr = htonl(INADDR_ANY);
//基于字符串的端口号初始化
addr.sin_port = htons(atoi(serv_port));

4.5 向套接字分配网络地址

#include<sys/socket.h>
//bind函数负责将初始化的地址信息分配给套接字
//sockfd:要分配地址信息(IP地址和端口号)的套接字文件描述符
//myaddr:存有地址信息的结构体变量地址值
//addlen:第二个结构体变量的长度
int bind(int sockfd, struct sockaddr* myaddr, socklen_t addrlen);

//常见套接字初始化过程
int serv_sock;
struct sockaddr_in serv_addr;                                  
char* serv_port = "9190"; 

//创建服务器端套接字(监听套接字)
serv_sock = socket(PF_INET, SOCK_STREAM, 0);

//地址信息初始化
memset(&serv_addr, 0, sizeof(addr));               
addr.sin_family = AF_INET;                    
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(atoi(serv_port));

//分配地址信息
bind(serv_sock, (struct sockaddr* )&serv_addr, sizeof(serv_addr));
......

5 基于Windows的实现

WSAStringToAddress&WSAAddressToString

  1. Winsock2增加的两个转换函数,功能上与inet_itoa和inet_addr完全相同
  2. 优点:支持多种协议,在IPv4和IPv6中均可适用
  3. 缺点:依赖于特定平台,降低兼容性
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值