文章目录
- 1、理解网络间通信
- 2、理解协议
- 3、网络字节序
- 4、socket编程接口和sockaddr结构
1、理解网络间通信
宏观上,是主机与主机在发送接收消息,但主机怎么去发送消息?主机间的通信是通过进程完成的,这个进程就是用户发起的进程,比如打开微信聊天,就是打开了微信这个进程。主机包含操作系统,驱动,物理层,上方的应用层则有用户打开的进程,进程调用系统中的网络接口发送消息给到另一个主机的系统,系统会把这个消息传给上层的应用层的对应的进程。主机是如此,服务器也是如此,刷短视频的时候就是在进程在和软件公司的服务器交互,发送请求,由服务器去完成。
主机间的通信,先将数据通过OS,通过TCP/IP协议发送到目标主机,IP地址可以表示互联网上唯一一台主机,目标主机将收到的数据推送给自己上层的指定进程。但进程不只有一个,那么我们自己的主机如何知道是哪一个网络进程在与它交互?通过端口号。端口号是传输层协议的内容,是一个2字节16位的整数,它标识一个进程,告诉操作系统当前这个进程的数据要交给哪个进程来处理。IP地址标识唯一一台主机,端口号可以标识对应主机内,唯一的一个进程,所以IP地址+端口号就可以标识互联网上的唯一一个进程。
发送消息的主机,有源IP,源端口,接收消息的,有目的IP,目的端口,网络通信的本质就是通过IP + PORT(端口)构建进程唯一性,进行基于网路的进程间通信。通过IP+端口号来进行通信的方案就是套接字,socket通信。
网络通信不止这一个方案。除了端口号,其实也可以通过进程PID来实现通信,因为它也是唯一的,能够标识唯一一个进程,但端口号能告诉主机,这个进程是来通信的,而PID则不行;并且如果用PID,那么网络又得管理好这些进程,进程PID改了,网络相关部分也改了,所以不如单独用一个端口号来标识更针对。
一个进程可以绑定多个端口号,但一个端口号不能被多个进程绑定。一个主机的系统层收到信息后,通过TCP/IP协议,会得到两个端口号和两个IP地址,操作系统层面会维护一张端口号的哈希表,但不是哈希桶,所以一个位置只能连接上一个进程,但是一个进程可以映射到多个位置,所以通过这个哈希表,端口号就可以找到对应的进程。
网络也是在系统内部的,所以也是文件。网路这个文件有文件描述符,有自己的文件结构体,有自己的缓冲区,当网络收到数据时,通过有效载荷找到进程,根据进程找到网络的套接字,根据套接字查找文件描述符表,就找到了对应的缓冲区,把接收的数据放到缓冲区里,进程就可以以文件的形式读取到数据了。
2、理解协议
TCP/UDP协议在传输层。TCP是传输控制协议,面向连接的协议,并且自带可靠性,可靠性是指如果传输中数据丢失,TCP协议会重新发一遍。以及面向字节流。字节流是指在没有发数据前,存储时是以字节流存储的,拿取的策略靠拿数据的来确定,可以一次性拿全部,也可以每一次都拿4字节这样的。
UDP协议是用户数据报协议。它是无连接,不可靠传输,面向数据报的。用UDP协议发送数据时,就不关系数据后续怎么样了。它在通信之前也不需要建立连接。
面向数据报是什么意思?简单理解一下,就是拿快递时,只能一个个拿,或者多个拿,但不可能拿半个。而面向字节流,就是看拿取方怎么做了,只拿十分之一个快递也都是可以的。
TCP可靠,也表明它通信成本更高,使用成本更高,要做的工作更多,而UDP则更简洁,像直播场景用的就是UDP协议,不过TCP协议用的还是要更多。
3、网络字节序
每个字节都有地址,这些地址的权值位不同,有高有低,高权值位放在内存的高地址处(小端)和低地址处(大端)就有了不同的存放方法,也就有了大小端。如果两个主机存储方式不同,一个大,一个小,那么发送数据时,接收的数据就会变成一个反向存储的数据,数据就不对了,所以就出现了字节序来解决这个问题。
要解决这个问题,不能用添加报头来解决,因为报头也是数据,也会被反向存储。而字节序解决这个问题就很简单粗暴,就是直接规定了,网络序列必须是大端。发送数据一方必须将数据转成大端存储,接收一方就不管是不是大端了。
主机序列转成网络所需要的序列是一些函数支持的
前两个函数是主机转网络,后两个是网络转主机,32和16就是表示32和16位整数。
4、socket编程接口和sockaddr结构
struct sockaddr是一个结构体。这个结构体属于一个既能主机内部通信,又能网络间通信的标准,POSIX标准,而System V只能本主机通信。POSIX基于套接字制定。struct sockaddr_in套接字是用来网络间通信的,sockaddr_un是用来进行本地间通信的。un的方法和in类似,基本会了in就可以看得懂un了。struct sockaddr是一个通用接口,既可以网络通信,也可以本地通信。想做本地通信,就把un强转成sockaddr类型。通信的接口接收的都是sockaddr类型的。而为了强转能够没有问题,三个结构体最前头都有一个16位的地址,sockaddr会先判断是要进行哪个通信,这个16位地址其实存储的就是一个整数,和两个宏AF_INET AF_UNIX比较,等于哪个就采用哪个的方法。
实际上可以看出来,函数参数里是sockaddr,传过来in就用in的方法,传过来un就用un的方法,这就是多态,但因为TCP是用C语言写的,所以这是用C实现的多态。那为什么不用viod来代替sockaddr呢?其实是可以的,不过因为制定这个标准时还没有void的制定。
下一篇写udp套接字通信,开始写代码。
结束。