【计算机网络】Socket

【计算机网络】Socket,第1张

socket进行通信的方式如下:

使用socket()系统调用能够创建一个socket,它返回一个用来在后续系统调用中引用该socket的文件描述符。

socket存在于一个通信domain中,它确定:

现在 *** 作系统支持下列domain:

每个socket实现都至少提供了两种socket:流和数据报。这两种类型在UNIX和Internet domain中都得到了支持。

流socket提供了一个可靠的双向的字节流通信信道:

数据报socket允许数据以数据报的形式进行交换。在使用时无需与另一个socket简历连接。

传入bind()的addr比较复杂,每种socket domain都使用了不同的地址格式,如UNIX domain socket使用路径名,而Internet domain socket 使用IP地址和端口号。struct sockaddr适用于所有domain,将各种domain特定的地址结构转换成单个类型以供socket系统调用中的各个参数使用。

socket I/O 可以使用传统的read()和write()系统调用或使用一组socket特有的系统调用send() recv() sendto() recvfrom()。默认情况下,这些系统调用在I/O *** 作无法被立即完成时阻塞,使用fcntl() F_SETFL *** 作用启用 O_NONBLOCK 打开文件状态标记可以执行非阻塞I/O

listen()系统调用将文件描述符sockfd引用的流socket标记为被动,这个socket后面会被用来接受来自其他(主动的)socket的链接。

无法再一个已连接的socket(已成功执行connect()的socket或由accept()调用返回的socket)上执行 listen()

如果服务器正忙于处理其他客户端,那么客户端的connect()可能并不能马上被accept(),这将产生一个未决的连接。

内核必须要记录所有未决的连接请求的相关信息,backlog参数允许限制这种未决连接的数量。在这个限制之内的连接请求会立即成功,之外的连接请求就会阻塞直到一个未决的连接被接受,并从未决连接队列中删除。

accept()系统调用会文件描述符sockfd引用的监听流socket上接受一个连入连接。如果在调用accept时不存在未决的连接,那么调用会阻塞直到有连接请求到达为止。

返回的结果是已连接的socket的文件描述符。addr参数指向一个用来返回socket地址的结构。

一对连接的流 socket 在两个端点之间提供了一个双向通信信道。

关闭一个连接之后,对等应用程序读取数据时将会收到文件结束(所有缓冲数据都读取之后),如果要写入数据,会收到一个SIGPIPE信号,并且系统调用返回EPIPE错误。

无法保证顺序,也无法保证能够到达。由于底层协议有时会重新传包,也可能多次到达。

尽管数据报socket是无连接的,但在数据报socket上应用connect()系统调用仍然起作用,会导致内核记录这个socket的对等socket地址。

当一个数据报socket已连接后:

在UNIX domain中,socket地址以路径名来表示,domain特定的socket地址结构的定义如下:

为将一个UNIX domain socket绑定到一个地址上,需要初始化一个sockaddr_un结构,然后将指向这个结构的一个指针作为addr参数传入bind()并将addrlen指定为这个结构的大小。

当用来绑定UNIX domain socket时,bind()会在文件系统中创建一个条目,作为socket路径名的一部分的目录需要可访问和可写。这个文件会被标记为一个socket,当再这个路径名上应用stat()时,它会在stat结构的st_mode字段中的文件类型部分返回值S_IFSOCK。

尽管UNIX domain socket是通过路径名来标识的,但这些socket上发生的I/O无须对底层设备进行 *** 作。

有关绑定一个UNIX domain socket的注意点:

服务器流程:

客户端流程:

对于UNIX domain socket来说,数据报的传输是在内核中发生的,也是可靠的,所有消息都会按序被递送并且不会发生重复的状况。

服务器创建socket后并绑定后,进入一个无线循环,在循环中使用recvfrom()接收来自客户端的数据报,将接收到的文本转换成大小格式并使用通过recvfrom()获取的地址将转换过的文本返回给客户端。

socket文件的所有权和权限决定了哪些进程能够与这个socket进行通信

有时候让单个进程创建一对socket并将它们连接起来是比较有用的。

允许将一个UNIX domain socket绑定到一个名字上但不会在文件系统中创建的名字

要传输数据,数据链路层需要将网络层传递过来的数据报封装进被称为帧的一个一个单元。最大传输单元MTC是改层所能传输的帧大小的上限。

网络层任务:

网络层的协议是IP,IPv4使用32位地址来标识子网和主机,IPv6则使用了128位的地址。

一个裸socket(SOCK_RAW),允许程序直接与IP层进行通信,但大多数都会基于一种传输层协议之上的socket。

IP以数据报(包)的形式来传输数据。在两个主机之间发送的每一个数据报都是在网络上独立传输的,它们经过的路径可能会不同。一个IP数据报包含一个头,其大小范围为20字节到60字节。包含目标主机的地址,源地址。

一个IP实现可能会给它所支持的数据报的大小设定一个上限。所有IP实现都必须做到数据报的大小上限至少与规定的IP最小重组缓冲区大小一样大。IPv4限制值是576字节,IPv6是1500字节。

IP是一种无连接协议,并没有在相互连接的两个主机之间提供一个虚拟电路。

IP是一种不可靠的协议:尽最大可能将数据报从发送者传输给接收者,但并不保证包到达的顺序与它们被传输的顺序一致,也不保证是否重复,甚至到达。IP也美誉错误恢复。可靠性是通过使用TCP来保证的。

IPv4为IP头提供了一个校验和,这样能够检测出头中的错误,但并没有为包中所传输的数据提供任何错误检测机制。IPv6并没有为IP头提供校验和,它依赖高层协议来完成错误检测和可靠性。

IP数据报的重复使可能发生的,数据链路层采用一些技术确保可靠性以及IP数据报可能会以隧道形式穿越采用了重传机制。

IP会将数据报分段成一个个大小合适的传输单元,这些分段在到达最终目的之后会被重组成原始的数据报(每个IP分段本身就包含一个偏移量)

套接(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。

一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信的接口,是应用程序与网络协议根进行交互的接口。

套接字是通信的基石,是支持TCP/IP协议的路通信的基本 *** 作单元。

可以将套接字看作不同主机间的进程进行双间通信的端点,它构成了单个主机内及整个网络间的编程界面。套接字存在于通信域中,通信域是为了处理一般的线程通过套接字通信而引进的一种抽象概念。

套接字通常和同一个域中的套接字交换数据(数据交换也可能穿越域的界限,但这时一定要执行某种解释程序),各种进程使用这个相同的域互相之间用Internet协议簇来进行通信。

Socket(套接字)可以看成是两个网络应用程序进行通信时,各自通信连接中的端点,这是一个逻辑上的概念。它是网络环境中进程间通信的API(应用程序编程接口),也是可以被命名和寻址的通信端点,使用中的每一个套接字都有其类型和一个与之相连进程。

通信时其中一个网络应用程序将要传输的一段信息写入它所在主机的 Socket中,该 Socket通过与网络接口卡(NIC)相连的传输介质将这段信息送到另外一台主机的 Socket中,使对方能够接收到这段信息。

Socket是由IP地址和端口结合的,提供向应用层进程传送数据包的机制。

类型

1、数据报套接字

无连接套接字,使用用户数据报协议(UDP)。在数据报套接字上发送或接收的每个数据包都单独寻址和路由。数据报套接字不能保证顺序和可靠性,因此从一台机器或进程发送到另一台机器或进程的多个数据包可能以任何顺序到达或可能根本不到达。在数据报套接字上发送广播可能需要特殊配置。

为了接收广播数据包,数据报套接字不应该绑定到特定地址,尽管在某些实现中,当数据报套接字绑定到特定地址时也可能接收广播数据包。

2、流套接字

面向连接的套接字,使用传输控制协议(TCP)、流控制传输协议(SCTP) 或数据报拥塞控制协议(DCCP)。流套接字提供了无记录边界的有序且独特的无错误数据流,并具有用于创建和销毁连接以及报告错误的明确定义的机制。

流套接字以带外功能可靠地、有序地传输数据。在 Internet 上,流套接字通常使用 TCP 实现,以便应用程序可以使用 TCP/IP 协议在任何网络上运行。

3、原始套接字

允许直接发送和接收 IP 数据包,无需任何特定于协议的传输层格式。对于其他类型的套接字,根据选择的传输层协议(例如 TCP、UDP)自动封装有效载荷,并且套接字用户不知道与有效载荷一起广播的协议头的存在。从原始套接字读取时,通常包含标头。

从原始套接字传输数据包时,自动添加标头是可选的。

大多数套接字应用程序编程接口(API),例如基于Berkeley 套接字的那些,支持原始套接字。Windows XP于 2001 年发布,在Winsock接口中实现了原始套接字支持,但三年后,微软出于安全考虑限制了 Winsock 的原始套接字支持。

原始套接字用于与安全相关的应用程序,如Nmap。原始套接字的一个用例是在用户空间中实现新的传输层协议。

原始套接字通常在网络设备中可用,用于路由协议,例如Internet 组管理协议(IGMP) 和开放最短路径优先(OSPF),以及用于Internet 控制消息协议(ICMP) 等事情,由ping 实用程序。

以上内容参考 百度百科-套接字

Linux 下,一切皆文件

在Linux *** 作系统中,可以将一切都看作是文件,包括普通文件,目录文件,字符设备文件(如键盘,鼠标…),块设备文件(如硬盘,光驱…),套接字等等,所有一切均抽象成文件,提供了统一的接口,方便应用程序调用。

既然在Linux *** 作系统中,你将一切都抽象为了文件,那么对于一个打开的文件,我应用程序怎么对应上呢?

文件描述符应运而生。

文件描述符:File descriptor,简称fd,当应用程序请求内核打开/新建一个文件时,内核会返回一个文件描述符用于对应这个打开/新建的文件,其fd本质上就是一个 非负整数 。实际上,它是一个索引值,指向 内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。 在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的 *** 作系统。

*** 作系统的核心叫内核,是一个独立的软件。

*** 作系统为每一个进程维护了一个文件描述符表,该表的索引值都从从0开始的,所以在不同的进程中可以看到相同的文件描述符,这种情况下相同的文件描述符可能指向同一个文件,也可能指向不同的文件,具体情况需要具体分析,下面用一张简图就可以很容易的明白了。

通过上图可以看到,当不同进程中出现相同的文件描述符时,可能实际对应的文件并不是同一个,相反不同进程中不同的文件描述符也可可能对应同一个文件。

当一个应用程序刚刚启动的时候,0是标准输入,1是标准输出,2是标准错误。如果此时去打开一个新的文件,它的文件描述符会是3。POSIX标准要求每次打开文件时(含socket)必须使用当前进程中最小可用的文件描述符号。

文件描述符是一个重要的系统资源,理论上系统内存多大就应该可以打开多少个文件描述符,但是实际情况是,内核会有系统级限制,以及用户级限制(不让某一个应用程序进程消耗掉所有的文件资源,可以使用ulimit -n 查看)。

进程 + 文件描述符ID确认,因为内核为每个进程都有一份其所属的文件描述符表。

所以linux下两个进程返回的文件描述符是不一样的

多个进程之间的fd:

应用程序进程拿到的 文件描述符ID 对应 进程文件描述符表 的索引,通过索引拿到 文件指针 ,指向系统级文件描述符表的 文件偏移量 ,再通过文件偏移量找到 inode指针 ,最终对应到真实的文件。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/tougao/11442908.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-16
下一篇 2023-05-16

发表评论

登录后才能评论

评论列表(0条)

保存