
序言:上一篇博客我们讲解了计算机网络的基础,我们了解了计算机的诞生,局域网的简单概念,MAC地址和IP地址的区别,为什么需要这两种地址?,我们在上个博客都详细介绍了,这篇博客我们将先预备socket的相关知识,去实现网络通信。
如果没有看我上一篇博客的同学快去看吧!《计算机网络:网络基础》
一、端口号
1、为什么要有端口号?
上个博客我们说到,IP地址全网唯一(暂时这样理解,其实这个理解不对但我们刚开始学习这样理解简单一点),我们思考一个问题我们用网络传输数据的目的是把数据给这些主机就行了嘛?当然不是,数据是给我们人的,比如下载数据并不是给计算机的,聊天的数据是传给对方看的不是传给对方的计算机看的,那我们人是如何可以从计算机从获得网络传来的数据呢?答案是进程,操作系统内核将数据从底层硬件拿上来了以后将数据经过应用层处理交付给用户,换一句话来说,进程是不是我们人在操作系统中的代表,将数据交给进程就相当于人拿到了这个数据。
那下面我还有一个问题:计算机是如何知道数据交给哪一个进程的?换一句话来说,我们在Linux系统的学习我们知道在操作系统中我们同时运行很多进程,在遥远网络的对面的人或者说进程是如何知道数据发给我这台主机的这个进程的?
答案是端口号。
2、认识端口号
端口号是传输层协议的内容,在一台主机中每个需要通过网络通信的进程都有一个端口号这个端口号只会被这个进程使用其他进程不可以使用。
端口号是一个2字节16位的整数。
端口号用来标识一个进程,告诉操作系统,当前的这个数据需要交付给哪一个进程使用。
IP地址 + 端口号(port)可以标识全网的唯一的一个进程。
这里可能很多同学不理解,这是因为,上面我们解释了IP地址可以标识全网的唯一的主机,那端口号可以标识这台主机上唯一的一个进程那么这两个加起来不就可以表示全网内唯一主机上唯一进程吗? 可以。
结论:IP + port < == >主机的唯一进程
这里我们可以得出一个结论:
什么是网络通信?
其实就是两个主机上的两个进程通信,我们在系统部分学过进程间通信,我们说进程间通信的前提是两个进程看见同一份资源,那我们说网络通信是进程间通信,那这里的"共享资源"是什么?
答案是网络

一个端口号只能被一个进程占用,一个进程可以有多个端口号,但一个端口号只能标识一个进程。
这个问题的原因我们在后面的代码过程中我们再去解释。
下面这幅图可以让各位同学去更形象的理解它们之间的关系:


端口号的划分范围:
0 - 1023:知名端口号,HTTP ,FTP,SSH等这些广为流传的应用层协议,它们的端口号是固定的。
这里可能很多同学不理解了,如果多个进程用同一个上层协议的话,比如HTTP协议是80号端口,那么这样的话不就导致一个进程无法正常获得端口号吗?
0-1023的作用是为了一些系统的核心进程,一般0 - 1023端口需要root权限去绑定,普通用户是无法去绑定的,比如在公司的机器上跑着一个软件的服务器端这个进程就是核心软件,为了避免其他的软件干扰它才会使用0 - 1023号端口。
1024 - 65535:操作系统动态分配的端口号,客户端程序的端口号,就是有操作系统从这个范围内去分配的(这个等到我们编码的时候就会解释原因,这里解释不好说清楚)。
理解"端口号"和"进程PID"
在我们系统的部分我们说过pid可以唯一标识一个进程,那上面我们说过我们的端口号也可以唯一标识一个进程,那么这两个的关系是什么样的?
1、一个进程可以绑定多个端口号,而一个端口号只可以被一个进程绑定。
2、进程pid是系统内核的概念,技术上也是具有唯一性的,那为什么在网络层不去使用pid去标识唯一性,还需要搞出来一个端口号来标识进程唯一性?这是因为这会让系统进程的管理和网络形成强耦合,这不利于我们代码的设计目标 " 低耦合,高内聚 " 的目的,所以在实现的时候并没有使用pid去代替端口号。
二、理解Socket
我们把端口号 (port)+ 主机地址(IP地址) 叫做套接字(socket),socket的中文翻译是插座的意思,那port + ip不就相当于在网络世界定位出一个唯一的插座给我们去传递信息吗?我们可以这样形象的理解一下。
socket = ip + port;
在网络通信的时候,本质不就是系统中两个进程代表我们人去进行通信,所以{srcIP , srcPort , detIP , detPort}被我们称为在网络通信中的四元组,四元组可以唯一标识在网络中通信的唯二的两个进程。
三、理解网络字节序
1、大小端
在我们之前学习C语言的时候我们就说过大小端的问题,大端:高位字节存低地址处,低位字节存高地址处 ,小端:高位字节存高地址处,低位字节存低地址处。
下面一段代码可以检测电脑是大端还是小端:
cpp
#include <stdio.h>
int main() {
union {
short s; // 2字节整数
char c[2]; // 字符数组(存储字节)
} un;
un.s = 0x1234; // 赋值多字节数据
// 若低地址(c[0])是低位字节0x34,则为小端;若是高位字节0x12,则为大端
if (un.c[0] == 0x34 && un.c[1] == 0x12) {
printf("小端(Little-Endian)\n");
} else if (un.c[0] == 0x12 && un.c[1] == 0x34) {
printf("大端(Big-Endian)\n");
}
return 0;
}
2、网络字节序
1、什么的网络字节序?
简单一点来说 :网络字节序 == 大端序
为什么要有大端序?
因为网络是大家共享的,但我们的机器并不是只有一台,大家的机器有的是采用的是大端序,有的是小端序,如果大家不去使用同一的方式去传输数据那么一个大端机传数据给小端机,小端机看到的数据都是反的那么处理数据也是错误的,所以我们要去采用同一种方法在网络中交流这就是网络字节序。
2、关于网路字节序的API
cpp
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort)
uint32_t ntohl(uint32_t netlong);
uintl6_t ntohs(uintl6_t netshort);
这四个API可以将网络字节序变成本主机的字节序,或者将本机的字节序变成网络字节序,下面我们看一下如何去理解每个接口。
在函数名中的 h 代表着host(主机的意思),n代表着network(网络的意思),比如htonl这个函数名的意思就是将32位的长整数从本机的字节序变成网络字节序,htons这个函数名的意思就是将16位的短整型从本机的字节序变成网络字节序,ntohs这个函数名的意思就是将16位的短整型从网络字节序变成本机的字节序。
如果本机是大端机那这些函数对参数不做如何处理直接返回,如果本机是小端机那这些函数会将网络字节序转换成小端序返回或者将小端序转换成网络字节序再发送到网络中。
四、简单理解TCP和UDP
这里我们只要简单理解就行了后面我们会专门说这两种协议的。
TCP和UDP都是传输层的协议。

五、Socket相关结构体和API
上面我们说过网络通信就是进程间通信,我们在系统部分说过System V标准的进程间通信,现在我们学的Posix标准的网络通信,为了实现网络通信和本地通信,这就意味着socket会有多个种类,但socket的设计者不想去设计这么多繁多的接口,所以他设计了sockaddr这个结构体,下面我们来看一下理解一下他设计的精妙之处。


我们在上面说过socket的设计者为了socket实现本地通信和网络通信,他是如何去设计的?
答案是通过struct sockaddr这个公共的结构体实现的用一套API即实现网络通信和本地通信,如果我们先要使用网络通信我们将要使用struct sockaddr_in这个结构体,当我们需要调用接口传参的时候它的结构体都是sockaddr,无论你是本地通信(sockaddr_un)还是网络通信(sockaddr_in)都被强转成struct sockeaddr*类型,在函数内部再通过每个结构体的16位地址类型去判断是需要本地通信还是网络通信。
看到这里我们可能有的同学可能有一种感觉这不是"多态"吗?
是的,这就是用C语言实现多态。
cpp
// 创建 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);
上面API的具体介绍我们到下个博客中去介绍和使用,我们将在下一个博客中实现基于UDP实现的网络通信代码。
=========================================================================
本篇关于Linux的文件理解与操作的介绍就暂告段落啦,希望能对大家的学习产生帮助,欢迎各位佬前来支持纠正!!!
