源IP地址和目的IP地址
- 在ip数据包头部中,有两个ip地址,分别叫做源ip地址和目的ip地址。
- 源ip地址:发送数据包的那个电脑的ip地址。
- 目的ip地址:想要发送到的那个电脑的ip地址。
端口号port
- 在一台主机上标识一个进程。用来识别同一台计算机中进行通信的不同应用程序。也被称为程序地址。
- 一个端口只能被一个进程占用。
- 一个进程可以占用多个端口。
- 每条数据都会包含src port和dest port,标识这个数据从哪个进程到哪个进程。
- 一条数据中包含sip、sport、dip、dport、proto(传输层协议)。(五元组,标识一条通信),五个中只要有一个不同,就认为是不同通信。
- uint16_t类型数据(0~65535)。
- 0~1023:知名端口号,HTTP、FTP、SSH等这些广为使用的应用层协议,它们的端口号是固定的。不建议用户使用。
- 1024~65535:操作系统动态分配的端口号。客户端程序的端口号,就是由操作系统从这个范围分配的。
- 一些知名的端口号。有些服务器是非常常用的,为了使用方便,人们约定一些常用的服务器,都是用以下这些固定的端口号:
- SSH服务器:22号端口。
- FTP服务器:21号端口。
- Telnet服务器:23号端口。
- HTTP服务器:80号端口。
- HTTPS服务器:443号端口。
可以使用下述命令查看端口号。
[sss@aliyun ~]$ vim /etc/services
UDP协议
- UDP是不具有可靠性的数据报协议。细微的处理需要交给上层的应用去完成。UDP可以保证发送消息的大小,但是却不能保证消息一定会到达。因此需要应用根据自己的需要进行重发处理。
- UDP不提供复杂的控制机制,利用IP提供面向无连接的通信服务。并且它是将应用程序发来的数据在收到的那一刻,立即按照原样发送到网络上的一种机制。
- 即使是出现网络拥堵的情况下,UDP也无法进行流量控制等避免网络拥塞的行为。
- 此外,传输途中即使出现丢包,UDP也不负责重发。甚至当出现包的到达顺序乱掉时也没有纠正的功能。如果需要这些细节控制,那么就需要交给应用程序去处理。
总结:
用户数据报协议(User Datagram Protocol)。是一种无连接、不可靠、面向数据报的传输协议。
- 面向数据报:每条数据有长度标识,整条发,整条收。传输不够灵活,但是不会存在粘包问题。
- 使用场景:音频、视频传输。对数据实时性要求比较高的场景。
TCP协议
- TCP是面向连接的、可靠的流协议。流就是指不间断的数据结构,可以把它想象成排水管道中的水流。当应用程序采用TCP发送消息时,虽然可以保证发送的顺序,但还是犹如没有任何间隔的数据流发送给接收端。
- TCP为提供可靠性传输,实行“顺序控制”或“重发控制”机制。此外还具备“流量控制”、“拥塞控制”、提高网络利用率等众多功能。
总结:
传输控制协议(Transmission Control Protocol)。是一种面向连接的、可靠的、面向字节流的传输协议。
- 面向连接:通信之前先建立连接,确保双方在线。
- 可靠传输:在网络正常的情况下,数据不会丢失。
- 面向字节流:传输灵活,但是数据之间没有明显边界,存在粘包问题。
- 使用场景:传输文件。对安全性要求比较高的场景。
- TCP为了实现可靠传输牺牲了部分传输性能。
网络字节序
- 字节序是指多字节数据在计算机内存中存储或者网络传输时各字节的存储顺序。
- 我们知道内存中的多字节数据相对于内存地址有大端和小端之分。比如:X86_64为小端、MIPS为大端、ARM芯片默认采用小端,但可以切换为大端。
- 问题来了,如果网络上通信的两台主机的字节序不同,就会造成数据的二义性,这显然是不合适的。
- 为了解决通信双方主机字节序不同的问题,可以订立一个标准,网络通信中都使用该标准,于是就有了网络字节序。网络通信中,都使用网络字节序,网络字节序实际上是一个大端字节序。
接口介绍:
头文件:arpa/inet.h
功能:主机字节序转换为网络字节序:
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
参数:
hostlong/hostshort:主机字节序数据。
返回值:网络字节序数据。
头文件:arpa/inet.h
功能:网络字节序转换为主机字节序:
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
参数:
netlong/netshort:网络字节序数据。
返回值:主机字节序数据。
套接字地址结构
大多数套接字函数都需要一个指向套接字地址结构的指针作为参数。每个协议族都定义自己的套接字地址结构。这些结构的名字均已sockaddr_开头,并以对应每个协议族的唯一后缀结尾。
IPv4地址结构:
IPv4套接字地址结构通常也称为“网际套接字地址结构”,它以sockaddr_in命名,定义在<netinet/in.h>头文件中。我们使用下面命令打开头文件看一下sockaddr_in的结构:
[sss@aliyun ~]$ vim /usr/include/netinet/in.h
我们来看一下第一个结构(__SOCKADDR_COMMON)是什么,这应该是一个宏,我们去找一下它的定义:
找到这个宏所在文件后,我们使用下面命令打开看一下:
[sss@aliyun ~]$ vim /usr/include/bits/sockaddr.h
我们对__SOCKADDR_COMMON(sin_)宏展开就是:
sa_family_t sin_family;
下面,我们使用下面命令来看一下in_port_t和in_addr的类型:
[sss@aliyun ~]$ vim /usr/include/netinet/in.h
POSIX规范只需要这个结构中的3个字段:sin_family、sin_addr和sin_port。
- sin_family:AF_INET(IPv4)或AF_INET6(IPv6)。
- sin_addr:IP地址。
- sin_port:端口号。
通用套接字地址结构:
- 当作为一个参数传递进任何套接字函数时,套接字地址结构总是以引用形式(也就是以指向该结构的指针)来传递。然而以这样的指针作为参数之一的任何套接字函数必须处理来自所支持的任何协议族的套接字地址结构。
- 在如何声明所传递指针的数据类型上存在一个问题。有了ANSI C后解决办法很简单:void*是通用的指针类型。然而套接字函数是在ANSI C之前定义的,在1982年采取的办法是在<sys/socket.h>头文件中定义一个通用的套接字地址结构。
我们使用下面命令打开头文件看一下:
[sss@aliyun ~]$ vim /usr/include/sys/socket.h
于是套接字函数被定义为以指向某个通用套接字地址结构的一个指针作为其参数之一,正如bind函数的ANSI C函数原型所示:
int bind(int, struct sockaddr*, socklen_t);
这就要求对这些函数的任何调用都必须要将指向特定协议的套接字地址结构的指针进行类型强制转化,变成指向某个通用套接字地址结构的指针,例如:
struct sockaddr_in serv;
bind(sockfd, (struct sockaddr*)&serv, sizeof(struct sockaddr_in));
如果我们省略了其中的类型强制转换部分"(struct sockaddr*)",并假设系统的头文件中有bind函数的一个ANSI C原型,那么C编译器就会产生这样的警告信息:“warning:passing arg 2 of ‘bind’ from incompatible pointer type.”(警告:把不兼容的指针类型传递给’bind’函数的第二个参数)。
从应用程序开发人员的观点看,这些通用套接字地址结构的唯一用途就是对指向特定协议的套接字地址结构的指针指向类型强制转换。
地址转换:
接口介绍:
头文件:arpa/inet.h
功能:点分十进制字符串转换为网络字节序二进制值(适用于IPv4)。
int inet_aton(const char *cp, struct in_addr *inp);
参数:
cp:点分十进制形式IP地址。
inp:输出型参数,保存转换得到的二进制值。
返回值:成功返回1,失败返回0。
头文件:arpa/inet.h
功能:二进制形式转换为点分十进制形式(适用于IPv4)。
char *inet_ntoa(struct in_addr in);
参数:
in:二进制形式IP地址。
返回值:点分十进制形式的IP地址。
注意:返回值所指向的字符串在静态区。
上面的两个接口不推荐使用,因为它们只适用于IPv4,推荐使用下面两个接口:
头文件:arpa/inet.h
功能:点分十进制字符串转换为网络字节序二进制值(适用于IPv4和IPv6)。
int inet_pton(int af, const char *src, void *dst);
参数:
af:IP协议类型,AF_INET或AF_INET6。
src:点分十进制IP地址。
dst:输出型参数,保存转换得到的二进制IP地址。
返回值:成功返回1,否则如果对于所指定的family而言输入的字符串不是有效的表达格式,返回0。
头文件:arpa/inet.h
功能:二进制型是转换为点分十进制形式(适用于IPv4和IPv6)。
const char *inet_ntop(
int af, const void *src, char *dst, socklen_t size
);
参数:
af:IP协议类型,AF_INET或AF_INET6。
src:二进制形式的IP地址。
dst:输出型参数,保存转换得到的点分十进制IP地址。
size:指定dst目标空间大小,避免移除;netinet/in.h中有两个宏,如下:
INET_ADDRSTRLEN:16
INET6_ADDRSTRLEN:46
返回值:成功,返回一个指向dst的指针;失败返回空,errno被设置。