TCP IP网络编程(三) 地址族与数据序列

文章目录

    • 分配给套接字的IP地址与端口号
      • 网络地址
      • 网络地址分类与主机地址边界
    • 地址信息的表示
      • 表示 IPv4地址的结构体
      • 结构体sockaddr_in 的成员分析
    • 网络字节序与地址变换
      • 字节序与网络字节序
      • 字节序转换
    • 网络地址的初始化与分配
      • 将字符串信息转换为网络字节序的整数型
      • 网络地址初始化
      • INADDR_ANY
      • 向套接字分配网络地址
    • 总结

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

网络地址

IP地址分为两类:

  • IPv4 4字节地址族
  • IPv6 16字节地址族

IPv4和IPv6的差别主要是表示在IP地址所用的字节数,目前通用的地址族为IPv4,而IPv6是为了应对IP地址耗尽的问题而提出的标准,目前主要还是使用IPv4

IPv4标准的4字节IP地址分为网络地址和主机地址,且分为A、B、C、D等类型。

A类		网络ID 主机ID 主机ID 主机ID
B类		网络ID 网络ID 主机ID 主机ID
C类		网络ID 网络ID 网络ID 主机ID
D类		网络ID 网络ID 网络ID 网络ID (多播IP地址)

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

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

  • A类地址的首字节范围:0-127
  • B类地址的首字节范围:128-191
  • C类地址的首字节范围:191-223

还有另一种表述方式

  • A类地址的首位以0开始
  • B类地址的首位以10开始
  • C类地址的首位以110开始

地址信息的表示

表示 IPv4地址的结构体

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

该结构体中提到了另外一个结构体 in_addr 定义为:

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

上述两个结构体包含一些数据类型,uint16_t、in_addr_t等类型可以参考POSIX。POSIX是为UNIX系统操作设立的标准

POSIX中定义的数据类型

数据类型名称数据类型说明声明头文件
int8_tsigned 8-bit intsys/types.h
uint8_tunsigned 8-bit intsys/types.h
int16_tsigned 16-bit intsys/types.h
uint16_tunsigned 16-bit intsys/types.h
int32_tsigned 32-bit intsys/types.h
uint32_tunsigned 32-bit intsys/types.h
sa_family_t地址族(address family)sys/socket.h
socklen_t长度(length of struct)sys/socket.h
in_addr_tIP地址,,uint32_tnetinet/in.h
in_port_t端口号,uint16_tnetinet/in.h

结构体sockaddr_in 的成员分析

成员 sin_family

​ 每种协议族适用的协议族均不同,比如IPv4使用4字节地址族,IPv6使用16字节地址族

地址族

地址族含义
AF_INETIPv4网络协议中使用的地址族
AF_INET6IPv6网络协议中使用的地址族
AF_LOCAL本地通信中采用的UNIX协议的地址族

AF_LOCAL是为了说明具有多种地址族而添加的

成员sin_port

该成员保存16位端口号,重点在于,它以网络字节序保存

成员sin_addr

该成员保存32位IP地址信息,也以网络字节序保存,同时管擦结构体in_addr

成员sin_zero

无特殊含义,只是为使结构体sockaddr_in的大小与sockaddr结构体保存一致而插入的成员,必须填充为0

网络字节序与地址变换

字节序与网络字节序

CPU保存数据的方式有两种,意味着CPU解析数据的方式也分为两种

  • 大端序:高位字节存放到低位地址
  • 小端序:高位字节存放到高位地址

在通过网络传输数据时约定统一方式,这种约定称为网络字节序,统一为大端序

总结来说,将数据数组转发成大端序格式再进行网络传输,所有计算机接收数据时应该识别该数据是网络字节序格式,小端序系统传输时应该转化成大端序排列方式

字节序转换

填充结构体 sockadr_in 前将数据转换成网络字节序,介绍帮助转换字节序的函数

unsigned short htons(unsigned short)
unsigned short ntohs(unsigned short)
unsigned long htonl(unsigned short)
unsined long ntohl(unsigned long)

函数名的含义

  • htons中的 h 代表主机(host)字节序
  • htons中的 n 代表网络(network)字节序
  • htons中的 s 指的是short
  • htons中的 l 指的是long(Linux中long类型占用4个字节)
#include <stdio.h>
#include <arpa/inet.h>int main(int argc, char *argv[])
{unsigned short host_port=0x1234;unsigned short net_port;unsigned long host_addr=0x12345678;unsigned long net_addr;net_port=htons(host_port);net_addr=htonl(host_addr);printf("Host ordered port: %#x \n", host_port);printf("Network ordered port: %#x \n", net_port);printf("Host ordered address: %#lx \n", host_addr);printf("Network ordered address: %#lx \n", net_addr);return 0;
}

下面这就是在小端序CPU中运行的结果。如果在大端序CPU中运行,则变量值不会改变。大部分朋友都会得到类似的运行结果,因为Intel和AMD系列的CPU都采用小端序标准。

gcc endian_conv.c -o conv
./conv
输出:
Host ordered port : 0x1234
Network ordered port : 0x3412
Hostordered address : 0x12345678
Network ordered address : 0x78563412

网络地址的初始化与分配

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

​ 对于IP地址的表示,我们熟悉的是点分十进制表示法而非整数型数据表示法。幸运的是,有函数会帮我们将字符串形式的IP地址转换成32位整数型数据,在转换类型的同时进行网络字节序转换。

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

该函数的调用过程

#include <stdio.h>
#include <arpa/inet.h>int main(int argc, char *argv[])
{char *addr1="127.212.124.78";char *addr2="127.212.124.256";unsigned long conv_addr=inet_addr(addr1);if(conv_addr==INADDR_NONE)printf("Error occured! \n");elseprintf("Network ordered integer addr: %#lx \n", conv_addr);conv_addr=inet_addr(addr2);if(conv_addr==INADDR_NONE)printf("Error occureded \n");elseprintf("Network ordered integer addr: %#lx \n\n", conv_addr);return 0;
}

从结果可以看出,inet_addr函数不仅可以把IP地址转成32位整数型,而且可以检测无效的IP地址

inet_aton函数与inet_addr函数在功能上完全相同,也将字符串形式IP地址转换为32位网络字节序整数并返回。不同的是该函数利用了in_addr结构体,且使用频率更高。

#include<arpa/inet.h>
int inet_aton(const char * string, struct in_addr * addr);成功时返回1,失败时返回0参数1:string,含有需转换的IP地址信息的字符串地址值。参数2:addr,将保存转换结果的in_addr结构体变量的地址值。

该函数的调用过程

#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
void error_handling(char *message);int main(int argc, char *argv[])
{char *addr="127.232.124.79";struct sockaddr_in addr_inet;if(!inet_aton(addr, &addr_inet.sin_addr))error_handling("Conversion error");elseprintf("Network ordered integer addr: %#x \n", addr_inet.sin_addr.s_addr);return 0;
}void error_handling(char *message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}

调用inet_addr函数,返回转换后的IP地址信息还需保存到sockaddr_in结构体中声明的in_addr结构体变量。而inet_aton函数则不需此过程,因为该函数会自动把结果存入该结构体变量。

还有一个函数,与 inet_aton() 正好相反,它可以把网络字节序整数型IP地址转换成我们熟悉的字符串形式

#include <arpa/inet.h>
char *inet_ntoa(struct in_addr adr);成功返回转换的字符串地址值,失败返回-1
  • 该函数将通过参数传入的整数型IP地址转换为字符串格式并返回

  • 但要小心,返回值为 char 指针,返回字符串地址意味着字符串已经保存在内存空间,但是该函数未向程序员要求分配内存,而是在内部申请了内存保存了字符串。也就是说调用了该函数候要立即把信息复制到其他内存空间。因为,若再次调用inet_ntoa函数,则有可能覆盖之前保存的字符串信息

  • 总之,再次调用 inet_ntoa 函数前返回的字符串地址是有效的。若需要长期保存,则应该将字符串复制到其他内存空间

给出该函数的调用示例

#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>int main(int argc, char *argv[])
{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("Dotted-Decimal notation1: %s \n", str_ptr);inet_ntoa(addr2.sin_addr);printf("Dotted-Decimal notation2: %s \n", str_ptr);printf("Dotted-Decimal notation3: %s \n", str_arr);return 0;
}

网络地址初始化

服务器端套接字创建过程中常见的网络地址信息初始化方法:

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

INADDR_ANY

初始化地址信息

struct sockaddr_in addr;
char * serv_port = "9190";
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
add.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(atoi(serv_port));

与之前方式最大的区别在于,利用INADDR_ANY分配服务器端的IP地址若采用这种方式,则可自动获取运行服务器端的计算机IP地址,不必亲自输入。而且,若同一计算机中已分配多个IP地址,则只要端口号一致,就可以从不同IP地址接收数据。

向套接字分配网络地址

前面了解了sockaddr_in结构体的初始化方法,接下来就把初始化的地址信息分配给套接字。bind函数负责这项操作

#include<sys/socket.h>
int bind(int sockfd, struct sockaddr * myaddr, socklen_t addrlen);成功时返回0,失败时返回-1。参数1:sockfd,要分配地址信息(IP地址和端口号)的套接字文件描述符参数2:myaddr,存有地址信息的结构体变量地址值。参数3:addrlen,第二个结构体变量的长度。

如果此函数调用成功,则将第二个参数指定的地址信息分配给第一个参数中的相应套接字。

int serv_sock;
struct sockaddr_in serv_addr;
char * srev_port = "9190";/* 创建服务器端套接字(监听套接字)*/
serv_sock = socket(PF_INET, SOCK_STREAM, 0);/* 地址信息初始化 */
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(atoi(serv_port));/* 分配地址信息 */
bind(serv_sock, (struct sockaddr * )&serv_addr, sizeof(serv_addr));

服务器端代码结构如上

总结

这是《TCP/IP网络编程》专栏的第三篇文章,欢迎各位读者订阅!

更多资料点击 GitHub 欢迎各位读者去Star

⭐学术交流群Q 754410389 持续更新中~~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/71808.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

3次多项式轨迹规划(PLC SCL代码)

机器人、运动控制等常用的轨迹规划有三次多项式、五次多项式、梯形速度规划,S型速度规划,今天我们主要介绍三次多项式轨迹规划,有关T型和S型轨迹规划大家可以查看下面文章博客,这里不再赘述, 梯形轨迹规划 梯形速度曲线轨迹规划(速度前馈+PID、SCL+ ST代码)_RXXW_Dor的博…

【2023年11月第四版教材】第11章《成本管理》(第1部分)

第11章《成本管理》&#xff08;第1部分&#xff09; 1 章节说明2 管理基础3 管理过程3.1 管理ITTO汇总★★★ 1 章节说明 【本章分值预测】大部分内容不变&#xff0c;细节有一些变化&#xff0c;预计选择题考3-4分&#xff0c;案例和论文都有可能考&#xff1b;是需要重点学…

雅思写作 三小时浓缩学习顾家北 笔记总结(五)

目录 饥饿网100句翻译练习 Many girls are unwilling to seek employment in male-dominated industries. Many girls are not willing to find jobs in male-dominated industries. The main function of schools is to impart knowledge to the next generation. The ar…

筛选图片,写JSON文件和复制

筛选图片&#xff0c;写JSON文件和复制 筛选图片&#xff0c;写JSON文件筛选图片复制 筛选图片&#xff0c;写JSON文件 # coding: utf-8 from PIL import Image, ImageDraw, ImageFont import os import shutil import cv2 as cv import numpy as np import jsondef zh_ch(str…

统计子岛屿

统计子岛屿 关于岛屿的相似题目&#xff1a; 岛屿数量 – 二维矩阵的dfs算法封闭岛屿数量 – 二维矩阵的dfs算法统计封闭岛屿的数目统计子岛屿不同岛屿的数量 class CountSubIslands:"""floodFill 算法1254. 统计子岛屿https://leetcode.cn/problems/count-su…

Kafka3.0.0版本——增加副本因子

目录 一、服务器信息二、启动zookeeper和kafka集群2.1、先启动zookeeper集群2.2、再启动kafka集群 三、增加副本因子3.1、增加副本因子的概述3.2、增加副本因子的示例3.2.1、创建topic(主题)3.2.2、手动增加副本存储 一、服务器信息 四台服务器 原始服务器名称原始服务器ip节点…

PostgreSQL PG15 新功能 PG_WALINSPECT

开头还是介绍一下群&#xff0c;如果感兴趣PolarDB ,MongoDB ,MySQL ,PostgreSQL ,Redis &#xff0c;Oracle ,Oceanbase 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请加微信号 liuaustin3 &#xff08;…

Android离线文字识别-tesseract4android调用

Android在线文字识别可以调阿里云的接口Android文字识别-阿里云OCR调用__花花的博客-CSDN博客 需要离线文字识别的话&#xff0c;可以调tesseract4android。个人测试效果不是特别理想&#xff0c;但是速度真的很快&#xff0c;VIVO S10后摄照片&#xff0c;80ms内识别完成。现…

qemu-system-x86_64 命令创建虚拟机,报gtk initialization failed的

因为是ssh命令行启动&#xff0c;增加--nographic # /opt/debug/bin/qemu-system-aarch64 -machine virt-6.2 -qmp tcp:localhost:1238,server,nowait --nographic configure accelerator virt-6.2 start machine init start cpu init start add rom file: virtio-net-pci…

【6】uniform颜色写入

之前的Basic.shader: #shader vertex #version 330 corelayout(location 0) in vec4 position;void main() {gl_Position position; };#shader fragment #version 330 corelayout(location 0) out vec4 color;void main() {color vec4(1.0, 0.0, 0.0, 1.0); };这里color …

【免费模板】2023数学建模国赛word+latex模板免费分享

无需转发 免费获取2023国赛模板&#xff0c;获取方式见文末 模板文件预览如下&#xff1a; 模板参考格式如下&#xff1a; &#xff08;题目&#xff09;XXXXXX 摘 要&#xff1a; 开头段&#xff1a;需要充分概括论文内容&#xff0c;一般两到三句话即可&#xff0c;长度控…

idea的git入门

&#xff08;1&#xff09;安装好git之后&#xff0c;在idea的设置里面&#xff0c;按照下面三步&#xff0c;配置git &#xff08;2&#xff09;创建本地git仓库 选择本地仓库的根目录&#xff0c;点击ok &#xff08;3&#xff09;创建成功之后&#xff0c;会发现文件名称都变…

Notepad++ 的安装及配置

由于电脑重装了Win11系统&#xff0c;干脆重头开始&#xff0c;重新安装每一个软件~~~ 很多博客或者博主都会推荐notepad的官网&#xff1a;https://notepad-plus-plus.org/ 但大家亲自点开就会发现是无响应&#xff0c;如下图 同时&#xff0c;也会有很多博主直接给网盘地址…

垃圾回收 - 分代垃圾回收

分代垃圾回收在对象中导入了“年龄”的概念&#xff0c;通过优先回收容易成为垃圾的对象&#xff0c;提高垃圾回收的效率。 1、新生代对象和老年代对象 分代垃圾回收中把对象分类成几代&#xff0c;针对不同的代使用不同的 GC 算法&#xff0c;我们把刚生成的对象称为新生代对…

三维数字沙盘电子沙盘虚拟现实模拟推演大数据人工智能开发教程第15课

三维数字沙盘电子沙盘虚拟现实模拟推演大数据人工智能开发教程第15课 现在不管什么GIS平台首先要解决的就是数据来源问题&#xff0c;因为没有数据的GIS就是一个空壳&#xff0c;下面我就目前一些主流的数据获取 方式了解做如下之我见&#xff08;主要针对互联网上的一些卫星…

java+ssm+mysql电梯管理系统

项目介绍&#xff1a; 使用javassmmysql开发的电梯管理系统&#xff0c;系统包含管理员&#xff0c;监管员、安全员、维保员角色&#xff0c;功能如下&#xff1a; 管理员&#xff1a;系统用户管理&#xff08;监管员、安全员、维保员&#xff09;&#xff1b;系统公告&#…

Android签名查看

查看签名文件信息 第一种方法&#xff1a; 1.打开cmd&#xff0c;执行keytool -list -v -keystore xxx.keystore&#xff0c;效果如下图&#xff1a; 第二种方法: 1.打开cmd&#xff0c;执行 keytool -list -v -keystore xxxx.keystore -storepass 签名文件密码&#xff0…

Python调试学习资料

Python调试学习资料 python -m pdb example.py网络资源 Python代码调试的几种方法总结Python 程序如何高效地调试&#xff1f;Python Debugging With Pdbpdb — The Python DebuggerThe Python Debugger (pdb)Python Debugger with ExamplesHow to port Python 2 Code to Pyth…

功率信号源可以应用在哪些方面

功率信号源是一种能够产生一定功率的信号源&#xff0c;广泛应用于各个领域。下面将介绍功率信号源在电子、通信、工业和科研等方面的应用。 在电子行业中&#xff0c;功率信号源是一种重要的测试工具。它可以产生各种波形的信号&#xff0c;如正弦波、方波、脉冲波等&#xff…

【前端知识】前端加密算法(base64、md5、sha1、escape/unescape、AES/DES)

前端加密算法 一、base64加解密算法 简介&#xff1a;Base64算法使用64个字符&#xff08;A-Z、a-z、0-9、、/&#xff09;来表示二进制数据的64种可能性&#xff0c;将每3个字节的数据编码为4个可打印字符。如果字节数不是3的倍数&#xff0c;将会进行填充。 优点&#xff1…