socket简介
网络套接字是用于在计算机网络上的节点内发送或接收数据的内部端点。具体来说,它是一个网络软件端点的代表(协议栈),例如表中的条目(列出通信协议、目标、状态等),是系统资源的一种形式。
术语套接字类似于物理母连接器,两个节点之间通过通道进行通信,通道被可视化为电缆,每个节点上都有两个公连接器插入套接字。类似地,术语port(女性连接器的另一个术语)用于节点上的外部端点,术语socket也用于本地进程间通信(IPC)的内部端点(不通过网络)。但是,这种类比有些牵强,因为网络通信不需要是一对一的,也不需要有专用的通信通道。
使用
进程可以使用套接字描述符(句柄的一种类型)引用套接字。流程首先请求协议堆栈创建套接字,堆栈将向流程返回描述符,以便能够识别套接字。然后,当进程希望使用此套接字发送或接收数据时,将描述符传递回协议堆栈。
与端口不同,套接字是特定于一个节点的;它们是本地资源,不能被其他节点直接引用。此外,套接字不一定与用于两个节点之间通信的持久连接(通道)相关联,也不一定存在某个单独的其他端点。例如,数据报套接字可用于无连接通信,多播套接字可用于发送到多个节点。然而,在internet通信的实践中,套接字通常用于连接到特定的端点,并且通常使用持久连接。
地址
在实践中,套接字通常指Internet协议(IP)网络中的套接字(其中套接字可以称为Internet套接字),特别是传输控制协议(TCP),它是一对一连接的协议。在这个上下文中,套接字被假定与一个特定的套接字地址相关联,即本地节点的IP地址和端口号,并且在外部节点(其他节点)上有一个对应的套接字地址,它本身有一个关联的套接字,由外部进程使用。将套接字与套接字地址关联称为绑定。
注意,虽然当地的过程可以与外国交流过程通过发送或接收数据从外国或套接字地址,它没有访问外国插座本身,也不能使用外国套接字描述符,因为这些都是外国内部的节点。例如,在10.20.30.40:4444和50.60.70.80:8888(本地IP地址:本地端口,外部IP地址:外部端口)之间的连接中,每一端都有一个关联的套接字,对应于该节点上协议栈对连接的内部表示。这些由数值套接字描述符局部引用,比如一边是317,另一边是922。10.20.30.40可以请求流程节点与节点通信50.60.70.80在端口8888(请求的协议栈创建一个套接字与该目的地),一旦创建了一个套接字和接收一个套接字描述符(317),它可以通过这个套接字通信使用描述符(317)。然后协议栈将数据转发到端口8888上的节点50.60.70.80,并从节点50.60.70.80转发数据。但是,节点10.20.30.40上的进程不能请求基于外部套接字描述符进行通信(例如。“套接字922”或“节点50.60.70.80上的套接字922”),因为这些是外部节点内部的,并且不能被节点10.20.30.40上的协议栈使用。
实现
协议栈(现在通常由操作系统提供,而不是作为一个单独的库)是一组服务,允许进程使用堆栈实现的协议在网络上通信。程序使用网络套接字与协议栈通信所用的应用程序编程接口(API)称为套接字API。开发利用这个API的应用程序称为套接字编程或网络编程。
Internet套接字api通常基于Berkeley套接字标准。在Berkeley sockets标准中,套接字是文件描述符(文件句柄)的一种形式,这是由于Unix哲学“一切都是文件”,以及套接字与文件之间的类比。它们都具有读取、写入、打开和关闭功能。实际上,这种差异意味着这种类比是牵强的,而在套接字上使用不同的接口(发送和接收)。在进程间通信中,每个端通常都有自己的套接字,但这些套接字可能使用不同的api:它们由网络协议抽象。
在标准的Internet协议TCP和UDP中,套接字地址是IP地址和端口号的组合,很像电话连接的一端是电话号码和特定扩展名的组合。套接字不需要有源地址,例如,仅用于发送数据,但如果程序将套接字绑定到源地址,则套接字可用于接收发送到该地址的数据。基于这个地址,Internet套接字将传入的数据包发送到适当的应用程序进程。
与端口不同,套接字是特定于一个节点的;它们是本地资源,不能被其他节点直接引用。此外,套接字不一定与用于两个节点之间通信的持久连接(通道)相关联,也不一定存在某个单独的其他端点。例如,数据报套接字可用于无连接通信,多播套接字可用于发送到多个节点。然而,在internet通信的实践中,套接字通常用于连接到特定的端点,并且通常使用持久连接。
定义
套接字(内部表示)、套接字描述符(抽象标识符)和套接字地址(公共地址)之间的区别很细微,在日常使用中没有仔细区分它们。此外,“套接字”的特定定义在不同的作者之间有所不同,而且常常特别指internet套接字或TCP套接字。
Internet套接字至少具有以下特征:
- 本地套接字地址,由本地IP地址和(TCP和UDP,但不是IP)端口号组成
- 协议:传输协议,例如TCP、UDP、原始IP。这意味着带有TCP端口53和UDP端口53的(本地或远程)端点是不同的套接字,而IP没有端口。
已连接到另一个套接字的套接字,例如,在建立TCP连接期间,也有一个远程套接字地址。
在创建套接字的操作系统和应用程序中,套接字由称为套接字描述符的惟一整数值引用。操作系统通过从IP和传输协议头中提取套接字地址信息并从应用程序数据中提取头信息,将传入的IP包的有效负载转发给相应的应用程序。
在IETF征求意见、Internet标准、许多教科书以及本文中,术语套接字指的是由套接字编号惟一标识的实体。在其他教科书中,术语“套接字”指的是本地套接字地址,即“IP地址和端口号的组合”。在RFC 147中给出的套接字的最初定义中,由于它与1971年的ARPA网络有关,“套接字被指定为32位数字,偶数套接字标识接收套接字,奇数套接字标识发送套接字。”然而,今天的套接字通信是双向的。
例子
这个示例根据Berkeley套接字接口建模,通过TCP将字符串“Hello, world!”通过地址为1.2.3.4的主机端口80发送。它演示了一个套接字(getSocket)的创建,将它连接到远程主机,发送字符串,最后关闭套接字:
Socket socket = getSocket(type = "TCP")
connect(socket, address = "1.2.3.4", port = "80")
send(socket, "Hello, world!")
close(socket)
类型
有几种类型的互联网套接字:
数据报套接字,也称为无连接套接字,它使用用户数据报协议(UDP)。
流套接字,也称为面向连接的套接字,它使用传输控制协议(TCP)、流控制传输协议(SCTP)或数据报拥塞控制协议(DCCP)。
原始套接字(或原始IP套接字),通常在路由器和其他网络设备中可用。在这里,传输层被绕过,包头被应用程序访问,地址中没有端口号,只有IP地址。
数据报套接字
数据报套接字是一种网络套接字,它为发送或接收数据包提供无连接点。在数据报套接字上发送或接收的每个数据包都被单独寻址和路由。数据报套接字不能保证顺序和可靠性,因此从一台机器或进程发送到另一台机器或进程的多个数据包可能以任意顺序到达,也可能根本没有到达。
在网络上发送UDP广播总是在数据报套接字上启用。为了接收广播数据包,应该将数据报套接字绑定到通配符地址。当数据报套接字绑定到更特定的地址时,还可以接收广播数据包。
流套接字
流套接字是一种网络套接字,它提供面向连接的、有序的、惟一的数据流,没有记录边界,具有定义良好的机制来创建和销毁连接以及检测错误。
流套接字可靠地、有序地、带外地传输数据。
在Internet上,流套接字通常是在TCP之上实现的,因此应用程序可以使用TCP/IP协议在任何网络上运行。SCTP也可以用于流套接字。
原始套接字
原始套接字是一种网络套接字,它允许直接发送和接收IP数据包,而不需要任何特定于协议的传输层格式化。
对于其他类型的套接字,有效负载将根据所选的传输层协议(例如TCP、UDP)自动封装,套接字用户不知道使用有效负载广播的协议头的存在。当从原始套接字读取时,通常包括头。当从原始套接字传输数据包时,自动添加头是可选的。
原始套接字用于与安全性相关的应用程序,如Nmap。原始套接字的一个可能用例是在用户空间中实现新的传输层协议。[3]原始套接字通常在网络设备中可用,用于路由协议,如Internet Group Management Protocol (IGMPv4)和Open short Path First (OSPF),以及ping实用程序使用的Internet Control Message Protocol (ICMP)
大多数套接字应用程序编程接口(api),例如基于Berkeley套接字的api,都支持原始套接字。Windows XP于2001年发布,在Winsock接口中实现了原始套接字支持,但三年后,出于安全考虑,微软限制了Winsock的原始套接字支持
其他
其他套接字类型是通过其他传输协议实现的,比如系统网络体系结构(SNA)。有关内部进程间通信,请参阅Unix域套接字(UDS)。
开始学习
Socket和Sokcet的api都被用来网络传递信息。它们提供了一种进程间的通信(IPC)。网络可以是一个逻辑的、局域的网络对于计算机来说,或者一个物理上链接上的额外网络,有着自己对于其他网络的链接。一个明显的例子的就是因特网,你链接它通过你的网络供应商。
我们来学习以下如何去使用socket编程去构建服务器和客户端:
- 我们开始本篇教程通过一个简单的socket服务器和客户端。
- 一旦我们见过了api,和它们如何工作在起始的例子,我们会找改进版本,能够仿真地操作多种链接。
- 最后,我们会努力去建立一个示例服务器和客户端,这就像一个功能完备的套接字应用程序,具有自己的自定义头和内容。
我们最终能理解怎么使用主要地功能和python socket模块中的方法去写一个自己的客户端-服务器应用。这包括展示给你看如何使用一个自定义的类去发送信息和数据在您可以为自己的应用程序构建和利用的端点之间。
Socket 应用最常见的类型就是 客户端/服务器 应用,服务器用来等待客户端的链接。我们教程中涉及到的就是这类应用。更明确地说,我们将看到用于 InternetSocket 的 Socket API,有时称为 Berkeley 或 BSD Socket。当然也有 Unix domain sockets —— 一种用于 同一主机 进程间的通信。
套接字API概括
本模块主要的socket API函数和方法有:
- socket()
- bind()
- listen()
- accept()
- connect()
- connect_ex()
- send()
- recv()
- close()
TCP Sockets
稍后您将看到,我们将使用socket.socket()创建一个套接字对象,并将套接字类型指定为socket. sock_stream。当您这样做时,使用的默认协议是传输控制协议(TCP)。这是一个很好的默认值,可能也是您想要的。
为什么选择TCP:
- 可靠性 丢包可以被检测并且重发
- 顺序提交数据 从发送者得到的数据是按顺序提交的
相反,用户数据报协议(UDP)套接字是用套接字创建的。SOCK_DGRAM不可靠,接收方读取的数据可能与发送方的写入顺序不一致。
网络设备(例如路由器和交换机)具有有限的可用带宽和它们自身固有的系统限制。它们有cpu、内存、总线和接口包缓冲区,就像我们的客户机和服务器一样。TCP使您不必担心包丢失、数据无序到达以及在跨网络通信时经常发生的许多其他事情。
TCP数据流
一个监听的套接字,只做监听,响应客户端的信息。调用accept()完成链接。
客户端调用connect()去创建链接开始三方握手。握手步骤很重要,因为它确保连接的每一端在网络中都是可访问的,换句话说,客户机可以到达服务器,反之亦然。可能只有一台主机、客户机或服务器可以到达另一台主机。
中间是往返部分,在这里,客户机和服务器之间使用send()和recv()调用交换数据。
在底部,客户机和服务器关闭各自的套接字。
Echo服务器
#!/usr/bin/env python3
import socket
HOST = '127.0.0.1' # Standard loopback interface address (localhost)
PORT = 65432 # Port to listen on (non-privileged ports are > 1023)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind((HOST, PORT))
s.listen()
conn, addr = s.accept(