Socket概述
什么是Socket连接? Socket是应用层与TCP/IP协议簇通信的中间软件抽象层,它是一组接口。
我们应用层进行网络开发时,并不需要open来打开一个网络文件描述符,甚至,网络设备都不是一个/dev下的设备。我们通过socket机制来获取连接的套接字,然后使用发送接收等函数来发送和接收数据,也可以使用close来关闭已建立连接的套接字。
那么问题来了,linux中的socket是在哪里实现的呢?
Socket在操作系统的传输层和应用层之间实现,在具体实现中就是应用层与TCP/IP协议栈之间。
Socket,即套接字,是一个通信链的句柄,用于描述IP地址和端口,是一个通信的链的句柄。在OSI七层模型中,它位于应用层和传输层之间的抽象层,为两台机器上的应用程序提供数据通信的手段。
在网络编程中,Socket是一个重要的概念,它提供了一种发送和接收数据的机制。Socket通常与TCP/IP协议一起使用,但也可以与其他协议(如UDP)一起使用。TCP/IP协议族是一个四层协议系统,包括链路层、网络层、运输层和应用层。而Socket则位于运输层和应用层之间,作为一个抽象层,为应用程序提供了通过网络进行数据传输的能力。
具体来说,当应用程序需要通过网络发送或接收数据时,它会调用Socket接口。Socket接口会将数据封装成符合网络协议标准的数据包,并通过运输层(如TCP或UDP)发送到网络上。在接收端,Socket接口会从网络上接收数据包,并将其解封装,然后传递给应用程序。
socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。Socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)。
说白了Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
注意:其实socket也没有层的概念,它只是一个facade设计模式的应用,让编程变的更简单。是一个软件抽象层。
总的来说,Socket是在操作系统的传输层和应用层之间实现的,它为应用程序提供了通过网络进行数据传输的能力。
套接字(socket)是 Linux 下的一种进程间通信机制(socket IPC),在前面的内容中已经给大家提到过,使用 socket IPC 可以使得在不同主机上的应用程序之间进行通信(网络通信),当然也可以是同一台主机上的不同应用程序。socket IPC 通常使用客户端<--->服务器这种模式完成通信,多个客户端可以同时连接到服务器中,与服务器之间完成数据交互。
内核向应用层提供了 socket 接口,对于应用程序开发人员来说,我们只需要调用 socket 接口开发自己的应用程序即可!socket 是应用层与 TCP/IP 协议通信的中间软件抽象层,它是一组接口。在设计模式中,socket 其实就是一个门面模式,它把复杂的 TCP/IP 协议隐藏在 socket 接口后面,对用户来说,一组简单的接口就是全部,让 socket 去组织数据,以符合指定的协议。所以,我们无需深入的去理解 tcp/udp 等各种复杂的 TCP/IP 协议,socket 已经为我们封装好了,我们只需要遵循 socket 的规定去编程,写出的程序自然遵循 tcp/udp 标准的。
当前网络中的主流程序设计都是使用 socket 进行编程的,因为它简单易用,它还是一个标准(BSD socket),能在不同平台很方便移植,比如你的一个应用程序是基于 socket 接口编写的,那么它可以移植到任何实现 BSD socket 标准的平台,譬如 LwIP,它兼容 BSD Socket;又譬如 Windows,它也实现了一套基于socket 的套接字接口,更甚至在国产操作系统中,如 RT-Thread,它也实现了 BSD socket 标准的 socket 接口。
BSD套接字是最早在1983年随着BSD操作系统发布的套接字接口的名称。后来,这个接口被整合到了POSIX规范中,并增加了一些更详细的操作规范。除了术语不同以外,可以认为没有任何区别。BSD套接字涵盖了随着BSD操作系统发布的API,而POSIX标准适用于任何希望符合POSIX的操作系统。
可参考:
具体了解该套接字,可参考:
操作系统可以不实现socket吗?
操作系统可以不实现Socket,但这样做会限制网络通信的灵活性和可移植性。Socket作为网络编程接口,在操作系统中扮演着重要角色,但并非不可或缺。以下将详细分析操作系统可以不实现Socket的原因:
Socket的作用与替代方案
Socket的定义:Socket是应用层与传输层之间的一个抽象层,它隐藏了复杂的网络通信细节,为应用程序提供了一组简单的API来进行网络通信。
替代方案:如果操作系统不提供Socket,应用程序开发者需要自己实现一套网络通信机制,或者使用其他形式的IPC(进程间通信)机制,如消息队列、共享内存等。
操作系统的网络功能
网络驱动与协议栈:即使没有Socket,操作系统仍然需要实现网络驱动程序和TCP/IP协议栈来支持网络通信。
用户空间网络库:在用户空间,可以使用第三方网络库来实现网络通信功能,这些库可能不依赖于操作系统提供的Socket接口。
Socket的普遍性与标准化
广泛采用:Socket接口因其标准化和跨平台的特性而被广泛采用,大多数现代操作系统都实现了Socket。
兼容性问题:不实现Socket可能导致应用程序兼容性问题,因为许多网络应用程序和服务都是基于Socket开发的。
性能与安全性考虑
绕过内核:有些高性能网络应用可能会选择绕过操作系统的Socket接口,直接与硬件交互,以提高性能。
安全风险:直接操作网络硬件可能会带来安全风险,因为这样可能会绕过操作系统的安全机制。
特定场景的应用
嵌入式系统:在资源受限的嵌入式系统中,可能会选择不实现完整的Socket接口,而是提供更轻量级的网络通信方式。
实时操作系统:实时操作系统可能会为了响应时间的最优化,而不实现标准的Socket接口。
开发复杂性与成本
开发难度:不实现Socket意味着开发者需要处理更多的网络通信细节,增加了开发难度。
维护成本:自定义的网络通信机制可能需要更多的维护工作,尤其是在面对网络协议更新时。
生态系统与社区支持
社区资源:Socket接口有大量的文档、教程和社区支持,不使用Socket可能会失去这些资源。
第三方服务:许多第三方服务和库都是基于Socket设计的,不实现Socket可能会导致无法使用这些服务。
总的来说,虽然操作系统可以不实现Socket,但这样做会增加应用程序开发的复杂性,降低网络通信的灵活性和可移植性。Socket作为一种成熟的网络编程接口,已经被广泛接受和使用,它的存在大大简化了网络编程的难度。因此,除非有特殊的性能或安全需求,否则操作系统通常会实现Socket接口。
所谓的客户端和服务端,其实就是两个应用程序,或者说两段程序,他们之间可以在一台机器上,也可以不在一台机器上,如果在同一台机器上,就通过本地socket通信,如果不在同一台机器上,就通过远端socket通信,甚至可以不通过网络通信,而是通过任意其他方式通信。通常是请求服务的一侧被叫做客户端,提供服务的那一侧被叫做服务端。
建立连接相关API
socket()函数
socket()函数原型如下所示:
#include <sys/types.h> /* See NOTES */ #include <sys/socket.h> int socket(int domain, int type, int protocol);
socket()函数类似于 open()函数,它用于创建一个网络通信端点(打开一个网络通信),如果成功则返回一个网络文件描述符,通常把这个文件描述符称为 socket 描述符(socket descriptor),这个 socket 描述符跟文件描述符一样,后续的操作都有用到它,把它作为参数,通过它来进行一些读写操作。
我们可以不用管文件描述符怎么来的,只要知道文件描述符能和设备唯一对应和关联起来就行。
该函数包括 3 个参数,如下所示:
domain
参数 domain 用于指定一个通信域;这将选择将用于通信的协议族。可选的协议族如下表所示:
对于 TCP/IP 协议来说,通常选择 AF_INET 就可以了,当然如果你的 IP 协议的版本支持 IPv6,那么可以选择 AF_INET6。
type
参数 type 指定套接字的类型,当前支持的类型有:
protocol
即协议类别,表示为给定的通信域和套接字类型选择默认协议,一般设置为0即可,因为该函数会通过前两个参数自动推导出第三个参数的协议类别。
调用 socket()与调用 open()函数很类似,调用成功情况下,均会返回用于文件 I/O 的文件描述符,只不过对于 socket()来说,其返回的文件描述符一般称为 socket 描述符。当不再需要该文件描述符时,可调用close()函数来关闭套接字,释放相应的资源。
如果 socket()函数调用失败,则会返回-1,并且会设置 errno 变量以指示错误类型。
使用示例
int socket_fd = socket(AF_INET, SOCK_STREAM, 0);//打开套接字 if (0 > socket_fd) { perror("socket error"); exit(-1); } ...... ...... close(socket_fd); //关闭套接字
注意,初始时,客户端和服务器都需要调用socket,来指定使用的协议族和socket类型。
SOCK_STREAM和SOCK_DGRAM,怎么记忆呢?
SOCK_STREAM里有个T,可以联想为TCP,SOCK_DGRAM里面有个D,可以联想为UDP。
Linux中的socket网络文件描述符也属于文件描述符。
在Linux操作系统中,一切皆可以看成是文件,包括socket连接。文件描述符(file descriptor)是内核为了高效管理已被打开的文件所创建的索引,它是一个非负整数,用于指代被打开的文件或套接字。每个进程都有一个文件描述符表,用来管理当前进程打开的文件和套接字。标准输入、标准输出和标准错误的文件描述符分别是0、1和2,而大于等于3的文件描述符通常用来表示其他文件或套接字。
为什么网络设备不像字符设备一样用open打开?
二者的设计理念是不一样的,这源于网络设备的复杂性。
网络设备通常不使用
open
系统调用来操作,而是通过专门的网络编程接口和协议栈进行通信。应用程序通过套接字(Socket)接口与网络进行通信。套接字提供了一种抽象机制,使得应用程序可以像读写普通文件一样进行网络通信。
在Unix-like系统中,网络设备通常不表现为传统的设备文件(如/dev/下的字符设备或块设备),因此不会使用
open
来打开。网络协议栈将网络通信分为多个层次,每一层都使用不同的协议和技术。这种分层结构使得网络通信更加高效和可靠。
操作系统通过套接字接口和网络协议栈实施严格的访问控制,确保只有授权的应用程序才能进行网络通信。
网络编程接口和协议栈是跨平台的标准,确保不同操作系统上的应用程序可以进行一致的网络通信。
网络协议栈经过优化,以提供高效的数据传输。直接使用
open
可能无法充分利用这些优化。网络协议栈提供了丰富的错误处理机制,可以帮助开发者诊断和解决网络问题。
网络协议栈存在多种网络调试工具,如Wireshark、tcpdump等,这些工具依赖于标准的网络协议栈,而不是简单的文件操作。
总的来说,网络设备不使用
open
系统调用来操作,是因为网络通信需要更复杂的接口和协议支持,而这些是由网络编程接口和协议栈提供的。直接使用open
无法满足网络通信的需求,也无法保证通信的安全性和效率。
网络设备及其描述符
每个字符设备在/dev目录下对应一个设备文件,linux用户程序通过设备文件(或称设备节点)来使用驱动程序操作字符设备。
每个块设备在/dev目录下对应一个设备文件,linux用户程序通过设备文件(或称设备节点)来使用驱动程序操作块设备。
/dev 中放的都是字符设备和块设备,而网络设备并不属于此二类,无法实现它们的接口。
参考网络设备驱动
【Linux驱动】网络设备驱动介绍_dev下有网卡设备吗-优快云博客
(1)网络设备:软件层面的,如操作系统中看到的eth0(以太网)、wlan0(无线网)等。
(2)物理网卡: 真正的硬件网卡设备。
(3)/dev下没有设备文件,也不通过/sys下的属性文件访问。直观看来,应用层都是通过一些特殊的命令(如ifconfig、ping等)来访问网卡硬件(调用驱动)的。
(4)面向报文而不是面向流的,因此将网络接口映射到文件系统的节点比较困难。
(5)struct net_device来管理所有网络接口。
!!!!!!!!!注意网络设备的描述符;
我们知道,不管是打开字符设备还是块设备还