嵌入式Linux系统编程学习之三十三网络相关概念


一、网络相关名词的概念

1.套接口

  套接口也叫“套接字”,是操作系统内核中的一个数据结构,它是网络中的节点进行相互通信的门户。它是网络进程的 ID。网络通信,归根结底还是进程间的通信(不同计算机上的进程间通信)。在网络中,每一个节点(计算机或路由)都有一个网络地址,也就是 IP 地址。两个进程通信时,首先要确定各自所在的网络节点的网络地址。
  但是,网络地址只能确定进程所在的计算机,而一台计算机上很可能同时运行着多个进程,所以仅凭网络地址还不能确定到底是和网络中的哪一个进程进行通信,因此套接口中还需要包括其他信息,也就是端口号(PORT)。
  在一台计算机中,一个端口号一次只能分配给一个进程,也就是说,在一台计算机中,端口号和进程之间是一一对应关系。所以,使用端口号和网络地址的组合可以唯一地确定整个网络中的一个网络进程。
  例如,网络中某一台计算机的 IP 地址为 10.92.20.160,操作系统分配给计算机中某一个应用程序进程的端口号为 1500,则此时 10.92.20.160 1500 就构成了一个套接口。

2.端口号

  在网络技术中,端口大致有两种意思:一是物理意义上的端口,如集线器、交换机、路由器等用于连接其他网络设备的接口。二是指 TCP/IP 协议中的端口,端口号的范围为 0-65535,一类是由互联网指派名字和号码,公司 ICANN 负责分配给一些常用的应用程序固定使用的“周知的端口”,其值一般为 0-1023 。例如,HTTP 的端口号是 80,FTP 为 21,SSH 为 22,TELNET 为 23 等;还有一类是用户自己定义的,通常是大于 1024 的整型值。

3.IP 地址

  通常用户在表达 IP 地址时采用的是点分十进制表示的数值(或是冒号分开的十进制 IPv6 地址),而在通常使用的 Socket 编程中使用的则是二进制值,这就需要将这两个数值进行转换。
  IPv4 地址:32bit,4 字节,通常采用点分十进制记法。例如,对于 10000000 00001011 00000011 00011111,点分十进制表示为:128.11.3.31。
  IP 地址的分类:
在这里插入图片描述
  特殊的 IP 地址:
在这里插入图片描述

二、Socket 概念

  Linux 中的网络编程是通过 Socket 接口来进行的。Socket 是一种特殊的 I/O 接口,也是一种文件描述符。它是一种常用的进程之间的通信机制,通过它不仅能实现本地机器上的进程之间的通信,而且通过网络能够在不同机器上的进程之间进行通信。
  每一个 Socket 都用一个半相关描述 {协议、本地地址、本地端口} 来表示;一个完整的套接字则用一个相关描述 {协议、本地地址、本地端口、远程地址、远程端口} 来表示。Socket 也有一个类似于打开文件的函数调用,该函数返回一个整型的 Socket 描述符,随后的连接建立、数据传输等操作都是通过 Socket 来实现的。

三、Socket 类型

  (1)流式 Socket (SOCK_STREAM) 用于 TCP 通信
  流式套接字提供可靠的、面向连接的通信流;它使用 TCP 协议,从而保证了数据传输的正确性和顺序性。
  (2)数据报 Socket (SOCK_DGRAM) 用于 UDP 通信
  数据报套接字定义了一种无连接的服务,数据通过互相独立的报文进行传输,是无序的,并且不保证是可靠、无差错的。它使用数据报协议——UDP。
  (3)原始 Socket (SOCK_RAW) 用于新的网络协议实现的测试等
  原始套接字允许对底层协议如 IP 或 ICMP 进行直接访问,它功能强大但使用较为不便,主要用于一些协议的开发。

四、Socket 的信息数据结构

  Socket 的信息数据结构:

	struct sockaddr
	{
		unsigned short sa_family;	/*地址族*/
		char sa_data[14];			/*14字节的协议地址,包含该Socket的IP地址和端口号*/
	};
	struct sockaddr_in
	{
		short int sa_family;		/*地址族*/
		unsigned short int sin_port;/*端口号*/
		struct in_addr sin_addr;	/*IP地址*/
		unsigned char sin_zero[8];	/*填充0以保持与struct sockaddr同样大小*/
	};
	struct in_addr
	{
		unsigned long int s_addr;	/*32位IPv4地址,网络字节序*/
	};
	#include <netinet/in.h>			/*头文件*/
	sa_family:AF_INET	表示IPv4协议,
			  AF_INET6	表示IPv6协议

五、数据存储优先顺序的转换

  计算机数据存储有两种字节的优先顺序:高位字节优先(称为大端模式)和低位字节优先(称为小端模式)。内存的低地址存储数据的低字节,高地址存储数据的高字节的方式叫小端模式。内存的高地址存储数据的低字节,低地址存储数据的高字节的方式称为大端模式。
  示例:
  对于内存中存放的数字 0x12345678 来说,如果是采用大端模式存放的,则其真实的数是 0x12345678;如果是采用小端模式存放的,则其真实的数是 0x78563412。

  如果称某个系统所采用的字节序为主机字节序,则它可能是小端模式的,也可能是大端模式的。而端口号和 IP 地址都是以网络字节序存储的,不是主机字节序,网络字节序都是大端模式。要把主机字节序和网络字节序相互对应起来,需要对这两个字节存储优先顺序进行相互转化。这里用到 4 个函数:htons、ntohs、htonl 和 ntohl,这 4 个函数分别实现网络字节序和主机字节序的转化,这里的 h 代表 host,n 代表 network,s 代表 short,l 代表 long。通常 16 位的 IP 端口号用 s 代表,而 IP 地址用 l 代表。
  函数原型:
在这里插入图片描述

六、地址转格式转化

  通常用户在表达地址时采用的是点分十进制表示的数值(或者是为冒号分开的十进制 IPv6 地址),而在通常使用的 Socket 编程中使用的则是 32 位的网络字节序的二进制值,这就需要将这两个数值进行转换。这里在 IPv4 中用到的函数有 inet_aton、inet_addr 和 inet_ntoa,而 IPv4 和 IPv6 兼容的函数有 inet_pton 和 inet_ntop。

  IPv4 的函数原型为:

	#include <sys/socket.h>
	#include <netinet/in.h>
	#include <arpa/inet.h>
	int inet_aton(const char *straddr, struct in_addr *addrptr);
	char *inet_ntoa(struct in_addr inaddr);
	in_addr_t inet_addr(const char *straddr);

 函数 inet_aton:将点分十进制数的 IP 地址转换成网络字节序的 32 位二进制数值。
  参数 straddr:存放输入的点分十进制数 IP 地址字符串。
  参数 addrptr:传出参数,保存网络字节序的32位二进制数值。
  返回值:成功,则返回 1;失败,返回 0。
 函数 inet_ntoa:将网络字节序的 32 位二进制数值转换为点分十进制的 IP 地址。
 函数 inet_addr:功能与 inet_aton 相同,但是结果传递的方式不同。inet_addr 函数若成功,则返回 32 位二进制的网络字节序地址。

  IPv4 和 IPv6 的函数原型为:

	#include <arpa/inet.h>
	int inet_pton(int family, const char *src, void *dst);
	const char *inet_ntop(int family, const void *src, char *dst, socklen_t len);

 函数 inet_pton 跟 inet_aton 实现的功能类似,只是多了 family 参数,该参数指定为 AF_INET,表示是 IPv4 协议,如果是 AF_INET6,表示是 IPv6 协议。
 函数 inet_ntop 跟 inet_ntoa 类似,其中 len 表示转换之后的长度(字符串的长度)。

七、名字地址转化

  通常,人们在使用过程中都不愿意记忆冗长的 IP 地址,尤其到 IPv6 时,地址长度高达 128 位,这时就更加不可能一次性记忆那么长的 IP 地址了。因此,使用主机名或域名会是很好的选择。
  众所周知,百度的域名为:www.baidu.com ,而这个域名其实对应了一个百度公司的 IP 地址,那么百度公司的 IP 地址是多少呢?我们可以利用 ping www.baidu.com 命令来得到百度公司的 IP 地址。

  在 Linux 中,有一些函数可以实现主机名和地址的转化,最常见的有 gethostbyname 函数、gethostbyaddr 函数等,它们都可以实现 IPv4 和 IPv6 的地址和主机名之间的转化。其中 gethostbyname 函数为将主机名转化为 IP 地址,gethostbyaddr 函数则是逆操作,将 IP 地址转化为主机名。
  函数原型为:

	#include <netdb.h>
	struct hostent *gethostbyname(const char *hostname);
	struct hostent *gethostbyaddr(const char *addr, size_t len, int family);

  结构体为:

	struct hostent
	{
		char *h_name;		/*正式主机名*/
		char **h_aliases;	/*主机别名*/
		int h_addrtype;		/*主机IP地址类型IPv4为AF_INET*/
		int h_length;		/*主机IP地址字节长度,对于IPv4是4字节,即32位*/
		char **h_addr_list;	/*主机的IP地址*/
	}

 函数 gethostbyname:用于将域名 (www.baidu.com) 或主机名转化为 IP 地址。
  参数 hostname 指向存放域名或主机名的字符串。
 函数 gethostbyaddr:用于将 IP 地址转化为域名或主机名。
  参数 addr 是一个 IP 地址,此时这个 IP 地址不是普通的字符串,而要通过函数 inet_ntoa 转换。
  参数 len 为 IP 地址的长度,AF_INET 为 4 。
  参数 family 可用 AF_INET : IPv4 或 AF_INET6 : IPv6 。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值