Python 中的套接字编程:初学者指南

Aditya Raj 2023年10月10日
  1. 在 Python 中如何实现 Socket 编程
  2. 如何在 Python 的 Socket 编程中创建服务器
  3. 在 Python 中使用 UDP 协议进行套接字编程
  4. 结论
Python 中的套接字编程:初学者指南

通常,当我们编写程序时,我们不需要与其他程序或计算机进行通信。

但是,我们可能需要与其他计算机通信以创建具有服务器-客户端架构的信使或其他应用程序。为了创建这样的应用程序,我们可以使用 Python 中的套接字编程。

本文将讨论 Python 中套接字编程的基础知识。我们还将使用 TCP 和 UDP 协议的套接字编程分别实现一个简单的消息应用程序。

Python 中的套接字是什么

当两个应用程序或进程交互时,它们使用指定的通信通道。套接字是此类通信通道的端点或入口点。

我们可以使用套接字在两个进程之间、一个进程内或不同机器上的进程之间建立通信通道。有不同类型的套接字,例如 TCP 套接字、UDP 套接字和 UNIX 域套接字。

在 Python 中如何实现 Socket 编程

Python 为我们提供了 socket 模块来实现套接字编程。socket 模块是标准 Python 库的一部分,它提供了你可以在 Python 中创建套接字的所有函数和方法。

你不需要在你的机器中显式下载 socket 模块,你可以使用如下 import 语句直接将其导入你的程序中。

import socket

要实现套接字编程,我们需要创建两个使用套接字进行通信的进程。

其中一个程序用作服务器,另一个程序用作客户端。服务器和客户端都有不同的功能。因此,我们在创建服务器和客户端进程时使用不同的功能。

让我们一一讨论如何创建服务器和客户端进程。

如何在 Python 的 Socket 编程中创建服务器

要创建服务器,我们将首先创建一个套接字。为此,我们使用 socket() 方法。

创建一个套接字:socket() 方法

socket() 方法的语法如下。

socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, fileno=None)

这里,

  • 参数 family 表示套接字所属的地址族。默认情况下,它是 AF_INET,并使用 Internet 协议版本 4 (IPv4) 地址创建一个套接字。你可以使用其他地址系列,例如用于 UNIX 地址的 AF_UNIX 和用于 Internet 协议版本 6 (IPv6) 地址的 AF_INET6
  • 参数 type 表示套接字类型。默认情况下,它的值 SOCK_STREAM 表示套接字将遵循面向连接的 TCP 协议。你可以使用 SOCK_DGRAM 创建遵循 UDP 协议的数据报套接字。
  • 参数 proto 表示协议号,通常为 0。如果在参数族中使用地址族 AF_CAN,则协议号应为 CAN_RAW、CAN_BCM、CAN_ISOTP 或 CAN_J1939 之一。
  • 参数 fileno 包含默认值 None。如果我们在 fileno 中指定文件描述符,参数 familytypeproto 的值会自动从文件描述符中检测。

创建套接字后,我们使用 bind() 方法将其绑定到地址和端口号。

将套接字绑定到地址:bind() 方法

使用 socket() 函数,在我们创建的套接字对象上调用 bind() 方法。

它需要一个包含套接字将绑定到的地址的元组。地址的格式可能会因你选择的地址系列而异。我们将创建一个地址族为 AF_INET 的套接字。因此,地址将包含主机名和端口号。

bind() 方法的语法如下。

bind((hostname, port))

你可以明确指定主机名。如果你在本地机器上创建服务器,你可以将主机名指定为 localhost127.0.0.1,这是 localhost 地址的默认值。

或者,你可以使用 gethostname() 方法获取主机名。对于参数 port,你可以使用大于 1024 且小于 65535 的任何端口号。

将套接字绑定到地址后,服务器会监听客户端的连接请求。为此,我们使用 listen() 方法。

监听连接:listen() 方法

listen() 方法的语法如下。

listen(backlog)

这里,参数 backlog 表示在拒绝新连接之前系统将允许的最大未接受连接数。

执行 listen() 方法后,服务器准备好接受连接。

接受连接请求:accept() 方法

服务器不断地在无限循环中运行,并监听客户端请求以接受来自客户端的连接。一旦找到客户端请求,服务器使用 accept() 方法接受请求。

accept() 方法返回一个元组 (client, address)。在这里,client 表示我们用来发送和接收消息的新套接字对象。地址 是绑定客户端套接字的位置。

与客户端通信:send()recv() 方法

接受连接后,服务器可以与客户端进行通信。

我们使用 send() 方法向客户端发送消息。send() 方法在 accept() 方法返回的 client 对象上调用。

我们使用 recv() 方法来接收消息。recv() 方法,当在 client 对象上调用时,接受一个数字,表示它可以从连接中读取的最大字节数。执行后,返回从连接中读取的数据。

所有操作完成后,我们需要关闭连接。为此,我们在 accept() 方法返回的 client 对象上调用 close() 方法。

在讨论了创建服务器所需的所有方法之后,让我们创建一个服务器进程。

import socket

mySocket = socket.socket(
    family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, fileno=None
)
print("Socket created.")
hostname = "localhost"
portno = 9999
mySocket.bind((hostname, portno))
print("Socket bound to address {} and port number {}".format(hostname, portno))
mySocket.listen(5)
print("Listening for client.")
while True:
    client, client_addr = mySocket.accept()
    print("Connection established with client at address {}".format(client_addr))
    msg = client.recv(1024).decode()
    print("Message received from the client:")
    print(msg)
    print("Sending acknowledgment to the client.")
    msg_out = "Message received: {}. Thank you.".format(msg).encode()
    client.send(msg_out)
    print("Terminating the connection.")
    client.close()
    break

现在我们已经创建了一个服务器,让我们创建一个将与服务器通信的客户端进程。

如何在 Socket 编程中创建客户端

要创建客户端,我们首先需要使用 socket() 方法创建一个套接字,就像我们在创建服务器时所做的那样。请记住,为客户端套接字定义的协议应该与服务器套接字相同。否则,程序将无法按预期运行。

创建套接字后,我们需要将其连接到服务器。为此,我们将使用 connect() 方法。

连接到服务器:connect() 方法

connect() 方法的语法如下。

connect((host, port))

这里,参数 host 表示服务器的地址。参数 port 表示创建服务器套接字的端口号。在创建服务器时,你应该为提供给 bind() 方法的主机和端口参数提供与输入相同的值。

与服务器通信

连接到服务器后,你可以使用 send()recv() 方法与服务器通信。最后,这将有助于使用 close() 方法从客户端关闭连接。

以下是我们将用于创建客户端进程的客户端程序。

import socket

mySocket = socket.socket(
    family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, fileno=None
)
print("Socket created.")
hostname = "localhost"
portno = 9999
mySocket.connect((hostname, portno))
print("Connection established with the server.")
msg = "Hi I am a TCP client created by Aditya."
print("Sending msg to the server:", msg)
mySocket.send(msg.encode())
msg_in = mySocket.recv(1024).decode()
print("Acknowledgment received from the server:")
print(msg_in)
print("Terminating the Connection.")
mySocket.close()

创建服务器和客户端后,让我们现在运行程序。请记住,你应该同时运行客户端程序和服务器程序,以便两个进程可以同时处于活动状态并相互通信。

带有服务器程序的终端中的输出将如下所示:

Socket created.
Socket bound to address localhost and port number 9999
Listening for client.
Connection established with client at address ('127.0.0.1', 37958)
Message received from the client:
Hi I am a TCP client created by Aditya.
Sending acknowledgment to the client.
Terminating the connection.

带有客户端程序的终端中的输出将如下所示:

Socket created.
Connection established with the server.
Sending msg to the server: Hi I am a TCP client created by Aditya.
Acknowledgment received from the server:
Message received: Hi I am a TCP client created by Aditya.. Thank you.
Terminating the Connection.

在 Python 中使用 UDP 协议进行套接字编程

在前面的部分中,我们在连接中创建了遵循 TCP 协议的套接字。在 TCP 协议中,客户端和服务器之间的连接在整个通信过程中保持不变。

但是,在很多情况下,由于资源限制,我们无法保持客户端和服务器之间的稳定连接。因此,我们需要一个不需要稳定连接的通信协议。为此,我们使用 UDP 协议。

在 Python 中如何使用 UDP 协议创建服务器

要使用 UDP 协议创建连接,我们需要在实现服务器时遵循以下步骤。

  • 在使用 socket() 方法创建服务器套接字时,在类型参数的输入中指定 SOCK_DGRAM
  • 使用 bind() 方法将套接字绑定到地址和端口号。
  • 由于我们不需要与客户端建立连接,因此我们不使用 listen()accept() 方法来建立连接。我们可以直接开始与客户沟通。
  • 要在 UDP 协议中接收消息,我们使用 recvfrom() 方法。它将要读取的字节数作为输入参数,并返回一个元组,其中包含数据和接收数据的地址。
  • 要在 UDP 协议中发送消息,我们使用 sendto() 方法。sendto() 方法将数据作为其第一个输入参数,并将包含主机名和端口号的元组作为数据将发送到的套接字地址。
  • 通信后,你必须使用 close() 方法关闭套接字。

使用以下 Python 程序,你可以实现一个与 UDP 协议通信的服务器进程。

import socket

mySocket = socket.socket(
    family=socket.AF_INET, type=socket.SOCK_DGRAM, proto=0, fileno=None
)
print("Socket created.")
hostname = "localhost"
portno = 9999
mySocket.bind((hostname, portno))
print("Socket bound to address {} and port number {}".format(hostname, portno))
while True:
    msg, client_addr = mySocket.recvfrom(1024)
    print("Message received from the client:")
    print(msg.decode())
    print("Sending acknowledgment to the client.")
    msg_out = "Message received: {}. Thank you.".format(msg).encode()
    mySocket.sendto(msg_out, client_addr)
    mySocket.close()
    break

在 Python 中如何使用 UDP 协议创建客户端

要创建遵循 UDP 协议的客户端进程,我们需要通过在 type 参数的输入中指定 SOCK_DGRAM 来创建套接字,同时使用 socket() 方法创建服务器套接字。我们不需要在这里使用 connect() 方法,因为我们不必创建连接。

创建套接字后,我们可以使用 sendto()recvfrom() 方法直接开始与服务器通信。与服务器通信后,不要忘记使用 close() 方法关闭套接字。

使用以下 Python 程序,你可以实现与 UDP 协议通信的客户端进程。

import socket

mySocket = socket.socket(
    family=socket.AF_INET, type=socket.SOCK_DGRAM, proto=0, fileno=None
)
print("Socket created.")
while True:
    msg = "Hi I am a UDP client created by Aditya."
    print("Sending msg to the server:", msg)
    mySocket.sendto(msg.encode(), ("localhost", 9999))
    msg_in = mySocket.recv(1024).decode()
    print("Acknowledgment received from the server:")
    print(msg_in)
    print("Terminating the Connection.")
    mySocket.close()
    break

同样,要观察输出,你应该同时运行客户端程序和服务器程序,以便两个进程可以同时处于活动状态并且可以相互通信。

带有服务器程序的终端中的输出将如下所示:

Socket created.
Socket bound to address localhost and port number 9999
Message received from the client:
Hi I am a UDP client created by Aditya.
Sending acknowledgment to the client.

带有客户端程序的终端中的输出将如下所示:

Socket created.
Sending msg to the server: Hi I am a UDP client created by Aditya.
Acknowledgment received from the server:
Message received: b'Hi I am a UDP client created by Aditya.'. Thank you.
Terminating the Connection.

结论

在本文中,我们讨论了 Python 中的套接字编程。我们还分别使用 TCP 和 UDP 协议实现了客户端和服务器程序,以学习 Python 中套接字编程的基础知识。

TCP 是面向连接的,因此是一种可靠的协议。使用 TCP 协议时,可以保证消息从服务器到达客户端,反之亦然。在 UDP 中,不能保证消息将被传递到所需的目的地。

另一方面,UDP 协议更快、更容易实现,而 TCP 协议速度较慢。另外,TCP 协议不能用于广播,而我们可以使用 UDP 协议进行广播。

根据可用资源和你的需要,你可以选择任何协议来使用套接字实现客户端-服务器通信。

作者: Aditya Raj
Aditya Raj avatar Aditya Raj avatar

Aditya Raj is a highly skilled technical professional with a background in IT and business, holding an Integrated B.Tech (IT) and MBA (IT) from the Indian Institute of Information Technology Allahabad. With a solid foundation in data analytics, programming languages (C, Java, Python), and software environments, Aditya has excelled in various roles. He has significant experience as a Technical Content Writer for Python on multiple platforms and has interned in data analytics at Apollo Clinics. His projects demonstrate a keen interest in cutting-edge technology and problem-solving, showcasing his proficiency in areas like data mining and software development. Aditya's achievements include securing a top position in a project demonstration competition and gaining certifications in Python, SQL, and digital marketing fundamentals.

GitHub

相关文章 - Python Socket

相关文章 - Python Module