Python 套接字编程完整指南

推荐:使用 NSDT场景编辑器 快速搭建3D应用场景

 

连接设备以交换信息是网络的全部意义所在。套接字是有效网络通信的重要组成部分,因为它们是用于通过本地或全球网络以及同一台计算机上的不同进程在设备之间传输消息的基本概念。它们提供了一个低级接口,允许对要发送或接收的流量进行细粒度控制。

这种低级特性使得可以为特定用例创建性能非常高的通信通道(或自定义协议),这些用例可能存在于传统协议中,这些协议建立在套接字通信之上。

这就是套接字在依赖即时消息交换或处理大量数据的实时客户端-服务器应用程序中非常有用的原因。

在本文中,我们将介绍套接字编程的基础知识,并提供使用 Python 创建基于套接字的客户端和服务器应用程序的分步指南。因此,事不宜迟,让我们直接潜入!

网络基础知识

网络支持任何类型的通信和信息共享。

这是连接两个或多个设备以允许它们交换信息的过程。此类互连设备的集合称为网络。

我们可以在物理世界中观察到许多网络:航空公司或电力线网络或通过高速公路相互连接的城市就是一些很好的例子。

同样,信息技术领域有许多网络;其中最突出和最著名的是互联网,这是连接无数设备的全球网络网络,也是您现在可能正在使用的阅读本文的网络。

网络类型

互联网包含更多的网络,这些网络本身因规模或其他属性而异:例如,局域网 (LAN),它们通常将彼此靠近的计算机链接起来。公司或其他机构(银行、大学等)中的机器,甚至连接到路由器的家庭设备都构成了这样的网络。

还有更大或更小类型的网络,如PAN(个人局域网),可以简单地将您的智能手机通过蓝牙连接到笔记本电脑,MAN(城域网),可以互连整个城市的设备,以及WAN(广域网),可以覆盖整个国家或整个世界。是的,最大的WAN网络是互联网本身。

不言而喻,计算机网络可能非常复杂,由许多元素组成。最基本和最关键的原语之一是通信协议。

网络通信协议的类型

通信协议规定了如何以及以何种格式发送和接收信息的规则。这些协议被组装到一个层次结构中,以管理网络通信中涉及的各种任务。

换句话说,一些协议处理硬件接收、发送或路由数据包的方式,而其他协议则更高级别,例如,关注应用程序级通信等。

一些常用和广为人知的网络通信协议包括:

无线网络

链路层协议的示例,这意味着它非常靠近硬件,并负责在无线环境中将数据从一台设备物理发送到另一台设备。

IP(互联网协议)

IP 是一种网络层协议,主要负责路由数据包和 IP 寻址。

TCP(传输控制协议)

一种可靠的、面向连接的协议,可提供全双工通信并确保数据完整性和交付。这是一种传输层协议,用于管理连接、检测错误和控制信息流。

UDP(用户数据报协议)

与 TCP 来自同一协议套件的协议。主要区别在于 UDP 是一种更简单、更快速但不可靠的无连接协议,它不执行任何传递检查,并遵循“即发即弃”的范式。作为TCP,UPD也位于传输层。

HTTP(超文本传输协议)

一种应用层协议,也是网络上浏览器到服务器通信最常用的协议,特别用于为网站提供服务。不用说,您现在正在阅读的这篇文章也是通过HTTP提供的。HTTP协议建立在TCP之上,管理和传输与Web应用程序相关的信息,如用于传输元数据和cookie的标头,不同的HTTP方法(GET,POST,DELETE,UPDATE)等。

MQTT(消息队列遥测传输)

另一个应用程序级协议示例,用于处理能力和电池寿命有限的设备,在不可靠的网络条件下运行(例如,采矿现场的气体传感器或您家中的智能灯泡)。MQTT是IoT(物联网)中使用的标准消息传递协议。它既轻巧又易于使用,设计有内置的重传机制,可增强可靠性。如果您有兴趣将此协议与 Python 一起使用,可以阅读此 Python MQTT 指南,其中提供了 Paho MQTT 客户端的深入概述。

一个重要的观察结果是,上述所有协议都在引擎盖下使用套接字,但在顶部添加了自己的逻辑和数据处理。这是由于套接字是现代设备中任何网络通信的低级接口,我们将在下一节中讨论。

关键概念和术语

当然,在网络环境中还使用了许多其他重要概念和术语。以下是本教程其余部分可能出现的一些最突出问题的快速概述:

  • 数据包:计算机网络中数据传输的标准单位(可以通俗地将其与术语“消息”进行比较)。
  • 终结点:数据包到达的目标。
  • IP 地址:唯一标识网络上设备的数字标识符。IP 地址的示例为:192.168.0.0
  • 端口:一个数字标识符,用于唯一标识设备上运行的进程并处理特定的网络通信:例如,它通过HTTP为您的网站提供服务。IP 地址标识设备,端口标识应用程序(每个应用程序都是一个进程或由进程组成)。一些众所周知的端口示例是:端口 80,服务器应用程序通常使用它来管理 HTTP 流量,以及端口 443 用于 HTTPS(安全 HTTP)。
  • 网关:一种特殊的网络节点(设备),用作从一个网络到另一个网络的接入点。这些网络甚至可能使用不同的协议,因此网关可能需要执行一些协议转换。网关的一个例子可以是将家庭本地网络连接到互联网的路由器。

了解套接字

什么是套接字?

套接字是位于相同或不同机器上的不同进程之间进行通信的接口(门)。在后一种情况下,我们谈论网络套接字。

网络套接字抽象出连接管理。您可以将它们视为连接处理程序。特别是在Unix系统中,套接字只是支持相同写读操作但通过网络发送所有数据的文件。

当套接字处于侦听或连接状态时,它始终绑定到 IP 地址和标识主机(计算机/设备)和进程的端口号的组合。

套接字连接的工作原理

套接字可以侦听传入连接或自行执行出站连接。建立连接后,侦听套接字(服务器套接字)将额外绑定到连接端的 IP 和端口。

或者,创建一个新的套接字,该套接字现在绑定到侦听器和请求者的两对 IP 地址和端口号。这样,不同计算机上的两个连接的套接字可以相互识别并共享单个连接以进行数据传输,而不会阻塞侦听套接字,同时侦听套接字会继续侦听其他连接。

对于连接套接字(客户端套接字),它会在连接启动时隐式绑定到设备的 IP 地址和随机可访问的端口号。然后,在建立连接时,与另一通信端的 IP 和端口的绑定方式与侦听套接字大致相同,但不创建新套接字。

网络上下文中的套接字

在本教程中,我们关注的不是套接字实现,而是套接字在网络上下文中的含义。

可以说套接字是一个连接端点(流量目的地),它一方面与主机的 IP 地址和为其创建套接字的应用程序的端口号相关联,另一方面,它与在建立连接的另一台计算机上运行的应用程序的 IP 地址和端口相关联。

套接字编程

当我们谈论套接字编程时,我们在代码中实例化套接字对象并对其执行操作(侦听、连接、接收、发送等)。在这种情况下,套接字只是我们在程序中创建的特殊对象,它们具有处理网络连接和流量的特殊方法。

在后台,这些方法调用操作系统内核,或者更具体地说,调用网络堆栈,网络堆栈是内核中负责管理网络操作的特殊部分。

套接字和客户端-服务器通信

现在,同样重要的是要提到套接字经常出现在客户端-服务器通信的上下文中。

这个想法很简单:套接字与连接有关;它们是连接处理程序。在 Web 上,每当您想要发送或接收一些数据时,您都会启动一个连接(通过称为套接字的接口启动)。

现在,您或您尝试连接的一方充当服务器,另一方充当客户端。当服务器向客户端提供数据时,客户端会主动连接并从服务器请求数据。服务器通过侦听套接字侦听新连接,建立新连接,获取客户端的请求,并在响应中将请求的数据传达给客户端。

另一方面,客户端使用它希望连接的服务器的 IP 地址和端口创建套接字,启动连接,将其请求传达给服务器,并接收数据作为响应。客户端和服务器套接字之间的这种无缝信息交换构成了各种网络应用程序的骨干。

套接字作为网络协议的基础

套接字形成骨干的事实也意味着在其上构建和使用各种协议。非常常见的是UDP和TCP,我们已经简要讨论过了。使用这些传输协议之一的套接字称为 UDP 或 TCP 套接字。

工控机插座

除了网络套接字,还有其他类型。例如,IPC(进程间通信)套接字。IPC 套接字用于在同一台计算机上的进程之间传输数据,而网络套接字可以在网络上执行相同的操作。

IPC 套接字的好处是它们避免了构建数据包和解析发送数据的路由的大量开销。由于在IPC发送方和接收方的上下文中是本地进程,因此通过IPC套接字进行通信通常具有较低的延迟。

Unix-sockets

IPC套接字的一个很好的例子是Unix套接字,与Unix中的所有内容一样,它只是文件系统上的文件。它们不是由 IP 地址和端口标识,而是由文件系统上的文件路径标识。

网络套接字作为 IPC 套接字

请注意,如果服务器和接收方都在本地主机上(即具有 IP 地址 127.0.0.1),您也可以使用网络套接字进行进程间通信。

当然,一方面,由于与网络堆栈处理数据相关的开销,这会增加额外的延迟,但另一方面,这使我们能够不必担心底层操作系统,因为网络套接字存在并在所有系统上工作,而不是特定于给定操作系统或操作系统系列的 IPC 套接字。

Python 套接字库

对于 Python 中的套接字编程,我们使用官方内置的 Python 套接字库,该库由用于创建、管理和使用套接字的函数、常量和类组成。此库的一些常用功能包括:

  • socket():创建一个新套接字。
  • bind():将套接字关联到特定地址和端口。
  • listen():开始侦听套接字上的传入连接。
  • accept():接受来自客户端的连接并返回用于通信的新套接字。
  • connect():建立与远程服务器的连接。
  • send():通过套接字发送数据。
  • recv():从套接字接收数据。
  • close():关闭套接字连接。

Python 套接字示例

让我们通过一个用 Python 编写的实际示例来了解套接字编程。在这里,我们的目标是连接两个应用程序并使它们相互通信。我们将使用 Python 套接字库创建一个服务器套接字应用程序,该应用程序将通过网络与客户端进行通信和交换信息。

注意事项和限制

但是请注意,出于教育目的,我们的示例进行了简化,应用程序将在本地运行,而不是通过实际网络进行通信 - 我们将使用环回本地主机地址将客户端连接到服务器。

这意味着客户端和服务器将在同一台计算机上运行,并且客户端将启动与运行它的同一台计算机的连接,尽管连接到表示服务器的不同进程。

在不同的计算机上运行

或者,您可以将应用程序放在两个不同的设备上,并将它们都连接到同一个Wi-Fi路由器,这将形成局域网。然后,在一台设备上运行的客户端可以连接到在另一台计算机上运行的服务器。

但是,在这种情况下,您需要知道路由器分配给设备的IP地址,并使用它们而不是本地主机(127.0.0.1)环回IP地址(要查看IP地址,请使用终端命令用于类Unix系统或-对于Windows)。获取应用程序的 IP 地址后,可以在代码中相应地更改它们,该示例仍然有效。ifconfigipconfig

无论如何,我们将从我们的例子开始。当然,如果你想跟上去,你需要安装Python

在 Python 中创建套接字服务器

让我们从创建一个套接字服务器(特别是Python TCP服务器,因为它将与TCP套接字一起工作,正如我们将看到的),它将与客户端交换消息。为了澄清术语,虽然从技术上讲,任何服务器都是套接字服务器,但由于套接字总是在后台使用来启动网络连接,因此我们使用短语“套接字服务器”,因为我们的示例明确使用了套接字编程。

因此,请按照以下步骤操作:

使用一些样板创建 python 文件
  • 创建一个名为server.py
  • 在 Python 脚本中导入模块。socket

  • 添加一个名为 的函数。我们将在那里添加大部分代码。将代码添加到函数时,不要忘记正确缩进它:run_server

实例化套接字对象

下一步,在 中使用该函数创建一个套接字对象。run_serversocket.socket()

第一个参数 () 指定 IPv4 的 IP 地址系列(其他选项包括:IPv6 系列和 Unix 套接字)socket.AF_INETAF_INET6AF_UNIX

第二个参数(表示我们正在使用TCP套接字。socket.SOCK_STREAM)

在使用TCP的情况下,操作系统将通过顺序数据传输,错误发现和重新传输以及流量控制创建可靠的连接。您不必考虑实现所有这些细节。

还有一个用于指定 UDP 套接字的选项:。这将创建一个套接字,该套接字在后台实现UDP的所有功能。socket.SOCK_DGRAM

如果你想更底层,并在套接字使用的TCP/IP网络层协议之上构建自己的传输层协议,你可以使用value作为第二个参数。在这种情况下,操作系统将不会为您处理任何更高级别的协议功能,如果需要,您必须自己实现所有标头、连接确认和重传功能。您还可以在文档中阅读其他值。socket.RAW_SOCKET

将服务器套接字绑定到 IP 地址和端口

定义主机名或服务器 IP 和端口,以指示服务器可从何处访问以及侦听传入连接的位置。在此示例中,服务器正在侦听本地计算机 - 这是由设置为 (也称为 localhost) 的变量定义的。server_ip127.0.0.1

该变量设置为 ,这是操作系统将标识服务器应用程序的端口号(建议对端口号使用大于 1023 的值,以避免与系统进程使用的端口发生冲突)。port8000

通过将套接字绑定到我们之前定义的 IP 地址和端口来准备套接字以接收连接。

侦听传入连接

使用该函数在服务器套接字中设置侦听状态,以便能够接收传入的客户端连接。listen

此函数接受调用的参数,该参数指定排队的未接受连接的最大数量。在此示例中,我们使用此参数的值。这意味着只有一个客户端可以与服务器交互。在服务器使用另一个客户端时执行的任何客户端的连接尝试都将被拒绝。backlog0

如果指定的值大于 ,例如,它会告诉操作系统在对客户端调用方法之前可以放入队列中的客户端数。01accept

调用一次,客户端将从队列中删除,不再计入此限制。一旦您看到代码的更多部分,这可能会变得更加清晰,但此参数本质上的作用可以说明如下:一旦您的侦听服务器收到连接请求,它将将此客户端添加到队列中并继续接受它的请求。如果在服务器能够在第一个客户端上进行内部调用之前,它收到来自第二个客户端的连接请求,则只要其中有足够的空间,它将把第二个客户端推送到同一队列。此队列的大小由积压工作参数控制。一旦服务器接受第一个客户端,该客户端就会从队列中删除,服务器开始与其通信。第二个客户端仍留在队列中,等待服务器获得空闲并接受连接。acceptaccept

如果省略 backlog 参数,它将设置为系统的默认值(在 Unix 下,您通常可以在文件中查看此默认值)。/proc/sys/net/core/somaxconn

接受传入连接

接下来,等待并接受传入的客户端连接。该方法将停止执行线程,直到客户端连接。然后它返回一个元组对,其中地址是客户端 IP 地址和端口的元组,并且是与客户端共享连接并可用于与其通信的新套接字对象。accept(conn, address)conn

accept创建一个新的套接字来与客户端通信,而不是将侦听套接字(在我们的示例中称为)绑定到客户端的地址并将其用于通信,因为侦听套接字需要侦听来自其他客户端的进一步连接,否则它将被阻止。当然,在我们的例子中,我们只处理一个客户端并在这样做时拒绝所有其他连接,但是一旦我们进入多线程服务器示例,这将更加相关。server

创建通信循环

一旦与客户端建立了连接(在调用方法之后),我们就会启动一个无限循环进行通信。在此循环中,我们对对象的方法执行调用。此方法从客户端接收指定数量的字节 - 在我们的例子中为 1024。acceptrecvclient_socket

1024 字节只是有效负载大小的常见约定,因为它是 <> 的幂,可能比其他任意值更适合优化目的。您可以随意更改此值。

由于从客户端接收到变量中的数据是原始二进制形式,因此我们使用该函数将其从字节序列转换为字符串。requestdecode

然后我们有一个 if 语句,如果我们收到消息,它会脱离通信循环。这意味着一旦我们的服务器在请求中收到字符串,它就会将确认发送回客户端并终止与客户端的连接。否则,我们将收到的消息打印到控制台。在我们的例子中,确认只是向客户端发送一个字符串。”close””close””closed”

请注意,我们在 if 语句中的字符串上使用的方法只是将其转换为小写。这样,我们就不在乎字符串最初是使用大写还是小写字符编写的。lowerrequestclose

将响应发送回客户端

现在我们应该处理服务器对客户端的正常响应(即当客户端不希望关闭连接时)。在 while 循环中,紧接着 ,添加以下行,这会将响应字符串(在我们的例子中)转换为字节并将其发送到客户端。这样,每当服务器收到来自客户端的消息时,它将发送字符串作为响应:print(f"Received: {request}")”accepted””close””accepted”

释放资源

一旦我们脱离了无限 while 循环,与客户端的通信就完成了,所以我们使用释放系统资源的方法关闭客户端套接字。我们还使用相同的方法关闭服务器套接字,这有效地关闭了我们的服务器。在现实世界中,我们当然可能希望我们的服务器继续侦听其他客户端,而不是在与单个客户端通信后关闭,但别担心,我们将在下面进一步介绍另一个示例。close

现在,在无限 while 循环之后添加以下行:

注意:不要忘记在文件末尾调用该函数。只需使用以下代码行:run_serverserver.py

完整的服务器套接字代码示例

以下是完整的源代码:server.py

import socket

def run_server():
# create a socket object
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

server_ip = "127.0.0.1"
port = 8000

# bind the socket to a specific address and port
server.bind((server_ip, port))
# listen for incoming connections
server.listen(0)
print(f"Listening on {server_ip}:{port}")

# accept incoming connections
client_socket, client_address = server.accept()
print(f"Accepted connection from {client_address[0]}:{client_address[1]}")

# receive data from the client
while True:
    request = client_socket.recv(1024)
    request = request.decode("utf-8") # convert bytes to string
    
    # if we receive "close" from the client, then we break
    # out of the loop and close the conneciton
    if request.lower() == "close":
        # send response to the client which acknowledges that the
        # connection should be closed and break out of the loop
        client_socket.send("closed".encode("utf-8"))
        break

    print(f"Received: {request}")

    response = "accepted".encode("utf-8") # convert string to bytes
    # convert and send accept response to the client
    client_socket.send(response)

# close connection socket with the client
client_socket.close()
print("Connection to client closed")
# close server socket
server.close()

run_server()

请注意,为了不使这个基本示例复杂化和复杂化,我们省略了错误处理。您当然希望添加 try-except 块,并确保始终关闭子句中的套接字。继续阅读,我们将看到一个更高级的示例。finally

在 Python 中创建客户端套接字

设置服务器后,下一步是设置一个客户端,该客户端将连接并向服务器发送请求。因此,让我们从以下步骤开始:

使用一些样板创建 python 文件
  • 创建一个名为client.py
  • 导入套接字库:

import socket

  • 定义我们将放置所有代码的函数:run_client

def run_client():
# your code will go here

实例化套接字对象

接下来,使用该函数创建一个 TCP 套接字对象,该对象充当客户端与服务器的联系点。socket.socket()

// create a socket object
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

连接到服务器套接字

指定服务器的 IP 地址和端口以便能够连接到它。这些地址和端口应与您之前设置的 IP 地址和端口匹配。server.py

server_ip = "127.0.0.1"  # replace with the server's IP address
server_port = 8000  # replace with the server's port number

使用客户端套接字对象上的方法与服务器建立连接。请注意,我们没有将客户端套接字绑定到任何 IP 地址或端口。这对客户端来说是正常的,因为将自动选择一个空闲端口并选取一个 IP 地址,该地址从系统的网络接口(在我们的例子中)提供到服务器的最佳路由,并将客户端套接字绑定到这些接口。connectconnect127.0.0.1

# establish connection with server
client.connect((server_ip, server_port))
创建通信循环

建立连接后,我们开始无限通信循环以向服务器发送多条消息。我们使用 Python 的内置函数从用户那里获取输入,然后将其编码为字节并修剪为最大 1024 字节。之后,我们使用.inputclient.send

while True:
# input message and send it to the server
msg = input("Enter message: ")
client.send(msg.encode("utf-8")[:1024])

处理服务器的响应

一旦服务器收到来自客户端的消息,它就会响应它。现在,在我们的客户端代码中,我们希望接收服务器的响应。为此,在通信循环中,我们使用该方法最多读取 1024 字节。然后我们使用将字节的响应转换为字符串,然后检查它是否等于值。如果是这种情况,我们将脱离循环,正如我们稍后看到的,这将终止客户端的连接。否则,我们将服务器的响应打印到控制台中。recvdecode”closed”

   # receive message from the server
    response = client.recv(1024)
    response = response.decode("utf-8")

    # if server sent us "closed" in the payload, we break out of the loop and close our socket
    if response.lower() == "closed":
        break

    print(f"Received: {response}")
释放资源

最后,在 while 循环之后,使用该方法关闭客户端套接字连接。这可确保正确释放资源并终止连接(即当我们收到消息并脱离 while 循环时)。close“closed”

// close client socket (connection to the server)
client.close()
print("Connection to server closed")

注意:同样,不要忘记在文件末尾调用我们上面实现的函数,如下所示:run_client

run_client()

完整的客户端套接字代码示例

以下是完整的代码:client.py

import socket

def run_client():
# create a socket object
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

server_ip = "127.0.0.1"  # replace with the server's IP address
server_port = 8000  # replace with the server's port number
# establish connection with server
client.connect((server_ip, server_port))

while True:
    # input message and send it to the server
    msg = input("Enter message: ")
    client.send(msg.encode("utf-8")[:1024])

    # receive message from the server
    response = client.recv(1024)
    response = response.decode("utf-8")

    # if server sent us "closed" in the payload, we break out of the loop and close our socket
    if response.lower() == "closed":
        break

    print(f"Received: {response}")

# close client socket (connection to the server)
client.close()
print("Connection to server closed")

run_client()

测试客户端和服务器

要测试我们上面编写的服务器和客户端实现,请执行以下操作:

  • 同时打开两个终端窗口。
  • 在一个终端窗口中,导航到文件所在的目录,然后运行以下命令以启动服务器:server.py

python server.py

这会将服务器套接字绑定到端口 127 上的本地主机地址 (0.0.1.8000),并开始侦听传入连接。

  • 在另一个终端中,导航到文件所在的目录并运行以下命令以启动客户端:client.py

python client.py

这将提示用户输入。然后,您可以键入消息并按 Enter 键。这会将您的输入传输到服务器并在其终端窗口中显示。服务器将向客户端发送响应,后者将再次要求您输入。这将一直持续到您将字符串发送到服务器。”close”

使用多个客户端 – 多线程

我们已经在前面的示例中看到了服务器如何响应来自单个客户端的请求,但是,在许多实际情况中,许多客户端可能需要同时连接到单个服务器。这就是多线程的用武之地。多线程用于需要同时(同时)处理多个任务(例如执行多个功能)的情况。

这个想法是生成一个线程,该线程是一组独立的指令,可以由处理器处理。线程比进程轻量级得多,因为它们实际上存在于进程本身中,您不必为自己分配大量资源。

python中多线程的局限性

请注意,Python 中的多线程是有限的。标准的Python实现(CPython)不能真正并行运行线程。由于全局解释器锁 (GIL),一次只允许执行一个线程。但是,这是一个单独的主题,我们不打算讨论。为了我们的示例,使用有限的CPython线程就足够了,并且可以理解这一点。但是,在实际场景中,如果您要使用Python,则应研究异步编程。我们现在不打算讨论它,因为它又是一个单独的主题,它通常会抽象出一些我们在本文中特别关注的低级套接字操作。

多线程服务器示例

让我们看一下下面的示例,了解如何将多线程添加到服务器中以处理大量客户端。请注意,这次我们还将使用 try-except-finally 块添加一些基本的错误处理。要开始使用,请按照以下步骤操作:

创建线程生成服务器函数

在您的 python 文件中,导入 and 模块以便能够同时使用套接字和线程:

定义函数,如上例所示,创建一个服务器套接字,绑定它并侦听传入的连接。然后调用无限 while 循环。这将始终侦听新的连接。获取传入连接并返回后,使用构造函数创建一个线程。该线程将执行我们稍后将要定义的函数,并作为参数传递给它(元组包含所连接客户端的 IP 地址和端口)。创建线程后,我们调用它开始执行。run_serveracceptacceptthreading.Threadhandle_clientclient_socketaddraddrstart

请记住,调用是阻塞的,因此在 while 循环的第一次迭代中,当我们到达 的行时,我们停止并等待客户端连接而不执行任何其他操作。一旦客户端连接,方法就会返回,我们继续执行:生成一个线程,该线程将处理所述客户端并转到下一次迭代,我们将再次在调用处停止等待另一个客户端连接。acceptacceptacceptaccept

在函数结束时,我们有一些错误处理,以确保服务器套接字始终关闭,以防发生意外情况。

def run_server():
server_ip = "127.0.0.1" # server hostname or IP address
port = 8000 # server port number
# create a socket object
try:
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# bind the socket to the host and port
server.bind((server_ip, port))
# listen for incoming connections
server.listen()
print(f"Listening on {server_ip}:{port}")

    while True:
        # accept a client connection
        client_socket, addr = server.accept()
        print(f"Accepted connection from {addr[0]}:{addr[1]}")
        # start a new thread to handle the client
        thread = threading.Thread(target=handle_client, args=(client_socket, addr,))
        thread.start()
except Exception as e:
    print(f"Error: {e}")
finally:
    server.close()

请注意,我们示例中的服务器仅在发生意外错误时才会停止。否则,它将无限期地侦听客户端,如果要停止它,则必须杀死终端。

创建客户端处理函数以在单独的线程中运行

现在,在函数上方,定义另一个名为 .此函数将是在每个客户端连接的单独线程中执行的函数。它接收客户端的套接字对象和元组作为参数。run_serverhandle_clientaddr

在这个函数中,我们执行与单线程示例中相同的操作以及一些错误处理:我们启动一个循环以使用 .recv

然后我们检查我们是否收到关闭的消息。如果是这样,我们使用字符串响应并通过中断循环来关闭连接。否则,我们将客户端的请求字符串打印到控制台中,然后继续进行下一个循环迭代以接收下一个客户端的消息。”closed”

在这个函数的末尾,我们有一些针对意外情况的错误处理(子句),还有一个使用.无论如何,此子句将始终执行,这可确保始终正确释放客户端套接字。

def handle_client(client_socket, addr):
try:
while True:
# receive and print client messages
request = client_socket.recv(1024).decode("utf-8")
if request.lower() == "close":
client_socket.send("closed".encode("utf-8"))
break
print(f"Received: {request}")
# convert and send accept response to the client
response = "accepted"
client_socket.send(response.encode("utf-8"))
except Exception as e:
print(f"Error when hanlding client: {e}")
finally:
client_socket.close()
print(f"Connection to client ({addr[0]}:{addr[1]}) closed")

当返回时,执行它的线程也将自动释放。handle_client

注意:不要忘记在文件末尾调用该函数。run_server

完整的多线程服务器代码示例

现在,让我们把完整的多线程服务器代码放在一起:

import socket
import threading

def handle_client(client_socket, addr):
try:
while True:
# receive and print client messages
request = client_socket.recv(1024).decode("utf-8")
if request.lower() == "close":
client_socket.send("closed".encode("utf-8"))
break
print(f"Received: {request}")
# convert and send accept response to the client
response = "accepted"
client_socket.send(response.encode("utf-8"))
except Exception as e:
print(f"Error when hanlding client: {e}")
finally:
client_socket.close()
print(f"Connection to client ({addr[0]}:{addr[1]}) closed")

def run_server():
server_ip = "127.0.0.1" # server hostname or IP address
port = 8000 # server port number
# create a socket object
try:
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# bind the socket to the host and port
server.bind((server_ip, port))
# listen for incoming connections
server.listen()
print(f"Listening on {server_ip}:{port}")

    while True:
        # accept a client connection
        client_socket, addr = server.accept()
        print(f"Accepted connection from {addr[0]}:{addr[1]}")
        # start a new thread to handle the client
        thread = threading.Thread(target=handle_client, args=(client_socket, addr,))
        thread.start()
except Exception as e:
    print(f"Error: {e}")
finally:
    server.close()

run_server()

注意:在实际代码中,为了防止在处理多线程服务器时出现争用情况或数据不一致等可能的问题,必须考虑线程安全和同步技术。然而,在我们的简单示例中,这不是问题。

具有基本错误处理的客户端示例

现在我们有一个能够同时处理多个客户端的服务器实现,我们可以使用上面第一个基本示例中所示的相同客户端实现来启动连接,或者我们可以稍微更新它并添加一些错误处理。您可以在下面找到代码,该代码与前面的客户端示例相同,但添加了 try-except 块:

import socket

def run_client():
# create a socket object
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

server_ip = "127.0.0.1"  # replace with the server's IP address
server_port = 8000  # replace with the server's port number
# establish connection with server
client.connect((server_ip, server_port))

try:
    while True:
        # get input message from user and send it to the server
        msg = input("Enter message: ")
        client.send(msg.encode("utf-8")[:1024])

        # receive message from the server
        response = client.recv(1024)
        response = response.decode("utf-8")

        # if server sent us "closed" in the payload, we break out of
        # the loop and close our socket
        if response.lower() == "closed":
            break

        print(f"Received: {response}")
except Exception as e:
    print(f"Error: {e}")
finally:
    # close client socket (connection to the server)
    client.close()
    print("Connection to server closed")

run_client()

测试多线程示例

如果要测试多客户端实现,请为客户端打开多个终端窗口,为服务器打开一个终端窗口。首先使用 启动服务器。之后,使用 .在服务器终端窗口中,您将看到新客户端如何连接到服务器。现在,您可以通过在相应的终端中输入文本来继续从不同的客户端发送消息,所有这些消息都将被处理并打印到服务器端的控制台。python server.pypython client.py

套接字编程在数据科学中的应用

虽然每个网络应用程序都使用操作系统在后台创建的套接字,但有许多系统严重依赖套接字编程,无论是用于某些特殊用例还是为了提高性能。但是套接字编程在数据科学的背景下究竟有什么用呢?嗯,每当需要快速接收或发送大量数据时,它肯定起着有意义的作用。因此,套接字编程主要用于数据收集和实时处理、分布式计算和进程间通信。但是,让我们仔细看看数据科学领域的一些特定应用。

实时数据收集

套接字广泛用于从不同来源收集实时数据以进行进一步处理、转发到数据库或分析管道等。例如,套接字可用于从金融系统或社交媒体API即时接收数据,以供数据科学家进行后续处理。

分布式计算

数据科学家可以使用套接字连接在多台机器上分配大型数据集的处理和计算。套接字编程通常用于Apache Spark和其他分布式计算框架中,用于节点之间的通信。

模型部署

在向用户提供机器学习模型时,可以使用套接字编程,从而即时提供预测和建议。为了促进实时决策,数据科学家可以使用基于套接字的高性能服务器应用程序,这些应用程序接收大量数据,使用经过训练的模型对其进行处理以提供预测,然后将结果快速返回给客户端。

进程间通信 (IPC)

套接字可用于IPC,它允许在同一台机器上运行的不同进程相互通信并交换数据。这在数据科学中很有用,可以在多个进程中分配复杂和资源密集型计算。事实上,Python 的子处理库经常用于此目的:它生成多个进程以利用多个处理器内核,并在执行繁重计算时提高应用程序性能。此类进程之间的通信可以通过IPC套接字实现。

协作与沟通

套接字编程允许数据科学家之间的实时通信和协作。为了促进有效的协作和知识共享,使用了基于套接字的聊天应用程序或协作数据分析平台。

值得一提的是,在上述许多应用程序中,数据科学家可能不会直接参与套接字的使用。他们通常使用库、框架和系统来抽象出套接字编程的所有低级细节。但是,在后台,所有这些解决方案都基于套接字通信并利用套接字编程。

套接字编程挑战和最佳实践

由于套接字是管理连接的低级概念,因此使用套接字的开发人员必须实现所有必需的基础结构,以创建健壮可靠的应用程序。这当然带来了很多挑战。但是,可以遵循一些最佳实践和一般准则来克服这些问题。以下是套接字编程最常遇到的一些问题,以及一些一般提示:

连接管理

一次处理多个连接;管理多个客户端并确保有效处理并发请求肯定具有挑战性且并非易事。它需要仔细的资源管理和协调,以避免瓶颈

最佳实践
  • 使用列表或字典等数据结构跟踪活动连接。或者使用连接池等高级技术,这也有助于提高可伸缩性。
  • 使用线程或异步编程技术同时处理多个客户端连接。
  • 正确关闭连接以释放资源并避免内存泄漏。

错误处理

处理连接失败、超时和数据传输问题等错误至关重要。处理这些错误并向客户端提供适当的反馈可能具有挑战性,尤其是在进行低级套接字编程时。

最佳实践
  • 使用 try-except-finally 块来捕获和处理特定类型的错误。
  • 提供信息丰富的错误消息,并考虑使用日志记录来帮助进行故障排除。

可扩展性和性能

确保最佳性能和最小化延迟是处理大容量数据流或实时应用程序时的关键问题。

最佳实践
  • 通过最大限度地减少不必要的数据处理和网络开销来优化代码以提高性能。
  • 实施缓冲技术以高效处理大型数据传输。
  • 考虑使用负载平衡技术在多个服务器实例之间分发客户端请求。

安全性和身份验证

保护基于套接字的通信并实施适当的身份验证机制可能很困难。确保数据隐私、防止未经授权的访问和防止恶意活动需要仔细考虑和实施安全协议。

最佳实践
  • 利用 SSL/TLS 安全协议,通过加密信息来确保数据传输的安全。
  • 通过实施安全的身份验证方法(如基于令牌的身份验证、公钥加密或用户名/密码)来确保客户端身份。
  • 确保机密数据(如密码或 API 密钥)得到保护和加密,或者理想情况下根本不存储(如果需要,仅存储其哈希值)。

网络可靠性和弹性

处理网络中断、带宽波动和不可靠的连接可能会带来挑战。保持稳定的连接、优雅地处理断开连接以及实施重新连接机制对于强大的网络应用程序至关重要。

最佳实践
  • 使用保持活动状态消息检测非活动或断开的连接。
  • 实施超时以避免无限期阻塞并确保及时响应处理。
  • 实现指数退避重新连接逻辑,以便在连接丢失时再次建立连接。

代码可维护性

最后但并非最不重要的一点是代码可维护性。由于套接字编程的低级性质,开发人员发现自己编写了更多的代码。这可能会很快变成一个无法维护的意大利面条代码,因此尽早组织和构建它并花费额外的精力来规划代码的体系结构至关重要。

最佳实践
  • 将代码分解为类或函数,理想情况下不应太长。
  • 通过模拟客户端和服务器实现来尽早编写单元测试
  • 请考虑使用更高级的库来处理连接,除非绝对必须使用套接字编程。

总结:Python 中的套接字编程

套接字是所有网络应用程序的组成部分。在本文中,我们研究了 Python 中的套接字编程。以下是要记住的要点:

  • 套接字是抽象连接管理的接口。
  • 套接字支持不同进程(通常是客户端和服务器)之间的通信,或者通过网络进行通信。
  • 在 Python 中,使用套接字是通过库完成的,该库为套接字对象提供了各种方法,如、、、。socketrecvsendlistenclose
  • 套接字编程在数据科学中具有各种有用的应用,包括数据收集、进程间通信和分布式计算。
  • 套接字编程面临的挑战包括连接管理、数据完整性、可伸缩性、错误处理、安全性和代码可维护性。

借助套接字编程技能,开发人员可以创建高效、实时的网络应用程序。通过掌握概念和最佳实践,他们可以充分利用套接字编程的潜力来开发可靠且可扩展的解决方案。

但是,套接字编程是一种非常低级的技术,很难使用,因为应用程序工程师必须考虑应用程序通信的每一个小细节。

如今,我们通常不需要直接使用套接字,因为它们通常由更高级别的库和框架处理,除非需要真正从应用程序中挤出性能或扩展它。

但是,了解套接字并深入了解底层工作原理可以提高开发人员或数据科学家的整体意识,并且始终是一个好主意。

原文链接:Python 套接字编程完整指南 (mvrlink.com)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
内容简介 本书是经典的Python指导书,在第一版的基础上进行了全面升级。全书分为两个部分:第1部分占据了大约三分之二的篇幅,阐释这门语言的“核心”内容,包括基本的概念和语句、语法和风格、 Python对象、数字类型、序列类型、映射和集合类型、条件和循环、文件和输入/输出、错误和异常、函数和函数式编程、模块、面向对象编程、执行环境等内容:第2部分则提供了各种高级主题来展示可以使用Python做些什么,包括正则表达式、网络编程、网络客户端编程、多线程编程、图形用户界面编程、 Web编程、数据库编程、扩展Python 和一些其他材料。  本书适合Python初学者,以及已经入门但想继续学习和提高自身Python技巧的程序员。 目录 第1部分 Python核心  第1章 欢迎来到Python世界    1.1 什么是Python    1.2 起源    1.3 特点    1.4 下载和安装Python    1.5 运行Python    1.6 Python文档    1.7 比较PythonPython与其他语言的比较)    1.8 其他实现    1.9 练习   第2章 快速入门    2.1 程序输出,print语句及“Hello World!”    2.2 程序输入和raw_input()内建函数    2.3 注释    2.4 操作符    2.5 变量和赋值    2.6 数字    2.7 字符串    2.8 列表和元组    2.9 字典    2.10 代码块及缩进对齐    2.11 if语句    2.12 while循环    2.13 for循环和range()内建函数    2.14 列表解析    2.15 文件和内建函数open()、file()    2.16 错误和异常    2.17 函数    2.18 类    2.19 模块    2.20 实用的函数    2.21 练习   第3章 Python基础    3.1 语句和语法    3.2 变量赋值    3.3 标识符    3.4 基本风格指南    3.5 内存管理    3.6 第一个Python程序    3.7 相关模块和开发工具    3.8 练习   第4章 Python对象    4.1 Python 对象    4.2 标准类型    4.3 其他内建类型    4.4 内部类型    4.5 标准类型操作符    4.6 标准类型内建函数    4.7 类型工厂函数    4.8 标准类型的分类    4.9 不支持的类型    4.10 练习   第5章 数字    5.1 数字简介    5.2 整型    5.3 双精度浮点型    5.4 复数    5.5 操作符    5.6 内建函数与工厂函数    5.7 其他数字类型    5.8 相关模块    5.9 练习   第6章 序列:字符串、列表和元组    6.1 序列    6.2 字符串    6.3 字符串和操作符    6.4 只适用于字符串的操作符    6.5 内建函数    6.6 字符串内建函数    6.7 字符串的独特特性    6.8 Unicode    6.9 相关模块    6.10 字符串关键点总结   6.11 列表    6.12 操作符    6.13 内建函数    6.14 列表类型的内建函数    6.15 列表的特殊特性    6.16 元组    6.17 元组操作符和内建函数    6.18 元组的特殊特性    6.19 相关模块    6.20 *拷贝Python对象、浅拷贝和深拷贝    6.21 序列类型小结    6.22 练习   第7章 映像和集合类型    7.1 映射类型:字典    7.2 映射类型操作符    7.3 映射类型的内建函数和工厂函数    7.4 映射类型内建方法    7.5 字典的键    7.6 集合类型    7.7 集合类型操作符    7.8 内建函数    7.9 集合类型内建方法    7.10 集合类型总结表    7.11 相关模块    7.12 练习   第8章 条件和循环    8.1 if语句    8.2 else语句    8.3 elif(即else-if)语句    8.4 条件表达式(即“三元操作符”)    8.5 while语句    8.6 for语句    8.7 break语句    8.8 continue语句    8.9 pass语句    8.10 再谈else语句    8.11 迭代器和iter()函数    8.12 列表解析    8.13 生成器表达式    8.14 相关模块    8.15 练习   第9章 文件和输入输出    9.1 文件对象    9.2 文件内建函数(open()和file())    9.3 文件内建方法    9.4 文件内建属性    9.5 标准文件    9.6 命令行参数    9.7 文件系统    9.8 文件执行    9.9 永久存储模块    9.10 相关模块    9.11 练习   第10章 错误和异常    10.1 什么是异常    10.2 Python 中的异常    10.3 检测和处理异常    10.4 上下文管理    10.5 *字符串作为异常    10.6 触发异常    10.7 断言    10.8 标准异常    10.9 *创建异常    10.10 (现在)为什么用异常    10.11 到底为什么要异常    10.12 异常和sys模块    10.13 相关模块    10.14 练习   第11章 函数和函数式编程    11.1 什么是函数?    11.2 调用函数    11.3 创建函数    11.4 传递函数    11.5 Formal Arguments    11.6 可变长度的参数    11.7 函数式编程    11.8 变量作用域    11.9 *递归     11.10 生成器    11.11 练习   第12章 模块    12.1 什么是模块    12.2 模块和文件    12.3 名称空间    12.4 导入模块    12.5 模块导入的特性    12.6 模块内建函数    12.7 包    12.8 模块的其他特性    12.9 相关模块    12.10 练习   第13章 面向对象编程    13.1 引言    13.2 面向对象编程    13.3 类    13.4 类属性    13.5 实例    13.6 实例属性    13.7 绑定和方法调用    13.8 静态方法和类方法    13.9 组合    13.10 子类和派生    13.11 继承    13.12 类、实例和其他对象的内建函数    13.13 用特殊方法定制类    13.14 私有化    13.15 *授权    13.16 新式类的高级特性(Python 2.2+)    13.17 相关模块和文档    13.18 练习   第14章 执行环境    14.1 可调用对象    14.2 代码对象    14.3 可执行的对象声明和内建函数    14.4 执行其他(Python程序    14.5 执行其他(非Python程序    14.6 受限执行    14.7 结束执行    14.8 各种操作系统接口    14.9 相关模块    14.10 练习  第2部分 高级主题  第15章 正则表达式    15.1 引言/动机    15.2 正则表达式使用的特殊符号和字符    15.3 正则表达式和Python语言    15.4 正则表达式示例    15.5 练习   第16章 网络编程    16.1 引言    16.2 套接字:通信端点    16.3 Python中的网络编程    16.4 *SocketServer模块    16.5 Twisted框架介绍    16.6 相关模块    16.7 练习   第17章 网络客户端编程    17.1 什么是因特网客户端    17.2 文件传输    17.3 网络新闻    17.4 电子邮件    17.5 相关模块    17.6 练习   第18章 多线程编程    18.1 引言/动机    18.2 线程和进程    18.3 Python、线程和全局解释器锁    18.4 thread模块    18.5 threading模块    18.6 相关模块    18.7 练习   第19章 图形用户界面编程    19.1 简介    19.2 Tkinter与Python编程 534   19.3 Tkinter举例    19.4 其他GUI简介    19.5 相关模块和其他GUI    19.6 练习   第20章 Web编程    20.1 介绍    20.2 使用Python进行Web应用:创建一个简单的Web客户端    20.3 高级Web客户端    20.4 CGI:帮助Web服务器处理客户端数据    20.5 建立CGI应用程序    20.6 在CGI中使用Unicode编码    20.7 高级CGI    20.8 Web(HTTP)服务器    20.9 相关模块    20.10 练习   第21章 数据库编程    21.1 介绍    21.2 Python数据库应用程序程序员接口(DB-API)    21.3 对象-关系管理器(ORM)    21.4 相关模块    21.5 练习   第22章 扩展Python 623   22.1 引言/动机    22.2 创建Python扩展    22.3 相关话题    22.4 练习  第23章 其他话题    23.1 Web服务    23.2 用Win32的COM来操作微软Office    23.3 用Jython写Python和Java的程序    23.4 练习    23.3 用Jython写Python和Java的程序    23.4 练习
Tcl_TK编程权威指南pdf 内容简介回到顶部↑Tcl/Tk是第一种能通过Windows、Macintosh和Solaris等主要平台处理企业级任务的脚本语言。本书共分为55章,依次详细讲述了Tcl基础、Tcl高级特性、TK基础、TK组件、TK详解、C语言编程、各版本之间的差异等方面的知识,并通过大量实例,生动翔实地向读者介绍了Tcl/Tk编程,是读者掌握Tcl/Tt的必备参考书。 本书适合各个层次的读者阅读。 目录回到顶部↑第1部分 tcl基础 第1章 tcl的基本知识 tcl命令 hello,world! 变量 命令替换 数学表达式 反斜杠替换 使用花括号和双引号进行分组 过程 一个阶乘的例子 更多有关变量的知识 更多有关数学表达式的内容 注释 有关替换与分组的总结 要点 参考 第2章 开始使用 source命令 unix上的tcl脚本程序 .windows 95的开始菜单 macintosh与resedit console命令 命令行变元 预定义变量 第3章 cgi应用程序--顾客留言簿 html简介 使用cgi创建动态页面 guestbook.cgi脚本程序 定义表单以及处理表单数据 cgi.tcl软件包 接下去的几步 第4章 tcl中的字符串处理 string命令 append命令 format命令 scan命令 binary命令 相关章节 第5章 tcl列表 tcl列表 构建列表 获取列表元素 修改列表 搜索列表 对列表进行排序 split命令 join命令 相关章节 第6章 控制结构命令 if then else switch while foreach for break与continue catch error return 第7章 过程与作用域 proc命令 使用rename来改变命令名 作用域 global命令 通过upvar以名字进行调用 使用upvar来处理变量别名 第8章 tcl数组 数组的语法 array命令 使用数组来构建数据结构 第9章 对文件和程序的操作 使用exec运行程序 file命令 跨平台的文件命名方式 操作文件和目录 文件属性 对i/o命令的总结 打开文件用于i/o操作 读写操作 当前目录-cd和pwd 使用glob来匹配文件名 exit和pid命令 环境变量 registry命令 第2部分tcl高级特性 第10章 引用问题与eval 使用list命令来构建代码 在eval内部利用concat uplevel命令 subst命令 第11章 正则表达式 何时使用正则表达式 正则表达式的语法 高级正则表达式(are) 语法总结 regexp命令 rgsub命令 使用regsub将数据转换为程序 其他使用正则表达式的命令 第12章 脚本库及软件包 确定软件包的位置:auto-path变量 使用软件包 对软件包加载的总结 package命令 基于文件tclindex的库 unknown命令 方便交互 tclshell的库环境 编码风格 第13章 反射与调试 clock命令 info命令 跨平台支持 跟踪变量的值 交互式命令历史记录 调试 scriptics的tclpro 其他工具 性能调校 第14章 名字空间 使用名字空间 名字空间变量 命令查找 嵌套名字空间 过程的进口与输出 回调与名字空间 内省(introspection) namespace命令 转换现有的软件包以使用名字空间 [incrtcl]对象系统 注意事项 第15章 国际化(internationalization) 字符集与编码 消息目录 第16章 事件驱动的编程 tcl事件循环 after命令 fileevent命令 vwait命令 fconfigure命令 第17章 套接字编程 客户端套接字 服务器端套接字 回送(echo)服务 使用http获取一个url http软件包 基本认证 第18章 tclhttpd web服务器 将 tclhttpd与你的应用程序集成 域处理程序 应用执导的url 文档类型 html+tcl模板 表单处理程序 编程参考 标准应用执导(application-dirct)的url tclhttpd发行版 服务器配置 第19章 多解释器与 safe-tcl interp命令 创建解释器 安全解释器 命令别名 隐藏命令 替换 从安全解释器中执行i/o操作 安全基础 安全策略 第20章 safe-tk与浏览器插件 子解释器中的tk 浏览器插件 安全策略与浏览器插件 配置安全策略 第3部分 tk基础 第21章 tk的基本知识 th中的hello,world! tk组件的命名 配置tk组件 tk组件属性与资源数据库 tk命令概要 第22章 tk实例解析 execlog example browser tcl shell 第23章 打包摆放布局管理器(pack) 朝一侧摆放 水平与垂直难叠 空腔模型( cavity model) 打包摆放空间(packing space)与显w空间(display space) 尺寸调整与一expand 挂靠 摆放顺序 选择用于摆放的父组件 取消一个组件的摆放 打包器总结 窗口的堆叠顺序 第24章 栅格摆放布局管理器( grid) 一种基本栅格 跨行列摆放 行列约束 grid命令 第25 章定位摆放布局管理器( place) place的基础知识 面板管理器 place命令 第26章 将命令与事件编联 bind命令 bindtags命令 事件的语法 修饰符 事件序列 虚拟事件 事件关键词 第4部分 tk组件 第27章 按钮与菜单 按钮命令与作用域问题 与tcl变量关联的按钮 按钮属性 按钮操作 菜单和菜单按钮 键盘遍历 操纵菜单和菜单条目 菜单属性 通过名字来指定菜单的软件包 第28章 资源数据库 有关资源的介绍 加载选项数据库 添加单一的数据库条目 存取数据库 用户定义的按钮 用户定义的菜单 第29章 简单的tk组件 框架组件与顶层窗口 标签组件 消息组件 标尺组件 bell命令 第30章 滚动条 使用滚动条 滚动条协议 滚动条组件 第31章 输入条组件 使用输入条组件 输入条组件 第32章 列表框组件 使用列表框组件 列表框组件的编联 列表框组件的属性 第33章 文本组件 文本索引 文本标记 文本标签 文本信息的选择( selection) 标签的编联 文本搜索 嵌入组件 图片的嵌入 查看文本组件的内部信息 文本组件的编联 文本组件的操作 文本组件的属性 第34章 画布组件 画布坐标 hello, world! 最小和最大标尺的例子 画布对象 画布组件的操作 产生postscript输出 画布组件的属性 建议 第5部分 tk详解 第35章 选择和剪贴板 选择模型 selection命令 clipboard命令 选择处理程序 第36章 焦点、焦点的捕获和对话框 标准对话框 定制对话框 使用update命令实现动画 第37章 tk组件的属性 配置属性 尺寸 边界与浮雕效果 焦点的高亮显示 补自(padding)与挂靠(anchor) 第38章 颜色、图片和鼠标指针 颜色 色彩映射与视频种类 位图和图片 文本插入光标 鼠标指针 第39章 字体与文本属性 字体命名 x字体名 字模 font命令 文本属性 栅格化、尺寸调整和布局 一个字体选择应用程序 第40章 send send命令 发送者脚本 通信进程 通过套接字来实现远程eval 第41章 窗口管理器与窗口信息 win命令 winfo命令 tk命令 第42章 管理用户首选项 应用默认设置文件 定义首选项 首选项的用户界面 管理首选项文件 跟踪对首选项变量的修改 对该软件包的改进 第43章 一种操作编联的用户界面 一对协调工作的列表框 编辑界面 保存与加载编联 第6部分 c语言编程 第44章 c语言编程与tcl 基本概念 创建可加载软件包 一个用c语言实现的命令过程 blob命令的例于 字符串与国际化 tolmain和tcl-applnit tk_main 事件循环 从c中调用脚本 第45章 编译tci及扩展模块 标准目录结构 从源代码建立tci 使用占位函数库(stub library) 使用autoconf 扩展模块范例 makefile.in 第46章 使用c语言编写tk组件 初始化扩展模块 组件的数据结构 组件的类命令 组件实例命令 配置和重新配置属性 指定组件属性 时钟的显示 窗口事件过程 最后的清除工作 第47章 c函数库概览 tclc函数库概览 tk c函数库概览 第7部分 各版本之间的差异 第48章 tcl 7.4/tk 4.0 wish 过时废弃的功能 cgct操作 输入焦点的高亮显示 编联 滚动条接日 pack info 焦点 send命令 按钮的内部补白 单选按钮的值 输入条组件 菜单 列表框 没有了geometry属性 文本组件 颜色属性 颜色分配与tk colormodel 画布组件的scrollincrement 选择 bell命令 第49章 tcl 7.5/tk 4.1 跨平台脚本 clock命令 load命令 package命令 多个foreach循环变量 事件循环从tk转移到了tcl 网络套接字 多解释器与safe-tcl grid布局管理器 文本组件 输入条组件 第50章 tcl7.6/tk 4.2 更多的file操作 虚拟事件 标准对话框 新的grid布局管理器 macintosh的unsupportedl命令 第51章 tcl/tk 8.0 tcl编译器 名字空间 safe-tcl 新的lsort tcl_precision变量 2000年约定 http软件包 串行线i/o 独立于平台的字体 tk scaling命令 应用程序的嵌入 本地化菜单与菜单条 cde的边界宽度 本地化的按钮和滚动条 文本组件中的图片 destroy不再产生错误 grid rowconfigure 补丁版本 第52章 tcl/tk 8.1 unicode与国际化 线程安全 高级正则表达式 新字符串命令 dde扩展模块 杂类 第53章 tcl/tk 8.2 trf补丁 更快的字符串操作 空数组名 浏览器插件的兼容性 第54章 tcl/tk 8.3 关于tcl的修改建议 关于tk的改动建议 第55章 有关本书的cd-rom ↓展开全部内容 序言回到顶部↑Tcl为工具命令语言(Tool Command Language)的缩写。它其实是指两样东西:一种脚本语言,以及该脚本语言的解释器。该解释器可以很容易地嵌入到你的应用程序中。Tcl和与之关联的图形用户界面工具包(Tk)是由加州大学的John Ousterhout教授设计并编写的。尽管它是个商用软件包,但你也可以在Internet上找到它(见第VII页),而且可以在自己的应用程序中自由使用这个软件包。Tcl解释器已经从Unix平台移植到了DOS、Windows、OS/2、NT以及Macintosh环境中,而TK工具包也从X window系统移植到了Windows和Macintosh环境中。 1988年,当我在Berkeley做ousterhout教授的博士生时,第一次听说了Tcl。我们当时正在设计一种名为Sprite的网络操作系统。同学们在努力编制一个新式的内核程序,而John编写了一个新的编辑器和终端仿真程序。他使用Tcl作为这两种工具的命令语言,这样用户就可以定义菜单或者对那些程序进行定制。那时还处在使用X10的时代,他计划编写一个基于Tcl的X工具包,以使程序之间通过Tcl命令进行通信,彼此相互协作。对我来说,这种工具之间的相互协作就是Tcl的实质。 这种早期的设想就是让应用程序由包含编译代码的大块实体和一小部分用于进行配置和编写高级命令的Tcl代码组成。John的编辑器皿,还有终端仿真程序tx就遵循了这种模式。虽然这种模式仍然是有效的,但结果表明用Tcl来编写整个应用程序也是可能的。这是因为Tcl/Tk的shell程序wish提供了对其他程序、文件系统和网络套接字的存取功能,同时还能够创建图形用户界面。不管怎样,现在发现包含几千行Tcl脚本的应用程序并不稀奇。 我编写这本书的原因就是,虽然自己觉得使用Tcl与Tk既有乐趣又高效,但是也有令人头痛的时候。此外,在Xerox PARC工作,那里有许多语言和系统上的专家,我不得不强迫自己去理解Tcl/Tk的长处和弱点。我的许多同事都在他们的项目中采用了Tcl和Tk,但是他们也很快指出了它的缺点。因此,我就总结了一套编程技巧以充分利用Tcl/Tk的强大功能,同时回避一些棘手的问题。这本书就是一本帮助你最大限度地利用Tcl/Tk并回避一些我所经历过的令人头痛的问题的实用编程指南。 我接触Tcl语言大概已经有10年的时间了,而本书的第一版也已经出版5年了。在过去的几年中,我一直在John Ousterhout的手下工作,最初是在Sun微系统公司,而现在是在Scriptics公司。我一直使自己在很大程度上保持着一个Tcl程序员的角色,而我们工作组中的其他人员则埋头于Tcl本身的C语言实现。我创建的应用程序有HTML编辑器、EMAIL比用户接口程序、Web服务器以及用户数据库,我们的商务应用就建立在它们的基础上。这些经历在本书中有所反映。本书的大部分内容是有关Tcl脚本编程的,而有关使用C语言来创建Tcl扩展模块的内容没有着重讲述。我有幸一直参与Tcl核心技术的开发活动,希望通过本书能够将自己使用Tcl时获得的切身体会表达出来。 为什么要使用Tcl 作为一种脚本语言,Tcl与其他的Unix shell语言,如Bourne Shell(sh)、C Shell(csh)、Korn Shell以及Perl类似。Shell程序可以让你执行其他的程序。它们提供了足够的可编程特性(变量、流程控制和过程),使你可以将现有程序组装成符合自己需要的复杂的脚本程序。Shell程序非常适用于一些日常任务的自动化处理工作。 Tcl解释器可以很容易地添加到你的应用程序中,这种能力将它与其他的shell语言区分开来。Tcl扮演了一种扩展语言的角色,用来配置和定制应用程序。你没有必要再去为自己的新应用程序发明一种命令语言,或是费力为自己的工具提供某种用户可编程特性。其实,你可以通过添加一个Tcl解释器,来将自己的应用程序组织成一组操作原语,并使用这些原语来构造最符合用户需求的脚本程序。这样还可以允许其他的程序通过编程来控制你的应用程序,以使套装应用程序能够很好地在一起工作。 Tcl的C函数库拥有清晰的接口而且便于使用。该函数库实现了基本的解释器,它有一套实现变量、流程控制和过程的核心脚本命令,而且还有一组用来存取操作系统服务以运行其他程序、存取文件系统和使用网络套接字的命令。Tcl和Tk提供了一台可以在UNIX、Windows和Macintosh环境中可移植的"虚拟机"。 因为你的应用程序可以定义新的Tcl命令,所以Tcl虚拟机是可扩展的。这些命令与你的应用程序所提供的C或C++过程关联。结果应用程序就分割成一组用编译语言编写的原语,并输出成为相应的Tcl命令。使用Tcl脚本程序可以将这些原语组装成完整的应用程序。脚本语言层可以存取与shell类似的功能以运行其他的程序,可以存取文件系统,还可以直接通过自己定义的Tcl命令来调用应用程序中编译的代码部分。此外,从C编程的层面上来说,你还可以调用Tcl脚本程序、设置和询问Tcl变量,甚至跟踪Tcl解释器的执行。 在Internet上有许多可自由使用的Tcl扩展模块。许多扩展模块都包含了一个提供某种新功能的C函数库,以及该函数库的Tcl接口。这样的例子包括数据库存取、电话控制、MIDI控制器存取,还有expect,它为控制交互式程序增加了一组Tcl命令。 最为著名的扩展模块就是Tk,这是一种图形用户界面工具包。Tk定义了用来创建和操作用户界面组件的Tcl命令。这种基于脚本的用户界面编程方法有三个好处: . 由于快速的响应周期,所以开发迅速,不存在漫长的编译等待过程。 . Tcl命令提供了一种比绝大多数由标准C函数库实现的用户界面工具包更为高级的接口。它只需一小组命令就可以定义简单的用户界面,同时又可以对用户界面进行细化以恰当地实现每一个细节。快速的响应周期又为这种细化过程提供了帮助。 用户界面处理可以从你的应用程序的其余部分分离出来。因而开发人员能够专心致志地实现应用程序的核心部分,然后再颇为轻松地构建出用户界面。Tk组件的核心功能通常能够满足你所有的用户界面需求。不过,你还可以用C语言来编写定制的Tk组件,而且网上还有许多大家提供的Tk组件可以使用。 还有其他可以用做扩展语言的选择,这包括VisualBasic、Scheme、Elisp、Perl;Python和Javascript等,你可以依照个人喜好从中进行选择。Tcl拥有简单的结构,而且还有些地方类似于C语言,可以通过编制C过程来增添新的Tcl原语。Tcl非常易学,许多有关用户使用Tcl在很短的时间内(例如几个星期)就完成了相当难度的项目,并且他们以前压根就没有接触过Tcl。 当本书第一次出版时,Java轰动了计算机界。Java是一种极为优秀的系统编程语言,长远来看还有可能代替C和C什语言。这对Tcl来说挺好,它在设计时就被用来将由任意系统编程语言编写的构件粘连起来。Tcl过去被设计与C语言一起工作,但是现在已经被改造成能够与Java虚拟机一起工作。在我们提到"C或C++"的地方,现在也可以说"C、C++或Java"了,但是对于Java来说,其细节上还多少存在些差异。这本书并没有描述TcVJava接口,但是你可以在CD-ROM上找到TclBlend。TclBlend将Java虚拟机加载到你的Tc3应用程序中并允许你调用Java方法,它还可以让你使用Java而不是C或C十十来实现Tcl命令。 Javascript是一种来自于Netscape的语言,它被设计用来编写与w曲页面进行交互的脚本程序。由于Netscape的广泛使用,Javascript就显得很重要,然而Tcl提供了一种更为通用的脚本方案,可以在更为广泛的范围中使用。Tcl/Tk的Web浏览器插件提供了一种在浏览器中运行Tcl的方式,结果使得Tcl更像是一种Java的替代品而不是Javascript的替代品。该插件可以让你在浏览器中执行Tcl应用程序,而Javascript则为你提供了对浏览器和HTML显示的精细控制。这种插件将在第20章有所描述。TcI与Tk的版本 Tcl与Tk仍在继续演变。请参看http://www.beedub.com/book/来了解有关最新的Tcl版本的更新和消息。由于历史原因,Tcl与Tk曾各有各的版本号,但是它们成对发行,并一起工作。这本书的原始版本基于Tcl7.4和Tk 4.0并有几处引用了Tk 3.6的功能。第三版已经进行了更新,它反映了直到Tcl/Tk8.2以来所增添的各种新特性: . Tcl7.5和Tk 4.1的最终发布在1996年5月。这些版本的特点是将Tk移植到了Windows和Macintosh环境。它引入了Safe-Tcl安全机制,以支持网络小应用程序(Applet)的 .安全执行。它还提供了对网络套接字的支持以及一种新的输入输出(I/O)子系统,以支持高性 能的事件驱动I/O。 . Tcl7.6和Tk4.2的最终发布是在1996年的10月。这些版本包含了对S池-Tcl的改进,以及对在Tk 4.1中引进的grid布局管理器的改进。跨平台的支持包括虚拟事件(例如,以<<Copy>>宋代表<Control-c>=、标准对话框,还有更多的文件操作命令。 . Tcl 7.7和Tk 4.3是内部版本,用于开发NetscapeNavigator和MicrosoftInternetExplorer Web浏览器的Tcl/Tk插件。它们的开发工作实际上与Tcl7.6和Tk 4.2并行进行。Tcl/Tt插件已经发布了许多各种平台上的版本,其中包括Solaris/SPARC、Solaris/INTEL、SunOS、Linux、Digital UNIX、IRIX、HP/UX、Windows95、Windows NT以及Macintosh。该浏览器插件支持Web页面中的Tcl小应用程序(Applet),同时使用Safe-Tcl复杂的安全机制来提供安全保证。 . Tcl8.0为Tcl新增了一个运行时用的编译器,这个编译器提供了数倍于Tcl脚本的执行速度。Tcl8.0支持内嵌空字符的字符串。编译器对脚本来说是透明的,但是扩展模块编写入员需要学习一些新的C API才能发挥它的潜力。由于John Ousterhout从Sun微系统公司到了Scriptics公司,发布8.0版的时间推迟了几年。广泛使用的版本8.0p2是在1997年完成的,但是最终的补丁版本8.0.5直到1999年春才发布。 . 在8.0时,Tk更改了版本号以与Tcl相匹配。Tk 8.0包含了一种新的独立于平台的字体机制,它还包含了本地化菜单和菜单条,以及更多的本地化组件,它们在Windows和Macintosh上拥有更好的本地化外观。 Tcl/Tk8.1新特性主要包括对Unicode的完整支持,以及线程安全,这样你就可以将Tcl嵌入到多线程的应用程序中。Unicode是一种新的正则表达式引擎,它提供了在Perl5中所能找到的所有功能。Tk为找到正确的用于显示Unicode字符的字体完成了卓越的工作,它还增加了一种信息目录设施,这样你就可以编写国际化的应用程序。Tcylk 8.1的发布史中还包括了Sun到Scriptics的过渡。第一个alpha版本完成于1997年秋,而最终的补丁版本完成于1999年5月。 Tcl/Tk 8.2主要是一个进行bug修正和稳固化的版本。它对TclC函数库API进行了几处微小增补,这样无须核心补丁程序也能支持更多的扩展模块。Tcl/Tk 8.2很快在1999年夏进入最终版本。 谁应当阅读本书 本书不仅适用于熟练的编程人员,同样也适用于初学者。对于初学者和熟练编程人员来说,我建议大家仔细学习一下策1章"Tcl的基本知识"。Tcl的编程模型被设计成一种简单的模式,它与许多编程语言存在差异。该模型基于字符串替换,你对这一点的正确理解很重要,这样才能避免在复杂情况下遇到麻烦。这本书的其余部分则包含了演示如何高效地使用Tcl与Tt的例子。每一章中都有对其中所描述的Tcl命令和Tk组件进行总结的表格,以供参考。 本书假定你有一些编程经验,但是你如果是个彻头彻尾的新手也能够读下去。对Unix shell的了解将会对你有所帮助,但这并不是必须的。在那些涉及Windows系统的地方,我会提供一些背景信息。第2章详细描述了在UNIX、Windows和Macintosh上使用Tcl与Tk的内容。 如何阅读本书 本书最好能在上机实习中使用,可以在计算机上尝试一下书中的例子。Tcl与Tk的命令手册尽管完整但却缺少上下文的的相关信息和例子,本书就试图填补在简明手册与现有的文档化或没有很好文档化的Tcl程序之间的空隙。 我推荐使用联机手册来查阅有关的Tcl/Tk命令。它为每个命令都提供了详细的参考指南,但是它没能提供完整的细节,这在每一次发布的版本中都有所不同。HTML版本的联机手册可以在随书的CD-ROM中找到。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值