Python 套接字编程

17 Mar 2025 | 37 分钟阅读

Python 是最通用的语言,它拥有广泛的库,几乎应用于所有热门领域。如我们所知,Python 语法简单,用户界面友好,使得开发或数据分析变得直观。

Python 也兼容网络编程,并且包含许多提供对特定应用层网络协议(如 HTTP、FTP 等)的更高级访问的库。首先我们需要理解网络的基本术语。

近年来,Python 套接字编程的改进集中在提高性能、安全性和可扩展性。集成异步框架和库,如 asyncio 和 aiohttp,使得能够有效地进行并发网络编程。

Python 程序员越来越多地使用异步编程,因为它能够创建并发、非阻塞代码,从而有效地处理大量连接。Python 的 asyncio 包为异步编程提供了基础,它支持事件循环和协程的开发。在套接字编程中,asyncio 允许处理大量连接而无需线程或进程,从而提高了性能和资源利用率。

除了性能提升之外,安全性也已成为套接字编程的重要组成部分。Python 提供了多种安全网络连接的模块和协议,包括 TLS(传输层安全)和 SSL(安全套接字层)。这些加密技术可以保护在网络上传输的数据免遭未经授权的访问和篡改。通过在 Python 套接字编程中包含安全通信协议,可以更轻松地保护敏感信息并保持网络系统的完整性。

此外,可扩展性一直是现代套接字编程的关键因素。常用的 Python Web 开发框架,如 Flask 和 Django,提供了管理网络请求和响应的内置功能。这些框架处理套接字编程的复杂性,使程序员能够专注于创建具有负载均衡、缓存和分布式计算等功能的、可扩展的 Web 应用程序。

什么是计算机网络?

计算机网络允许计算机系统或设备之间的资源共享和通信。连接这些设备并允许它们之间交换信息的基础设施被称为计算机网络。为了促进通信和协作,必须建立网络连接。

让我们以打电话为例来更好地理解计算机网络。当我们打电话时,网络服务提供商必须在我们设备和接收者设备之间建立连接。计算机网络也需要类似的要求,设备必须连接在一起以交换数据并访问共享资源。

根据大小和覆盖区域,计算机网络可分为几种类型。常见的网络类型包括:

  • 局域网 (LAN): LAN 是覆盖有限地理区域的网络,例如校园、办公楼或其他类似结构。它连接了受限空间内的设备,实现了快速有效的通信。
  • 广域网 (WAN): WAN 连接多个 LAN,通常跨越更大的地理区域。它使得设备能够跨越城镇、国家甚至大陆相互通信。互联网就是一个广泛的全球性 WAN 的例子。
  • 城域网 (MAN): 在地理覆盖范围方面,MAN 介于 LAN 和 WAN 之间。它连接了整个城市或都市区的设备,实现了更广泛的资源共享和通信。
  • PAN: PAN(个人区域网络)是连接个人空间内设备的网络,例如智能手机、智能手表和电脑。
  • 无线网络: 无线网络使用 Wi-Fi、蓝牙或蜂窝网络等无线通信技术,无需物理线缆即可建立连接。它们使设备能够灵活、便捷地连接和通信。

为了有效运行,计算机网络需要各种硬件和软件组件。它们包括:

  • 网卡: 网络接口卡 (NIC) 是将设备连接到网络的物理设备。它们为联网设备提供发送和接收数据的物理接口。
  • 路由器和交换机: 路由器和交换机是帮助重定向和路由网络流量的网络设备。交换机通过将数据路由到正确的接收者来促进网络内部的通信,而路由器则连接多个网络并允许它们之间进行数据传输。
  • 协议: 协议或网络标准定义了网络上设备之间通信的规则和规范。常见协议包括用于在线通信的 HTTP(超文本传输协议),以及作为互联网基础协议的 TCP/IP(传输控制协议/互联网协议)。
  • 网络安全: 为了保护敏感数据并防止未经授权的访问,确保计算机网络安全至关重要。虚拟专用网络 (VPN)、防火墙、加密和身份验证技术是网络安全方法的例子。

随着技术的发展,计算机网络也在不断发展。物联网 (IoT) 和 5G 等新兴技术通过连接越来越多的设备并实现更快、更可靠的通信,正在改变网络。

LAN

局域网 (LAN) 是一种计算机网络,它在有限的地理区域内连接少量计算机。它经常用于家庭、办公室、小型企业和机构等场所。LAN 中的设备通常以点对点方式互连,从而实现直接通信和资源共享。

从只有一个用户的微型网络到拥有许多用户和设备的网络,LAN 的规模各不相同。为了在小空间内连接设备并实现顺畅的通信和协作,LAN 提供了一种实用且经济的选择。

随着网络技术的发展,LAN 经历了巨大的变革。如今,无线 LAN (WLAN) 越来越普遍,因为它们在有限区域内提供了无线连接的灵活性。WLAN 使用 Wi-Fi 技术,允许设备在不使用物理连接的情况下连接到网络。

WAN

广域网 (WAN) 连接了多个局域网 (LAN),并覆盖了广阔的地理区域。互联网就是一个例子,它使得跨越远距离的通信成为可能。在 WAN 上进行数据包交换时,数据被分成更小的数据包进行独立传输。这种方法实现了并发数据流和最优网络资源利用。

WAN 对于分支机构分散的企业至关重要,因为它们能够实现分支机构之间的顺畅通信和信息交换。它们在分布式系统和云计算中也起着至关重要的作用,可以访问远程资源。通过数据压缩等 WAN 优化方法可以提高性能。光纤和 5G 网络等创新技术增加了 WAN 带宽和速度。软件定义 WAN (SD-WAN) 解决方案提高了灵活性并实现了集中管理。为了满足日益增长的全球连接需求,WAN 将继续发展。

MAN

城域网 (MAN) 的范围小于广域网 (WAN),但覆盖的地理区域大于局域网 (LAN)。它连接了整个城市或都市区域内计算机用户和资源。

MAN 允许在定义的都会区域内集成多个 LAN 或其他网络技术,将通信范围扩展到单个 LAN 的限制之外。它为附近的业务或机构提供了共享资源、交换信息和改进通信的方式。

连接城市内多个建筑、大学或工作场所的网络基础设施是 MAN 的典型例子。MAN 通过构建一个更广阔的网络,实现了链接站点之间的高效资源共享和通信。它提供了比 LAN 更高的带宽和更广的覆盖范围,适合需要特定城市或郊区区域内更广泛连接的应用。

PAN

个人数字助理 (PDA)、PC、手机和平板电脑都可以通过个人区域网络 (PAN) 进行通信。PAN 的主要功能是促进各种个人设备之间的数据共享和通信。

PAN 可用于多种用途,例如将设备连接到其他设备或将它们连接到更高级别的网络。例如,PAN 可以创建笔记本电脑和智能手机之间的无线连接,以共享数据并连接到 Internet。此外,它还可以用于将设备连接到更大的网络,例如 Internet 或 LAN。

为了创建 PAN,通常会使用蓝牙技术。蓝牙允许设备之间进行短距离无线通信,通常在 10 米范围内。启用蓝牙的设备可以相互连接和配对以创建 PAN。

PAN 允许用户方便灵活地连接到自己的设备并与之通信。在有限的 PAN 范围内,它们可以实现无缝的数据传输、文件共享和设备同步。当多个个人设备需要相互通信或连接到更大的网络时,PAN 特别有用。

无线网络

无线网络是指使用无线通信技术来传输数据并建立设备之间连接的网络,因为它们不需要物理电缆或有线连接。这些网络允许设备无线连接和交换数据,从而在各种场景中提供灵活性、便携性和便利性。

设备之间的数据传输通过无线网络进行,使用无线电波、红外信号和其他无线通信方法。Wi-Fi 是最流行的无线网络技术。Wi-Fi 允许设备加入本地无线网络(通常由无线路由器提供),并连接到该网络以访问 Internet 或与网络上的其他设备通信。Wi-Fi 网络现在已遍布家庭、企业、公共场所和其他场所,为智能手机、笔记本电脑、平板电脑和物联网设备等各种设备提供无线访问。

蓝牙是另一种广泛使用的无线技术,特别适用于设备之间的近距离通信。蓝牙支持附近设备之间的无线连接,允许您将智能手机连接到无线耳机或在它们之间传输文件。

另一个无线网络是蜂窝网络(包括 3G、4G 和 5G 网络),它为大地理区域内的设备提供 Internet 访问和移动通信。蜂窝网络需要一个基站或蜂窝塔网络来传输和接收设备与网络基础设施之间的信号。这使用户能够在移动中拨打电话、发送短信和访问 Internet。

计算机网络术语

这些术语对于网络编程非常重要。如果您不熟悉它们,请先理解它们,然后再进行后续教程。

互联网协议

互联网协议 (IP) 是一套管理通过 Internet 进行数据传输和接收的规则和过程。它是 Internet 协议家族的基石,也是在线通信的基础。

IP 为每个连接的设备分配一个不同的 IP 地址,以提供唯一的标识系统。IP 地址是允许在网络上标识和定位设备的数字。IPv4(互联网协议版本 4)和 IPv6(互联网协议版本 6)是目前使用的两个 IP 版本。IPv6 地址由八组由冒号分隔的四位十六进制数字组成,而 IPv4 地址由句点分隔的四个数字组成。

例如 - 有两个朋友 **A** 和 **B**。假设 **A** 想和 **B** 通信。然后,他写了一封信,去了 **邮局**(一种通信网络)。他把信装进信封,然后交给邮局,以便寄给 **B**。他贴上了 **B** 的地址,这样信件就能到达正确的目的地。

现在,**A** 想把 15 页的脚本寄给 **B**。那么,一个信封可能装不下。所以他决定把每一页都装进单独的信封。现在,邮局可能不会按原顺序投递信件?这里互联网协议起着重要作用。

IP 负责跨网络的数据包路由。当通过 Internet 发送数据时,它会被分解成更小的数据包,其中包含必要的寻址信息和原始消息的一部分。然后,这些数据包会单独发送,并且它们经过的路径可能会有所不同。IP 使用 IP 地址在网络之间路由数据包,并处理数据包分片和重组,以确保每个数据包都传送到正确的目标。

  • UDP(用户数据报协议)
  • TCP(传输控制协议)

用户数据报协议

UDP 的一个特点是无序。接收者可能仅在发送的顺序之外收到数据报。UDP 将每个数据报视为一个独立实体,不强制规定特定的数据包序列或顺序。在传输及时性比精确顺序更重要的场景中,此特性可能很有用。

由于在数据传输之前不需要建立连接,UDP 被视为轻量级的。与需要握手过程来建立可靠连接的 TCP 不同,UDP 在尽力而为的基础上运行,没有连接管理的开销。由于其轻量级特性,UDP 在资源利用率和处理开销方面更有效。IP 负责跨网络的数据包路由。当通过 Internet 发送数据时,它会被分解成更小的数据包,其中包含必要的寻址信息和原始消息的一部分。然后,这些数据包会单独发送,并且它们经过的路径可能会有所不同。IP 使用 IP 地址在网络之间路由数据包,并处理数据包分片和重组,以确保每个数据包都传送到正确的目标。

UDP 使用的数据包称为数据报。每个数据报都作为一个独立实体来处理传输和完整性验证所需的信息。这些数据报一次发送一个,并且可能沿着多条路径到达目的地。UDP 不提供重传或错误校正技术。因此,应用程序层必须根据需要确保数据完整性和可靠性。

传输控制协议

TCP(传输控制协议)是一种面向连接的协议,可在 Internet 上可靠且有序地传输数据。它通过在开始数据传输之前,基于握手概念与另一个主机建立连接来工作。

当一个主机说“你好”时,另一个主机回应“你好”,连接就建立好了。这就是 TCP 中的握手过程。为了在网络上共享数据,两个主机都必须完成此握手。

TCP 具有一些不同于 UDP 的特性:

  • 可靠性: 由于 TCP 具有消息确认、丢失数据包重传和超时管理等机制,因此它比 UDP 更可靠。它确保数据成功传输,并在必要时允许重试。
  • 有序: TCP 确保消息按正确的顺序传输。消息以发送者发送的相同顺序传递给接收者。这确保了接收端数据的准确重构。
  • 重量级: 与 UDP 相比,TCP 有点重量级。在传输任何用户数据之前,必须通过称为三次握手的三个数据包过程建立套接字连接。这三个数据包是 SYN(同步)、SYN+ACK(确认)和 ACK(确认)。此握手过程会增加通信开销,但可确保可靠连接。

TCP 经常用于需要可靠且有序数据传输的应用程序,例如 Web 浏览、文件传输和电子邮件。它通过错误检测、流量控制和拥塞控制等方法来维护数据完整性和完整性。

IP 地址和端口

网络(包括 Internet)会为连接到它的设备分配 IP 地址,这些 IP 地址是独特的数值标识。它们充当计算机、服务器和 IoT 设备等设备的定位和标识信息。要创建在线设备之间的连接,IP 地址是必需的。

另一方面,端口是操作系统端点,用于提供应用程序或服务之间的通信。端口的数值标识用于区分设备上运行的各种服务或进程。在设备上,端口允许多个程序同时使用,并简化了它们之间的数据传输。

示例 -

在之前的例子中,A 想寄信给 B。所以 A 需要知道 B 的地址才能成功投递包裹。现在,A 拥有 B 的唯一邮寄地址,因此邮递员成功递送了信件。所以 IP 地址就像邮寄地址。

一个系统可能运行着成千上万的服务,但我们可以通过**端口号**唯一地识别一项服务。一个系统总共有 0-65535 个端口。

您可以将 IP 地址比作建筑物的地址,将端口比作该建筑物内的房间号,以此为例。设备由其 IP 地址标识,端口则标识其上运行的程序或服务。

IP 地址和端口协同工作,以实现联网设备和应用程序之间的精确通信。通过 Internet 传输数据时,数据会被发送到特定的 IP 地址,并发送到目标设备上的特定端口,该端口连接到特定的应用程序或服务。然后,目标应用程序或服务可以正确接收和处理数据。

有时,端口号也可以在 Web 或其他统一资源定位符 (URL) 中看到。默认情况下,HTTP 使用端口 80,HTTPS 使用端口 443。以下是常见端口的示例。

端口号描述
22安全外壳
23Telnet 远程登录服务
25简单邮件传输协议 (SMTP)
53域名系统 (DNS) 服务
80超文本传输协议 (HTTP),用于万维网 (WWW)。

IP 地址有两种类型。

私有 IP 地址: 本地网络(如家庭或企业网络)使用私有 IP 地址,这些地址无法从 Internet 访问。通常使用三个私有 IP 地址范围:

  1. 168.0.0 - 192.168.255.255
  2. 16.0.0 - 172.31.255.255
  3. 0.0.0 - 10.255.255.255

得益于这些私有 IP 地址,本地网络中的设备可以在不直接使用 Internet 的情况下进行连接。它们经常用于本地网络 (LAN)、家庭网络和小型企业。

公共 IP 地址: 另一方面,Internet 服务提供商 (ISP) 会为连接到 Internet 的路由器或网关设备分配一个公共 IP 地址。它是唯一的并且是全局可路由的,允许连接到 Internet 的设备与外部世界进行通信。设备要能够通过 Internet 访问,并能够访问网站和在线服务,公共 IP 地址是必需的。

ISP 提供公共 IP 地址,这些地址可以是动态的(可能更改)或静态的(永久的)。静态公共 IP 地址经常用于需要持续可访问性的服务,例如 Web 服务器或远程网络访问。相比之下,动态公共 IP 地址可能经常更改。

防火墙

防火墙是内部网络和外部源(如 Internet)之间的屏障,对网络安全至关重要。其主要职责是监控传入和传出的网络流量,并执行预定的安全规则集。防火墙根据定义好的规则评估数据包并决定允许或阻止流量。

防火墙的职责是防御内部网络风险,例如恶意攻击、未经授权的访问和数据泄露。它通过检查每个数据包的源和目标地址、端口号和其他相关数据来执行过滤功能。只有符合预设安全标准的封包才被允许通过,而可疑或可能有害的封包则被阻止。

例如

假设 IP 地址是房子,端口号是房子里的房间号。只有经过身份验证的人(源地址)才能进入房子(目标地址)。

为什么选择 Python 进行网络编程?

Python 是一种强大且适应性强的编程语言,已在网络编程领域变得极其受欢迎。选择 Python 作为网络编程的偏好有几个原因,在本解释中,我们将探讨一些关键因素。

首先,Python 因其可读性和简洁性而成为网络编程的绝佳语言。其清晰自然的语法允许工程师编写易于理解和维护的代码。由于其简单性,Python 代码出错的可能性较小。此外,Python 广泛的标准库提供了全面的网络特定模块和函数集,使得实现网络协议和通信变得更加简单。

其次,Python 的跨平台兼容性使其成为网络编程的绝佳选择。Python 程序无需大量修改即可在 Windows、macOS 和 Linux 操作系统上运行。由于网络通常包含异构设备和系统,因此这种灵活性在网络编程中至关重要。由于 Python 是可移植的,因此可以轻松地在各种平台上安装和运行网络应用程序。这使得开发和维护应用程序更加容易。

Python 广泛的生态系统和社区支持是另一个显著优势。Python 有许多第三方库和框架可供使用,它们可以极大地帮助完成网络编程任务。像“socket”和“asyncio”这样的库提供了高级网络通信抽象,使得处理套接字和实现网络协议更加容易。像“Curved”和“Django”这样的框架提供了构建复杂网络应用程序(包括 Web 服务器和 API)的全面工具和组件。由于这些资源的可用性,开发人员可以使用现有的解决方案来加速开发并减少总体工作量。

此外,Python 的适应性使其能够与其他技术和语言无缝集成。它支持多种语言间通信机制,如 API、套接字和消息队列,使网络应用程序能够与用不同语言编写的组件进行交互。当现有的网络基础设施或工具不是用 Python 编写时,这种能力尤其有用。Python 与其他技术协同工作的能力使其能够更轻松地集成和无缝协作,从而使网络编程任务整体上更有效。

最后,Python 在网络编程领域的普及得益于其广泛的文档和庞大的社区。活跃的 Python 社区拥有众多的在线资源、教程和论坛,开发人员可以在其中寻求指导和分享知识。开发人员可以轻松地访问信息并找到常见网络问题的解决方案,这得益于广泛的文档。

套接字编程基础

我们已经学习了网络的基本概念并理解了基本的网络术语。在开始 Python 网络编程之前,我们应该先了解套接字介绍。

网络和套接字编程是庞大的主题。计算机网络有很多主题需要探索,但在这里我们讨论使用 Python 进行的基本网络编程。

让我们来理解套接字,看看它们是什么以及为什么使用它们。

什么是套接字?

首先,我们需要了解互联网连接。互联网连接基本上用于在 Internet 上连接两个端点以进行数据共享和其他事务。

套接字是一种软件接口,它允许两台计算机通过网络(如 Internet)进行通信。它是不同设备上程序之间进行数据传输和接收的端点。互联网连接提供了从计算机 C1 的一个进程与计算机 C2 的一个进程通信的功能。它包含以下特性:

可靠: 套接字通过互联网连接提供了可靠的数据传输方法。它们提供了准确无误的数据传输,同时在客户端和服务器之间建立了安全的通信路径。

点对点: 套接字以点对点方式连接两个特定端点。由于可以通过其 IP 地址和端口号识别每个端点,因此数据可以传输并接收到所需位置。

全双工: 全双工通信是套接字的主要特性之一。这意味着数据可以在客户端和服务器之间双向同时传输。客户端和服务器都可以发送和接收数据,从而实现交互式和双向通信。

套接字提供双向、点对点通信通道的端点,这对于网络通信至关重要。它们通常用于通过网络传输消息。当客户端(如 Web 浏览器)连接到服务器(如 www.javatpoint.com)时,涉及两个端点:客户端套接字和服务器套接字。

套接字的概念最早出现在 1971 年,后来发展成为 Berkeley 套接字应用程序编程接口 (API),并于 1983 年被包含在 Berkeley 软件发行版 (BSD) 操作系统中。

随着 Internet 和万维网的推出,客户端-服务器应用程序在 1990 年得到了广泛应用,这促进了套接字的使用。在客户端-服务器应用程序中,一方充当服务器,等待客户端连接。这种方法允许 Web 服务器、电子邮件服务器和数据库服务器等众多网络服务有效地管理许多客户。

套接字允许使用多种协议在客户端和服务器之间进行通信,包括用户数据报协议 (UDP) 和传输控制协议 (TCP)。由于可靠的面向连接的 TCP 协议,数据包将始终按正确的顺序发送。它在客户端和服务器之间创建连接,确保数据传输的准确性和可靠性。相反,UDP 是一种轻量级、无连接的协议,它将速度置于可靠性之上。低延迟对于实时应用程序至关重要,通常在这些应用程序中使用。

套接字编程不需要特定的编程语言。它与 Python、Java、C++ 和 C# 等多种语言兼容,使其可供在多个平台上工作的开发人员使用。

为了支持新技术,套接字编程近年来得到了发展。例如,IPv6 的使用改进了连接和寻址,以适应越来越多的设备连接到 Internet。

套接字 API 函数

套接字 API(应用程序编程接口)提供了一组函数,应用程序程序员可以使用它们来构建、配置和管理其网络应用程序中的套接字。这些活动包括创建套接字、绑定到特定地址、建立连接、交换数据以及关闭套接字等。以下是一些常用的套接字 API 函数:

  1. socket(): 使用 socket() 方法创建一个新套接字,它提供一个套接字描述符,可用于后续操作。它接受指示套接字类型(如 TCP 的 SOCK_STREAM 或 UDP 的 SOCK_DGRAM)和地址族(如 IPv4 的 AF_INET 或 IPv6 的 AF_INET6)的参数。
  2. bind(): 使用 bind() 方法,可以将套接字绑定到网络上的特定 IP 地址或端口。服务器端通常在这里指定服务器将监听入站连接的 IP。
  3. listen(): 使用 listen() 方法可以将套接字标记为被动套接字,以接受入站连接。该方法指定套接字在拒绝连接之前的待处理连接的最大容量。
  4. accept(): 使用 accept() 方法,监听套接字可以接受入站连接请求。它为已接受的连接创建一个新套接字并返回一个新的套接字描述符。此函数通常在服务器端使用。
  5. connect(): 此方法启动客户端套接字连接到远程服务器。它使用远程服务器的地址创建连接。
  6. send() 和 recv(): 这两个函数-send() 和 recv()-用于通过套接字发送和接收数据。它们为客户端和服务器提供了发送和接收消息的手段。对于 TCP 等面向连接的套接字,这些操作提供了及时可靠的数据传输。
  7. sendto() 和 recvfrom(): 与 send() 和 recv() 不同,sendto() 和 recvfrom() 用于无连接套接字,例如 UDP。它们允许在不首先建立连接的情况下发送和接收数据。
  8. close(): 使用 close() 方法关闭套接字并释放所有相关资源。对于已连接的套接字,它会中断连接并阻止进一步通信。
  9. getaddrinfo(): 根据主机名、端口和其他因素,此函数用于编译一组合适的网络地址。它同时提供 IPv4 和 IPv6 网络地址解析的灵活性。
  10. setsockopt(): 此方法允许修改套接字选项。它允许精确控制各种套接字属性,包括将套接字设置为重用地址、更改套接字缓冲区大小以及启用多播选项。
  11. getsockopt(): 使用 getsockopt() 方法获取套接字选项的当前值。它允许查询套接字选项的当前配置或状态信息,例如查找超时值或特定套接字标志的值。
  12. shutdown(): 在 shutdown() 方法的帮助下,可以优雅地终止套接字连接。它可以终止连接的发送端或接收端,或者两者都终止。在关闭套接字之前,它会检查所有未完成的数据是否已传输。
  13. select(): 此函数允许在至少一个套接字准备好读取、写入或出现异常情况之前,监视多个套接字。它有助于有效且同时地管理多个套接字。
  14. gethostbyname() 和 gethostbyaddr(): DNS(域名系统)解析使用这些函数。它们将 IP 地址转换为主机名(gethostbyname())或将主机名转换为 IP 地址(gethostbyaddr())。
  15. inet_ntop() 和 inet_pton():这两个函数 inet_ntop() 和 inet_pton() 方便地将人类可读的 IP 地址从字符串格式转换为二进制格式。在网络编程中使用 IP 地址时,它们很有用。
  16. fcntl(): 此函数提供各种操作文件描述符的功能,例如设置其标志或切换其非阻塞模式。

创建套接字

当我们单击将我们带到特定页面的链接时。Web 浏览器充当以下方式:

Client.py

上面的代码将在客户端工作。当客户端尝试与服务器通信时,操作系统会为连接分配一个**临时端口**。临时端口只不过是操作系统分配的一个随机端口。客户端套接字在数据交换完成后立即关闭。客户端套接字仅用于一次。

服务器创建一个比客户端套接字稍复杂的服务器套接字。让我们看看服务器端发生了什么。

Server.py

客户端和服务器套接字之间的区别

客户端套接字

  • 客户端程序使用客户端套接字连接到服务器。
  • 它们通过向服务器发出查询来启动对话。然后它们收到回复。
  • 客户端连接是临时的,并且是为特定交互或活动而建立的。
  • 它们通常使用操作系统动态分配的临时端口进行通信。
  • 发送到服务器并接收的数据必须由客户端套接字处理。

服务器套接字

  • 服务器上的应用程序使用服务器套接字来检查新的客户端连接。
  • 它们等待客户端的连接,并同时处理多个客户端连接。
  • 服务器套接字寿命长,旨在始终可用。
  • 它们通过监听与特定服务或协议关联的指定端口来工作。
  • 服务器套接字负责接收入站客户端连接并响应服务或数据。
  • 它们通常可以通过为每个连接设置不同的线程或进程来管理多个客户端连接。

Python 套接字模块

Python 套接字模块提供了创建和使用套接字所需的方法。Socket.socket() 是创建套接字的主要方法。socket() 函数的语法如下:

socket() 方法的参数如下:

  • socket_family: socket_family 指定套接字的地址族。它可以是 AF_UNIX(用于 Unix 域套接字)或 AF_INET(用于 Internet 域)。
  • socket_type: socket_type 指定套接字的类型。对于 TCP 套接字,可以是 SOCK_STREAM;对于 UDP 套接字,可以是 SOCK_DGRAM。
  • protocol: 指定要使用的特定协议。通常设置为 0,允许操作系统根据提供的 socket_type 选择合适的协议。

在大多数情况下,我们将使用 AF_INET 套接字(用于 Internet 域)和 SOCK_STREAM 套接字(用于 TCP)。创建套接字后,我们可以使用套接字模块提供的各种方法与套接字交互,例如 bind()、connect()、listen()、send()、recv() 等。

这些方法允许我们将套接字绑定到特定地址和端口、建立到远程服务器的连接、监听入站连接、发送数据、接收数据以及执行其他套接字相关操作。

在客户端-服务器套接字模型中,客户端套接字连接到服务器,而服务器套接字等待入站连接。客户端套接字使用 connect() 方法建立连接,而服务器套接字使用 bind() 和 listen() 方法设置套接字。

在创建使用套接字通信的网络应用程序时,理解和使用 Python 套接字模块的功能至关重要。

客户端套接字方法

下面是客户端套接字方法。

connect()

此函数用于在指定地址设置到远程套接字的连接。地址格式包含用于 **AF_INET** 地址族的**主机和端口对**。

服务器套接字方法

下面是服务器套接字方法。

bind()

此方法用于将套接字绑定到地址。地址的格式取决于上面提到的套接字族(AF_INET)。

listen(backlog)

此方法用于监听对套接字的连接。backlog 是一个术语,用于表示在拒绝连接之前必须监听的排队连接的最大数量。

accepts()

accepts() 方法接受一个连接。套接字应已绑定到地址并准备好监听连接。它返回 **pair(conn, address)**,其中 **con** 是一个新套接字对象,可用于连接上的**发送**和**接收**数据,而 **address** 是与连接另一端的套接字关联的地址。

一些常用套接字方法

下面给出了一些常用的服务器对象函数。


TCP 套接字方法UDP 套接字方法
Server_Object.recv() - 接收 TCP 消息Server_Object.recvfrom() - 接收 UDP 消息。
Server_Object.send() - 发送 TCP 消息Server_Object.sendto() - 发送 UDP 消息。

Socket 类型

在网络中,有两种套接字:SOCK_STREAM 和 SOCK_DGRAM。让我们更详细地研究每种类型:

SOCK_STREAM

  • SOCK_STREAM 套接字用于基于 TCP(传输控制协议)的通信。
  • 它们提供可靠的、面向连接的数据流。
  • 数据通过 SOCK_STREAM 以连续流的形式传输,确保其到达顺序与发送顺序相同。
  • 这些套接字确保数据传递,并自动处理任何重传或丢失的数据包。
  • 需要可靠且有序数据传递的应用程序,例如 Web 浏览、文件传输、电子邮件和实时通信协议(如 HTTP、FTP 和 SSH),经常使用 SOCK_STREAM 套接字。
  • 由于通信是双向的,因此数据可以双向发送和接收。

SOCK_DGRAM

  • SOCK_DGRAM 套接字用于基于 UDP(用户数据报协议)的通信。
  • 它们提供不稳定的、无连接的数据报服务。
  • 数据通过 SOCK_DGRAM 以称为数据报的独立数据包的形式传输,每个数据报都是一个独立的通信单元。
  • 数据报在传输过程中可能会丢失或重复,并且不能保证它们会按发送时的顺序接收。
  • 能够容忍偶尔的数据包丢失或乱序交付的应用程序,如实时多媒体流、在线游戏、DNS 和 SNMP,经常使用 SOCK_DGRAM 套接字。
  • 数据只能在单向通信过程中发送。

TCP 套接字

在本节中,我们将使用 **socket.socket()** 函数创建套接字对象,并将套接字类型指定为 **socket.SOCK_STREAM**。正如我们所知,协议对于将数据从一端发送到另一端至关重要。这里使用 TCP(传输控制协议)作为默认协议。它高效且可靠。我们可以考虑这种协议,因为:

  • 它可靠 - 如果在传输到另一端时数据包丢失,则发送方会重新传输。
  • 有序数据交付 - 消息以与发送者编写时相同的顺序发送。

另一方面,使用 socket.SOCK_DGRAM 创建的 UDP(用户数据报协议)套接字不可靠。接收者读取的数据顺序可能与发送者编写的顺序不同。

让我们通过以下简单的客户端-服务器程序示例来理解。

简单服务器程序

示例 -

上面的代码目前不会执行任何操作。它会等待客户端连接到指定端口。如果程序没有错误,它将给出以下输出:

同样,我们访问的每个站点都有一个托管它的服务器。

现在,我们将创建一个 **client.py** 程序来连接 **server.py** 文件。

简单客户端程序

考虑以下 **client.py** 程序。客户端尝试建立到服务器端口的连接;我们分配了 9999 这个知名端口。

示例 -

要获得结果,请先运行 **server.py** 文件,然后运行 **client.py** 文件。如果没有错误,它将给出以下输出:

注意 - 这里,客户端和服务器文件在同一台机器上运行,但在现实生活中,服务器位于不同的地方。这里要指出的是 client.py 已终止,但 server.py 仍在运行。这在现实生活中也会发生。

例如 - 当您请求 javatpoint.com 时,它会满足请求,其服务器会持续在后台运行(24*7)。

Python Internet 模块

以下是与网络编程相关的 Python 模块列表。

协议通用功能端口号Python 模块
HTTP网页80httplib, urllib, xmlrpclib
NNTPUsenet 新闻119nntplib
FTP文件传输20ftplib, urllib
SMTP发送电子邮件25smtplib
POP3获取电子邮件110poplib
IMAP4获取电子邮件143imaplib
Telnet命令行23telnetlib
地鼠文档传输70gopherlib, urllib

该表列出了常见的网络协议、它们相关的函数、默认端口号以及可用于网络编程的相应 Python 模块。让我们解释每个条目:

1. HTTP(超文本传输协议)

用于检索和传输网页及相关文件。

默认端口号:80。

Python 模块:httplib、urllib、xmlrpclib。

2. NNTP(网络新闻传输协议)

用于访问和发布 Usenet 新闻文章。

默认端口号:119。

Python 模块:nntplib。

3. FTP(文件传输协议)

用于在网络上的主机之间传输文件。

默认端口号:20(数据传输)、21(控制)。

Python 模块:ftplib、urllib。

4. SMTP(简单邮件传输协议)

用于在服务器之间发送电子邮件。

默认端口号:25。

Python 模块:smtplib。

5. POP3(邮局协议版本 3)

用于从远程服务器检索电子邮件。

默认端口号:110。

Python 模块:poplib。

6. IMAP4(Internet 消息访问协议版本 4)

用于从远程服务器检索电子邮件,功能比 POP3 更高级。

默认端口号:143。

Python 模块:imaplib。

7. Telnet

用于建立远程命令行连接。

默认端口号:23。

Python 模块:telnetlib。

8. Gopher

用于从远程服务器检索文档。

默认端口号:70。

Python 模块:gopherlib、urllib。

这些 Python 模块提供了与相关网络协议交互的实用类和方法。它们允许程序员使用 Python 执行网络相关操作,包括执行 HTTP 请求、检索电子邮件、移动文件等。

使用 UDP 套接字

我们已经了解到,如果我们不指定套接字族和套接字类型,默认情况下它是 TCP。要创建 UDP 套接字,我们需要显式指定套接字族和套接字类型。让我们理解以下语法。

语法 -

让我们通过以下 UDP 服务器程序来理解。

UDP 服务器程序

编写以下脚本并将其保存为 server.py

示例 -

输出

Waiting for client

UDP 客户端程序

将此文件保存为 udpclient.py

示例 -

输出

Waiting for client.... 
Received Messages: Welcome To JavaTpoint from('192.168.43.217,5342')
Waiting for client

我们已经讨论了两种套接字,并建立了客户端-服务器套接字之间的连接。

Echo 客户端和服务器

Echo 服务器是一个简单的网络应用程序,客户端可以通过它向服务器提交消息或请求,服务器会响应并将相同的消息或请求发送回客户端。它演示了一个基本的客户端-服务器通信范例。

Echo 服务器的工作方式如下:

  • 服务器监视特定网络端口以接收入站客户端连接。
  • 当客户端连接到服务器时,会创建一个套接字以进行客户端和服务器之间的通信。
  • 客户端通过套接字与服务器通信或请求某项内容。
  • 消息从客户端传输到服务器。
  • 服务器处理接收到的消息(在 echo 服务器的情况下,可能根本不进行任何处理)。
  • 服务器通过套接字将相同消息或响应发送回客户端。
  • 客户端接收服务器回显的消息或响应。
  • 然后客户端可以发送更多消息或断开与服务器的通信。

Echo 服务器通常用于测试和调试,因为它允许客户端确认服务器正在成功接收和处理消息。

Python 的套接字模块可用于创建 echo 服务器和客户端。当收到消息时,服务器代码会等待入站连接并将消息回显给客户端。客户端代码会与服务器建立连接,发送消息,然后显示回显的响应。

使用输入和输出设备

为了管理 Echo 服务器和客户端之间的数据流以建立通信,需要输入和输出流。这些流允许客户端和服务器之间的通信,客户端从服务器接收消息的回显。

输入流表示从客户端到服务器的数据流。服务器使用它来接收客户端发送的消息。服务器在从输入流读取数据后对其进行处理。

输出流表示从服务器到客户端的数据流。服务器在从客户端接收消息后,将相同消息或响应发布到输出流。然后客户端可以从输出流中读取回显的消息或响应。

这些输入和输出流允许客户端和服务器双向通信并交换数据。服务器可以接收多个客户端消息,然后分别回显每个消息。

让我们理解以下示例

Echo 服务器

解释 -

**socket.socket()** 函数创建支持**上下文管理器类型**的套接字对象,我们也可以在 **with** 语句中使用它,而无需调用 **close()** 函数。

我们已经提到了地址族和**套接字类型。AF_INET** 是 **IPv4** 的 Internet 地址族。SOCK_STREAM 是 TCP 协议的套接字类型,用于在网络上传输我们的消息。

我们使用了 **bind()** 函数,它用于将套接字连接到显式网络接口和端口号。

在此,**host_name** 代表**主机名**、IP 地址**或空字符串**。主机必须是 IPv4 格式的地址字符串。

**port** 号码可以是一个介于 1 到 65535 之间的整数(0 已保留)。它表示用于接受客户端连接的 TCP 端口号。如果端口小于 1024,某些系统可能需要超级用户身份验证。

接下来,我们使用了 **listen()** 函数,它允许服务器**接受 ()** 连接。

默认情况下,**listen()** 函数包含 **backlog** 参数。**backlog** 参数表示在**拒绝**新连接之前系统允许排队的挂起连接的最大数量。

backlog 有助于管理挂起连接的队列的最大长度。

当客户端建立连接时,它会使用 **accept()** 函数返回一个新的套接字对象。

现在,我们从 accept() 函数获得了一个套接字对象。重要的是要记住,我们将用于通信的套接字必须与服务器用于接受新连接的监听套接字不同。

一旦我们完成了从 **accept()** 函数获取 conn 对象的流程。我们使用无限 while 循环处理阻塞调用 **conn.recv()**。客户端发送消息并使用 **conn.sendall()** 将其回显回去。

Echo 客户端

我们正在描述 echo 客户端程序,并将其保存为 echo-client.py。

程序 -

说明

与 echo 服务器相比,echo 客户端非常简单。它链接一个套接字对象,连接到服务器,然后调用 **s.sendall()** 来发送消息。使用 **s.recv()** 来读取服务器回复,然后我们可以打印它。

阻塞和非阻塞套接字

在前面的章节中,我们学习了客户端向服务器发送请求,服务器处理请求;使用套接字(TCP/UDP)发送响应。

阻塞套接字 I/O

在处理 TCP 套接字时,它们通常默认配置为阻塞模式。在阻塞模式下,程序的执行会暂停,直到某个操作完成。例如,在执行 connect() 函数连接到服务器时,程序将暂停并等待连接建立。

在某些情况下,例如当我们希望确保某个操作在继续进行下一项操作之前已完成时,阻塞 I/O 可能很有用。但是,如果过程花费的时间比预期长,则可能导致程序执行延迟。

在某些情况下,我们可能需要添加一个系统来处理或中断正在使用的活动连接。在这种情况下,非阻塞模式很有用。通过将套接字设置为非阻塞模式,我们可以允许程序在等待特定活动完成的同时继续运行。这为套接字操作提供了更大的灵活性和控制力。

非阻塞套接字 I/O

要在 Python 中切换套接字的阻塞和非阻塞模式,请使用 setblocking() 函数。通过将套接字切换到非阻塞模式,我们可以添加额外的逻辑来处理中断或执行其他活动,同时等待套接字操作完成。

非阻塞模式对充分的错误检查和与非阻塞 I/O 操作相关的异常处理提出了要求。因此,强调它需要谨慎处理至关重要。

让我们通过以下**阻塞套接字**的例子来理解。

保存文件 blockclient.py

程序

现在,理解以下 blockserver.py。

程序

首先,运行 blockServer.py 文件,然后运行 blockClient.py。服务器将持续打印消息 **Hello JavaTpoint.**,直到所有数据发送完毕。在上面的代码中,客户端不会长时间打印 **Hello JavaTpoint**。这需要很长时间,因为客户端需要发送大量字符串。直到套接字输入/输出被阻塞为止。

当发送缓冲区变满时,**send()** 函数用于将数据传送到服务器。内核将暂停进程,直到缓冲区中的数据传输到客户端并且缓冲区再次为空。一旦缓冲区变空,内核将重新启动其进程以获取要传输的下一块数据。

现在考虑一个非阻塞套接字。

程序

当我们运行 **non_block_client.py** 时,程序将运行一小段时间,它会打印“所有数据已接收”并快速完成。

如果我们通过 **setblocking(0)** 创建非阻塞套接字,它将永远不会等待操作完成。因此,当我们调用 send() 函数时,它会尝试将最大数量的数据放入缓冲区。

关闭连接

当终止连接时,通常建议在套接字上调用 **close()** 函数。使用 **close()** 通常就足够了,并且关闭不需要显式调用 **shutdown()** 函数。

然而,在某些情况下——尤其是在处理类似 HTTP 的交换时——使用 **shutdown()** 方法可能很有用。例如,如果客户端使用 **shutdown(1)**(表示它已完成数据发送),它仍然可以接收数据。服务器在收到 0 字节时,会识别出“EOF”(文件结束)情况,这表示客户端已完成其请求。然后,如果需要,服务器可以发送响应。如果传输操作成功,则会验证客户端是否能够继续接收。

如果套接字尚未显式关闭,Python 会在垃圾回收时自动关闭它。然而,不鼓励使用自动关闭和关闭。如果套接字过早关闭,可能会导致问题,例如另一个套接字发送请求的速度太慢。因此,在预期的活动完成后显式关闭套接字至关重要。

通过使用 **close()** 显式终止套接字,您可以确保执行所有必要的清理和终止过程,从而避免任何潜在的连接问题。

Tornado 框架

Tornado 是一个强大的 Python Web 框架和异步网络模块。它旨在处理高性能需求,包括一次管理数百个开放连接。其主要特点是 Tornado 的非阻塞 I/O 架构,它能够有效地管理大量连接,而无需传统的基于线程的并发。

事件驱动的方法是 Tornado 非阻塞 I/O 的基础,其中请求和响应是异步处理的。这意味着 Tornado 可以在单个线程中有效地管理大量连接,而不是为每个连接分配一个单独的线程或进程。这种方法极大地减少了上下文切换和线程同步的开销,从而提高了性能和可扩展性。

与普通套接字相比的 Tornado 框架

与传统的套接字编程相比,Tornado 提供了许多优势,使其成为许多开发人员的首选:

  • 异步 I/O: Tornado 的异步 I/O 架构旨在在单个线程内管理大量连接。因此,不再需要基于线程的并行,并且减少了上下文切换和线程同步的开销。因此,Tornado 可以同时管理大量连接,同时消耗更少的资源并实现更高的性能。
  • 可扩展性: Tornado 由于其非阻塞 I/O 设计而具有高度可扩展性。它可以在没有许多线程或进程的情况下同时处理数千个开放连接。实时应用程序和 WebSockets 是需要高性能并必须管理多个并发客户端的程序的两个示例。
  • WebSocket 支持: Tornado 集成了 WebSocket,一种用于客户端和服务器之间实时通信的流行技术。由于其异步特性,Tornado 非常适合维护持久的 WebSocket 连接,从而实现高效快速的实时应用程序。
  • 高性能: Tornado 因其设计和架构而成为高性能框架。通过减少标准套接字编程的开销,Tornado 可以实现更快的响应时间和更高的事务吞吐量。
  • 全面的功能: 路由、模板渲染、表单处理和身份验证只是 Tornado 提供的一系列用于构建 Web 应用程序的全面功能和工具。在保持其对异步和高性能网络关注的同时,它为开发可靠且功能丰富的 Web 应用程序提供了全面的框架。
  • 社区和生态系统: Tornado 的库和扩展生态系统不断壮大,其开发社区活跃。开发人员可以利用现有库、访问资源和教程,并获得社区的帮助,以增强他们的 Tornado 应用程序的功能。

总而言之,Tornado 因其异步 I/O 风格、可扩展性和 WebSocket 兼容性而成为构建高性能 Web 应用程序和实时通信系统的热门选择。它使开发人员能够创建高效快速的应用程序,同时满足大规模网络需求。

让我们通过以下简单的 Tornado **WebSocket** 示例来理解。

示例 -

输出

Socket Programming Using Python

说明

在上面的代码中,

  • 我们定义了 **ApplicationHandler** 类,该类用作请求的处理程序,并使用 **write()** 返回响应。
  • **main()** 方法是程序的入口。
  • **web.Application** 类创建 Web 应用程序的基础,并接受一系列处理程序。
  • 应用程序监听端口 5000,客户端可以使用同一端口与该应用程序通信。
  • **ioloop.IOLoop.instance().start()** 函数用于为应用程序创建一个非阻塞线程。

在本文中,我们讨论了使用 Python 进行网络编程的基本概念。我们定义了基本的网络术语,并创建了简单的服务器和客户端。网络编程是一个广阔的领域,很难在单个教程中涵盖,但我们已尽力涵盖有关使用 Python 进行网络编程的所有重要概念。