python socket并行收发

python socket parallel sending and receiving

我正在开发一个简单的项目,该项目将“服务器”cpu 和 ram 使用情况与 pstuil 库一起发送给客户端,但我遇到了困难。简而言之,我的问题是我无法验证客户端收到的数据。
我的服务器代码:

# Import Section
import socket
import threading
import psutil
import time

# TCP Socket Connection for server
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("127.0.0.1", 6767))
server.listen()
client, address = server.accept()


# Functions
def cpu():
    while True:
        cpu_usage = str(psutil.cpu_percent(1))
        client.send(cpu_usage.encode("ascii"))


def ram():
    while True:
        time.sleep(1)
        ram_usage = str(psutil.virtual_memory()[2])
        client.send(ram_usage.encode("ascii"))


# Threads
t1 = threading.Thread(target=cpu).start()
t2 = threading.Thread(target=ram).start()

我的客户代码:

# Import Section
import socket
import threading
import time

# TCP Socket Connection for server
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.connect(("127.0.0.1", 6767))


# Functions
def cpu():
    while True:
        server.send("cpu".encode("ascii"))
        cpu_usage = server.recv(1024).decode("ascii")
        print(f"cpu usage is {cpu_usage}")
        time.sleep(1)


def ram():
    while True:
        server.send("ram".encode("ascii"))
        ram_usage = server.recv(1024).decode("ascii")
        print(f"ram usage is {ram_usage}")
        time.sleep(1)


# Threads
t1 = threading.Thread(target=cpu).start()
t2 = threading.Thread(target=ram).start()

我的预期输出:

cpu usage is cpu_usage
ram usage is ram_usage

example: 
cpu usage is 0.3
ram usage is 14.7

实际输出:

randomly
cpu usage is ram_usage
ram usage is cpu_usage

or

cpu usage is cpu_usage
ram usage is ram_usage

example:
cpu_usage is 14.7
ram usage is 0.3
cpu usage is 0.3
ram usage is 14.7

好像不是在一行中发送的。一个答案是通过服务器发送所有数据,但我只想发送原始 cpu 或 ram 数据并在客户端代码中对它们进行排序。

感谢您提供代码,我能够轻松地在我的 VM 上重现该问题。

简短回答:您将 RAM 和 CPU 数据包发送到同一个套接字,当您调用 server.recv() 时,您可以获取 RAM或 CPU 数据包,无论您调用 recv() 时使用的函数如何。

更多信息:虽然 TCP 保证了数据包的顺序,但是当为每个功能使用不同的线程时,它们可能会失去同步,以至于您会到达在服务器端或客户端调用 ram() (或反之亦然)之前连续两次调用 cpu() 的点。您甚至可以看到这种情况的发生,因为屏幕上的打印不一致,有时会连续打印 CPU 或 RAM 两次。

有几种方法可以解决这个问题。通过为每个功能使用不同的套接字,您可以确保永远不会混淆(参见下面的代码)。另一种选择是仅 运行 在客户端请求时在服务器端进行测量。我不确定这是否是您打算在客户端代码中执行的操作:

在cpu():

cpu_server.send("cpu".encode("ascii"))

在 ram():

ram_server.send("ram".encode("ascii"))

我可以看到这些数据包在 tcpdump 中发出,但我不认为它们有任何作用,因为服务器没有从套接字中获取它们。

您的每次测量套接字代码(随意use/modify):

server.py:

# Import Section
import socket
import threading
import psutil
import time

# Functions
def cpu():
    # TCP Socket Connection for CPU server
    cpu_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    cpu_server.bind(("127.0.0.1", 6767))
    cpu_server.listen()
    cpu_client, cpu_address = cpu_server.accept()
    while True:
        cpu_usage = str(psutil.cpu_percent(1))
        cpu_client.send(cpu_usage.encode("ascii"))


def ram():
    # TCP Socket Connection for RAM server
    ram_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    ram_server.bind(("127.0.0.1", 6768))
    ram_server.listen()
    ram_client, ram_address = ram_server.accept()
    while True:
        time.sleep(1)
        ram_usage = str(psutil.virtual_memory()[2])
        ram_client.send(ram_usage.encode("ascii"))

# Threads
t1 = threading.Thread(target=cpu).start()
t2 = threading.Thread(target=ram).start()

client.py:

# Import Section
import socket
import threading
import time

# Functions
def cpu():
    # TCP Socket Connection for CPU server
    cpu_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    cpu_server.connect(("127.0.0.1", 6767))
    while True:
        cpu_usage = cpu_server.recv(1024).decode("ascii")
        print(f"cpu usage is {cpu_usage}")
        time.sleep(1)

def ram():
    # TCP Socket Connection for RAM server
    ram_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    ram_server.connect(("127.0.0.1", 6768))
    while True:
        ram_usage = ram_server.recv(1024).decode("ascii")
        print(f"ram usage is {ram_usage}")
        time.sleep(1)

# Threads
t1 = threading.Thread(target=cpu).start()
t2 = threading.Thread(target=ram).start()

请注意 - 由于操作阻塞且响应时间太慢,我不得不在线程内进行 TCP 连接。当我尝试从主进程连接时,客户端出现问题,因为当它在第二个套接字上调用 connect() 时,服务器端可能尚未侦听其第二个套接字。