【Linux】网络基础概念(下)
目录
- 一、Socket编程基础
- 1.1 理解源IP地址和目的IP地址
- 1.2 认识端口号
- pid vs port
- ip vs port
- 理解socket
- 1.3 传输层的典型代表
- 1.4 网络字节序
- 1.5 Socket通用结构体
个人主页:矢望
个人专栏:C++、Linux系统编程、Linux网络编程、C语言、数据结构、Coze-AI、MySQL
一、Socket编程基础
1.1 理解源IP地址和目的IP地址
主机A和主机B在通信,通过将数据从主机A发送到主机B的手段,可以达到让用户通信的目的!
源IP和目的IP保证了主机之间进行通信。
进程是用户在系统中的代表,只要把数据给进程,用户就相当于就拿到了数据。
所以数据传输到主机不是目的,而是手段。到达主机内部,在交给主机内的进程,才是目的。
但是系统中,同时会存在非常多的进程,当数据到达目标主机之后,怎么转发给目标进程?这就需要在网络的背景下,在系统中,标识进程的唯一性。
1.2 认识端口号
端口号(port)是传输层协议的内容。
端口号是一个2字节16位的整数;端口号用来标识一个进程,告诉操作系统,当前的这个数据要交给哪一个进程来处理;IP地址+端口号能够标识网络上的某一台主机的某一个进程;一个端口号只能被一个进程占用。
以主机A某一进程向主机B某一进程发消息为例,主机B会从网络中收到相关的报文,这个报文中会标出它的源IP地址是谁,目的IP地址是谁,源端口号是谁目的端口号是谁,通过目的IP地址找到了主机B,到时候还会通过目的端口号,找到主机B上的特定进程,比如两台主机通过各自的QQ进程聊天,QQ绑定的端口号就是固定的。
所以网络通信的本质是两个进程之间在通信,也就是进程间通信。在冯诺依曼体系的视角下看待网络通信,就是IO,你上网无非两件事,上传数据、下载数据;在Linux视角下一切皆文件,网络也是文件,所以本质也是IO。
端口号范围划分
•0 - 1023:知名端口号,HTTP, FTP, SSH等这些广泛使用的应用层协议,它们的端口号都是固定的。
•1024 - 65535:操作系统动态分配的端口号。客户端程序的端口号,就是由操作系统从这个范围分配的。
pid vs port
pid不就可以标志进程的唯一性了吗?为什么还要有端口号?
首先pid是系统的概念,在网络中还使用pid的话会造成强耦合,所以第一个原因,使用端口号port来标识本身就是一种解耦工作。
pid每个进程都要有,但是不是所有的进程都要进行网络通信,所以不是所有进程都需要端口号,这个时候有端口号的进程肯定就进行了网络通信。端口号起到了强标识的作用。
ip vs port
ip标识公网中的唯一一台主机,端口号标识主机中的唯一一个进程。
OS是如何把收到的数据转发给特定端口的进程的?
在操作系统中有很多的哈希列表,其中就有存储端口号到进程映射的列表,操作系统通过提取报文的报头就可以提取出该报文要发的端口号,于是就根据端口号映射到进程。
找到进程之后,如何把网络数据交给这个进程?
操作系统中有很多的报文,所以就需要使用数据结构对这些报文做管理,可以是链表等。在操作系统通过端口号找到进程之后,就可以找到进程的文件描述符fd,就可以找到进程的缓冲区,这样就可以将网络数据拷贝给进程。
理解socket
综上,IP地址用来标识互联网中唯一的一台主机,port用来标识该主机上唯一的一个网络进程。所以,IP+Port就能标识互联网中唯一的一个进程。
我们把ip+port叫做套接字socket。(socket的汉语意思是插座)
1.3 传输层的典型代表
如上图,传输层和网络层是在操作系统内核中的,应用层想要访问内核必须通过系统调用,所以应用层想要访问网络功能必须通过系统调用。
认识TCP协议:TCP(Transmission Control Protocol)是传输控制协议,它为应用程序提供稳定的端到端数据传输服务。
有这些特点:传输层协议,有连接,可靠传输,面向字节流。
认识UDP协议:UDP(User Datagram Protocol)是用户数据报协议,适合对实时性要求高、允许少量丢包的应用。
有这些特点:传输层协议,无连接,不可靠传输,面向数据报。
有无连接:这两种协议在设计的时候是要满足通信时的所有场景和需求的,日常通信无非就是有连接和无连接,例如打电话就是有连接,它需要保证双方都接通,例如发微信,发邮件就是无连接,一方发送数据即可。
可靠不可靠并不是TCP或UDP的优缺点,而是这两种协议的特点,要保证可靠就意味着需要做其它更多的维护工作,更复杂。而不可靠就意味着这个协议更轻量化。优缺点是伴生出现的。TCP的应用场景例如支付场景,必须保证可靠。UDP的应用场景例如直播,实时性较高,如果使用TCP维护成本大。TCP和UDP在日常的使用比例大概8:2。
1.4 网络字节序
内存中的多字节数据相对于内存地址有大端和小端之分,磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分,网络数据流同样有大端小端之分。
如上图,将低权值位数据存放在低地址处叫做小端存储;将高权值位数据存放在低地址处叫做大端存储。
也就是"小小小":小权值位数据存放在小地址处就是小端存储。
网络数据流发送这样规定:先发送低地址处的数据,再发送高地址处的数据。
当一个主机是大端存储而另一个主机是小端存储,并且大端主机向小端发送数据时,大端存储的主机存储0x11223344后发送给小端存储的主机,此时小端存储主机的低地址处收到的是11,这样小端存储的主机读出的数据就成了0x44332211。
为了避免这种情况,TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节。也就是所有发送到网络上的数据,都必须是大端的!选择大端存储可能是因为人读数据习惯从低向高读,例如1234,我们会先读权值高的1,大端存储的情况下,高权值位的数据刚好在低地址处。
为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。h表示host,n表示network,l表示32位长整数,s表示16位短整数。
例如htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送。
如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回。
1.5 Socket通用结构体
socket常见API:
// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)intsocket(intdomain,inttype,intprotocol);// 绑定端口号 (TCP/UDP, 服务器)intbind(intsocket,conststructsockaddr*address,socklen_t address_len);// 开始监听socket (TCP, 服务器)intlisten(intsocket,intbacklog);// 接收请求 (TCP, 服务器)intaccept(intsocket,structsockaddr*address,socklen_t*address_len);// 建立连接 (TCP, 客户端)intconnect(intsockfd,conststructsockaddr*addr,socklen_t addrlen);在上面的API中sockaddr结构体出现的频率很高。
Socket网络通信,它也支持本地通信,所以为了支持多种通信方式,它就有多种socket,例如网络通信套接字INET socket,本地通信unix域间socket,还有原始套接字raw socket。
结构越复杂,记忆和学习成本越高,对新技术的传播也不利,socket设计者考虑到了这一点,并通过统一的一套API接口解决了这一点,并引入了一个结构体sockaddr来保证API传递参数的一致。
如上图,网络通信,本地通信和sockaddr结构体都拥有一个16位地址类型的部分,将来在调用函数,例如int bind(int socket, const struct sockaddr *address, socklen_t address_len);时,可以先定义对象,比如struct sockaddr_in addr;addr.xxx = ip;等然后标注清楚自己是什么地址类型,addr.地址类型 = AF_INET;,紧接着调用函数的时候就将这个结构体变量强转成struct sockaddr*类型,由于sockaddr结构体中也有16位地址类型,所以它就可以通过判断是AF_INET或者是AF_UNIX来强转成相关结构体的指针,这样就可以进行访问了。
也就是基类指针指向子类对象,就是使用C语言实现了多态!
如上图,几种不同的socket,它们使用的都是同一套接口,这样就减少了记忆和学习的成本。并且基类sockaddr还保证了接口采用统一的参数传递。
总结:
以上就是本期博客分享的全部内容啦!如果觉得文章还不错的话可以三连支持一下,你的支持就是我前进最大的动力!
技术的探索永无止境! 道阻且长,行则将至!后续我会给大家带来更多优质博客内容,欢迎关注我的CSDN账号,我们一同成长!
(~ ̄▽ ̄)~