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
中指定文件描述符,参数family
、type
和proto
的值会自动从文件描述符中检测。
创建套接字后,我们使用 bind()
方法将其绑定到地址和端口号。
将套接字绑定到地址:bind()
方法
使用 socket()
函数,在我们创建的套接字对象上调用 bind()
方法。
它需要一个包含套接字将绑定到的地址的元组。地址的格式可能会因你选择的地址系列而异。我们将创建一个地址族为 AF_INET
的套接字。因此,地址将包含主机名和端口号。
bind()
方法的语法如下。
bind((hostname, port))
你可以明确指定主机名
。如果你在本地机器上创建服务器,你可以将主机名指定为 localhost
或 127.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 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