优于别人,并不高贵,
真正的高贵应该是优于过去的自己。
--- 海明威 ---
两台主机进行通信时,需要进行两个通信协议栈,分别进行封装信息和解包信息。网络层包装协议时,会加入当前IP地址和目标IP地址,然后再次封装到数据链路层进行通信。路由器得到数据包后进行一层的解包,通过目标IP地址进行判断:
图示:
我们讲个故事来理解这两个地址:
最近黑神话悟空非常热门,我们就以西游记进行举例: 唐僧师徒四人从东土大唐长安城出发,前往西天灵山取经。中间会经过若干城池:车迟国,女儿国,火焰山…
这里反复出现的“我从东土大唐而来 ,前往西天灵山取经”就是IP地址 而每一次的起始站是MAC地址,每个国相当于路由器,通过唐僧的目标,选择最合适的下一站
所以:
将西游记的落实到实际中就是这样的一张图!!!
MAC地址只能在局域网内保证唯一性,因为MAC地址不会超出局域网。IP地址才是用来保证网络中的主机的唯一性!
数据传输的目的不是仅仅是到达主机,而是让用户来进行使用使用数据!也就是要在进程中来使用数据! 我们看B站,从服务器主机传输来的视频数据,最终是要在我们手机上的B站进程中进行播放的!
数据传输到主机不是目的, 而是手段。 到达主机内部, 在交给主机内的进程,才是目的!
但是,数据包通过IP地址找到对应主机,之后是如何从大量进程中找到目标进程呢?通过端口号(PORT),IP地址中的端口号开放给进程使用,用来标识目标进程!
也就是数据包解包到应用层时通过端口号找到对应进程!
这里具有疑问了?为什么不直接使用进程的pid,这不也是唯一的吗? 因为如果使用pid进行网络标识,那么网络与系统就产生了强耦合!如果系统层出现问题导致PID变化,就会导致网络层也出现问题!这可不行,我们要做的是弱耦合!所以单独使用端口号来进行标识!
所以:socket = IP + PORT == 网络中唯一的进程
。
这样也就得到了网络通信的本质:不同主机的进程间通信!!!这两个进程看到的公共资源就是网络!!!这种通信就叫socket
通信!
端口号分为两部分,一部分是系统内部只有,其余的才允许用户使用!
传输层协议(TCP 和 UDP)的数据段中有两个端口号, 分别叫做源端口号和目的端口号.就是在描述 “数据是谁发的, 要发给谁”;
用户是在应用层进行操控,用户想要进行通信就需要使用传输层。传输层之下的层,用户不需要考虑,操作系统会帮我们完成!我们只需要理解使用传输层的系统调用即可!
传输层有UDP协议和TCP协议:
注意可靠听起来比不可靠更好,但是可靠的协议底层实现就肯定更加复杂!所以可不可靠不是优缺点,而是一种特性!以后我们再来进行详细讲解!
在学习C语言时,了解过两种机器:大端机和小端机,这两种的储存方式是不一样的:
如果两台主机不是一样的!那么主机如何读取另一个数据发送过来的数据包呢?因为根本无法判断对方是大端机还是小端机,那么就不可能正常的读取数据!
所以:TCP/IP 协议规定,网络数据流应采用大端字节序,即低地址高字节.
因此,网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址.这样按照大端字节序更加顺畅!传输数据时,如果是小端机就通过系统调用将数据转换成大端,否则就忽略,直接发送就可以!
这里有个很有意思的地方,因为设计者认识到网络通信的本质是进程间通信,所以设计者就希望通过一套接口完成网络通信和系统通信!
通过不同的标志为可以选择要进行何种方式的通信!其中的系统通信非常像命名管道的通信方式!那么通过一套公共接口实现了两种不同的通信方式,这不就是多态吗!通过结构体的第一个字段判断如何读取下面的字段,判断出应该采用什么方式进行通信。
下面是socket API:
// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);
// 绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address,
socklen_t address_len);
// 开始监听 socket (TCP, 服务器)
int listen(int socket, int backlog);
// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address,
socklen_t* address_len);
// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
socket API 可以都用 struct sockaddr *
类型表示, 在使用的时候需要强制转化成sockaddr_in
或sockaddr_un
; 这样的好处是程序的通用性!
之后我们就来学习UDP套接字编程!开始网络编程的第一步!