Python

Python-TCP与UDP 编程

2024-09-11  本文已影响0人  阿凡提说AI

套接字是用于网络通信的数据结构。在任何类型的通信开始之前,都必须创建Socket,可以将它们比作电话插孔,没有它就无法通信。
在计算机网络中,根据通信协议的不同,套接字(Socket)可以分为两大类:

面向连接的Socket(SOCK_STREAM)

无连接Socket(SOCK_DGRAM)

创建TCP套接字(SOCK_STREAM)

int tcp_socket = socket(AF_INET, SOCK_STREAM, 0);
if (tcp_socket == -1) {
    // 处理错误
}

创建UDP套接字(SOCK_DGRAM)

int udp_socket = socket(AF_INET, SOCK_DGRAM, 0);
if (udp_socket == -1) {
    // 处理错误
}

在实际应用中,无论是TCP还是UDP套接字,都需要进行适当的配置和错误处理,以确保网络通信的稳定性和效率。
在Python中创建一个基本的TCP Socket服务端程序的步骤。下面是一个简单的示例代码:

import socket
# 1. 创建socket对象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 绑定Socket服务端端口号
host = 'localhost'  # 也可以使用特定的IP地址,如 '192.168.1.2'
port = 12345
server_socket.bind((host, port))
# 3. 监听端口号
server_socket.listen(5)  # 参数5表示最大连接数
print(f"Server is listening on {host}:{port}")
# 4. 等待客户端连接
client_socket, client_address = server_socket.accept()
print(f"Connected to {client_address}")
# 5. 读取客户端发送过来的数据
data = client_socket.recv(1024)  # 1024表示接收的最大数据量
print(f"Received from client: {data.decode()}")
# 6. 向客户端发送数据
message = "Hello, Client!"
client_socket.send(message.encode())
# 7. 关闭客户端连接
client_socket.close()
# 8. 关闭服务端连接
server_socket.close()

请注意以下几点:

如果客户端传给服务器的数据过多,需要分多次读取,每次最多读取缓冲区尺寸的数据。可以根据当前读取的字节数是否小于缓冲区的尺寸来判断是否后面还有未读的数据,如果没有,终止循环。

import socket

# 创建socket对象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 绑定端口号
host = 'localhost'
port = 12345
server_socket.bind((host, port))

# 监听端口号
server_socket.listen(5)
print(f"Server is listening on {host}:{port}")

# 等待客户端连接
client_socket, client_address = server_socket.accept()
print(f"Connected to {client_address}")

# 设置缓冲区大小
buffer_size = 1024

# 循环读取数据
data_all = b''  # 用于存储所有接收到的数据
while True:
    data = client_socket.recv(buffer_size)
    if not data:  # 如果没有数据,则客户端关闭了连接
        break
    data_all += data  # 累积接收到的数据
    # 如果接收到的数据小于缓冲区大小,可能已经读完所有数据
    if len(data) < buffer_size:
        break

# 打印接收到的所有数据
print(f"Received from client: {data_all.decode()}")

# 向客户端发送响应
response = "Data received successfully"
client_socket.send(response.encode())

# 关闭连接
client_socket.close()
server_socket.close()

在服务端Socket有一个请求队列,如果服务器暂时无法处理客户端请求,会先将客户端请求放到这个队列里,而每次调用accept方法,都会在从这个队列中取一个客户端请求进行处理。

在TCP服务器编程中,当服务器的listen方法被调用时,它会创建一个请求队列(也称为完成连接队列),用于存储那些已经完成TCP三次握手但尚未被应用程序通过accept方法接受处理的客户端连接。
以下是关于请求队列的一些详细信息:

请求队列的工作原理:

  1. 监听队列大小listen方法有一个参数叫做backlog,它定义了请求队列的最大长度。如果队列满了,新的连接请求可能会被系统拒绝,这取决于具体的操作系统和网络配置。
  2. 三次握手:当一个客户端尝试连接到服务器时,它会执行TCP的三次握手过程。如果握手成功,客户端的连接请求就会被放入服务器的请求队列中。
  3. 处理连接:服务器调用accept方法时,它会从请求队列中取出第一个连接请求,创建一个新的套接字用于与该客户端通信,并从队列中移除该请求。
  4. 队列溢出:如果请求队列满了,新的连接请求可能会被拒绝。在Linux系统中,这通常会导致客户端收到一个错误,比如ECONNREFUSED

示例代码中的listen方法:

server_socket.listen(5)

在这个例子中,backlog被设置为5,这意味着请求队列可以容纳最多5个未处理的连接请求。

处理请求队列中的连接:

while True:
    client_socket, client_address = server_socket.accept()
    # 处理客户端连接...

在这个无限循环中,服务器会不断地调用accept方法来处理请求队列中的连接。每次调用accept时,如果队列中有等待的连接,它就会处理一个连接;如果没有,accept方法会阻塞,直到有新的连接到来。

注意事项:

时间戳服务端:当客户端向服务端发送数据时,服务端给客户端发送数据,并附带上服务器当前的时间,使用time模块中的ctime函数获取当前时间。

import socket
import time

# 创建socket对象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 绑定端口号
host = 'localhost'  # 也可以使用特定的IP地址
port = 12345
server_socket.bind((host, port))

# 监听端口号
server_socket.listen(5)
print(f"Server is listening on {host}:{port}")

try:
    while True:
        # 等待客户端连接
        client_socket, client_address = server_socket.accept()
        print(f"Connection from {client_address} has been established.")

        # 接收客户端数据
        data = client_socket.recv(1024)
        if data:
            # 打印接收到的数据
            print(f"Received: {data.decode()}")

            # 获取当前时间
            current_time = time.ctime()

            # 向客户端发送当前时间
            response = f"Server time: {current_time}"
            client_socket.send(response.encode())

        # 关闭客户端连接
        client_socket.close()
except KeyboardInterrupt:
    # 关闭服务端连接
    server_socket.close()
    print("Server has been shut down.")

以下是使用Python实现客户端Socket连接的步骤,以及一个简单的示例代码:

客户端Socket步骤:

  1. 创建客户端socket。
  2. 连接到服务端socket。
  3. 发送数据到服务端。
  4. 从服务端接收数据。
  5. 关闭客户端socket。

示例代码:

import socket
# 1. 创建客户端socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 服务器地址和端口号
server_host = 'localhost'  # 或者服务器的IP地址
server_port = 12345
try:
    # 2. 连接到服务端socket
    client_socket.connect((server_host, server_port))
    print(f"Connected to server at {server_host}:{server_port}")
    # 3. 发送数据到服务端
    message = "Hello, Server!"
    client_socket.send(message.encode())
    # 4. 从服务端接收数据
    response = client_socket.recv(1024)
    print(f"Received from server: {response.decode()}")
finally:
    # 5. 关闭客户端socket
    client_socket.close()
    print("Connection closed.")

在这个示例中,客户端首先创建了一个socket对象,然后连接到指定的服务器地址和端口。客户端发送一条消息到服务器,并等待接收服务器的响应。最后,客户端关闭了socket连接。
请注意,在实际应用中,应该添加异常处理来确保在出现错误时能够正确地关闭socket连接,并处理可能发生的异常情况。此外,如果服务器发送的数据可能超过1024字节,客户端可能需要在一个循环中接收数据,直到所有数据都被接收完毕。

UDP Socket接收数据的方法是recvfrom,发送数据的方法是sendto
以下是使用Python实现UDP服务端和客户端的示例代码:

UDP服务端示例代码:

import socket
# 1. 创建socket链接
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 2. 绑定
server_address = ('localhost', 12345)
server_socket.bind(server_address)
try:
    while True:
        # 3. 接收数据
        print("Waiting to receive message...")
        data, client_address = server_socket.recvfrom(4096)
        print(f"Received message: {data.decode()} from {client_address}")
        # 4. 发送数据
        message = f"Server time: {time.ctime()}"
        server_socket.sendto(message.encode(), client_address)
finally:
    # 5. 关闭socket
    server_socket.close()
    print("Server socket closed.")

UDP客户端示例代码:

import socket
# 1. 创建socket链接
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 服务端地址
server_address = ('localhost', 12345)
try:
    # 2. 向服务端发送数据
    message = "Hello, UDP Server!"
    client_socket.sendto(message.encode(), server_address)
    # 3. 接收服务端返回数据
    print("Waiting for response...")
    response, server_address = client_socket.recvfrom(4096)
    print(f"Received from server: {response.decode()}")
finally:
    # 4. 关闭socket
    client_socket.close()
    print("Client socket closed.")

在这个示例中,UDP服务端创建了一个socket,并将其绑定到一个地址和端口上。然后它进入一个无限循环,等待接收客户端发送的数据。一旦接收到数据,服务端会向客户端发送一条包含当前时间的消息。
UDP客户端创建了一个socket,然后向服务端发送一条消息,并等待接收服务端的响应。在接收到响应后,客户端关闭了socket。
请注意,UDP是无连接的协议,因此不需要显式地建立连接。UDP客户端和服务端之间的通信是通过发送和接收数据报文来完成的。另外,由于UDP不保证数据的可靠传输,因此在实际应用中可能需要额外的机制来确保数据的完整性和顺序。

socketsever是标准库的一个模块,目的是让Socket编程更简单。
socketsever模块中提供了个TCPServer类,用于实现TCP服务端。TCPServer类的构造方法有两个参数:第1个参数需要传入 host和port(元组形式);第二个参数需要传入一个回调类,该类必须是StreamRequestHandler类的子类。在StreamRequestHandler类中需要实现一个handle方法,如果接收到客户端的响应,那么系统就会调用handle方法进行处理,通过handle方法中的self参数中的响应API可以与客户端进行交互。(self.wfile.write(...), self.rfile.readlines)
让服务端等待客户端的连接:tcpServer.serve_forever()
。以下是如何使用socketserver.TCPServer类和socketserver.StreamRequestHandler类来创建一个简单的TCP服务端的示例:

import socketserver
# 定义请求处理器类,继承自StreamRequestHandler
class MyTCPHandler(socketserver.StreamRequestHandler):
    
    def handle(self):
        # self.rfile is a file-like object created by the handler;
        # we can now use e.g. readline() instead of raw recv() calls
        self.data = self.rfile.readline().strip()
        print(f"Received data: {self.data}")
        # Likewise, self.wfile is a file-like object used to write back
        # to the client
        self.wfile.write(self.data.upper())
# 主函数,设置服务器
if __name__ == "__main__":
    # 设置服务器地址和端口
    HOST, PORT = "localhost", 9999
    # 创建TCPServer实例,并将处理器类传递给它
    with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server:
        # 启动服务器,serve_forever将处理到来的连接,直到停止
        server.serve_forever()

在这个例子中,MyTCPHandler类继承自socketserver.StreamRequestHandler。在handle方法中,我们读取客户端发送的数据,将其转换为大写,然后发送回客户端。
以下是代码中各个部分的解释:

上一篇 下一篇

猜你喜欢

热点阅读