Python C/S mode socket network programming WebApi heartbeat model

Python C/S mode socket network programming WebApi heartbeat model

1. Case requirements

In this experiment, the intention is to actively and regularly initiate a request from the client, and pull the data sent to the client from the server - this solves the data downlink (from the server to the client) requirements as follows: 1. Write a
client 1. A server-side program
2. Server-side program requirements:
1) Provide only one interface;
2) Deploy in WebApi mode
3) Data is encapsulated in Json format
4) Every 500ms, generate a random number (within the int range) and cache it in the collection / in the array
5) When the interface is called, return the random number set generated after the last request to the client, and clear the corresponding set
3. Client program requirements:
1) Use a timer in the program, every 10 seconds Call the WebApi on the server side once
2) After each call, display the retrieved data on the console black window

2. Code implementation

1. Source code

# encoding:utf-8
"""
server.py
"""
__author__ = 'Regan_zhx'

import socket, sys, json, time
from threading import Thread, Lock
from queue import Queue
from random import randint


class Server:
    def __init__(self, port=9999):
        self.queue = Queue() # 存储随机数的队列
        self.lock = Lock() # 全局锁
        self.HOST = socket.gethostname()  # 主机名
        self.PORT = port  # 端口
        self.BUF_SIZE = 4096  # 缓冲区大小
        self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 创建socket套接字
        self.server.bind((self.HOST, self.PORT))  # 套接字绑定
        self.server.listen()  # 监听

    def server_thread(self, conn): #服务端线程
        global data_loaded
        while True:
            try:
                data = conn.recv(self.BUF_SIZE) #接受客户端发来的心跳包
                data_loaded = json.loads(data.decode('utf8')) #解码
                print("Receive the heart beat from ip:" + str(data_loaded['ip']) + " |status: " + data_loaded['status']
                       + " |pid: " + str(data_loaded['pid']) + " |Time: "+time.strftime('%Y-%M-%d %H:%m:%S'))
                if not self.queue.empty(): # 如果队列不为空则发送全部内容
                    num_dict = {
    
    }
                    num_id = 0
                    self.lock.acquire() # 上锁,保证线程安全
                    while not self.queue.empty():
                        num_dict[num_id] = self.queue.get()
                        num_id += 1
                    self.lock.release()
                    data_package = json.dumps(num_dict)
                    conn.sendall(data_package.encode('utf8')) # sendall发送TCP完整数据包
            except socket.error:
                print("One Client (IP: %s) Connected over!" % data_loaded['ip'])
                break
        conn.close()

    def produce_thread(self, queue): # 随机数生成单独线程
        while True:  # 每500ms生成一个数字加入到queue队列中
            num = randint(-2 ** 32, 2 ** 32) # 随机整数生成
            queue.put(num) # 放入队列
            time.sleep(0.5) # 睡眠500ms

    def run(self):
        print("Welcome to the WebApi Heart Beat Model!")
        Thread(target=self.produce_thread, args=(self.queue,)).start()  # 开启生成数字的线程
        while True:
            try:
                conn, addr = self.server.accept() # 被动接受TCP客户端连接,一直等待直到连接到达(阻塞)
                print("Connected with %s:%s " % (addr[0], addr[1]))
                Thread(target=self.server_thread, args=(conn,)).start()  # 开启线程
            except Exception as e:
                print(e)
                self.server.close()
                break


if __name__ == '__main__':
    Server().run()

# encoding:utf-8
"""
client.py
"""
__author__ = 'Regan_zhx'

import socket, sys, os
import time, json
from threading import Thread


class Client:
    def __init__(self, port=9999):
        self.host = socket.gethostname()
        self.port = port
        self.BUF_SIZE = 4096
        self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.remote_ip = socket.gethostbyname(self.host)
        self.client.connect((self.remote_ip, self.port)) # 客户端主动连接服务端
        """
        Bug在这里
        self.client.setblocking(False)
        """
        print("Socket connected to %s on ip %s" % (self.host, self.remote_ip))

    def heart_beat(self):
        while True:
            try:
                host_name = socket.gethostname()
                # 发送请求包,并告知ip,状态和进程ID
                data_to_server = {
    
    'ip': socket.gethostbyname(host_name), 'status': 'alive', 'pid': os.getpid()}
                data_dumped = json.dumps(data_to_server)
                try:
                    self.client.sendall(data_dumped.encode()) # 发送心跳数据包
                except socket.error:
                    print("Send failed!!")
                    break
                try:
                    data = self.client.recv(self.BUF_SIZE) # 接收返回数据包
                    data_package = json.loads(data.decode('utf8'))
                    print(data_package)
                except:
                    print("Receive failed!!")
                    break
            except:
                print("One Server Connected over!")
                self.client.close()
                break
            time.sleep(10) #睡眠10s后再发送心跳


if __name__ == '__main__':
    Client().heart_beat()

2. Key points explained

This article refers to Python Socket to realize heartbeat monitoring
. However, the original article is old and uses the syntax of Python2. The structure of the socket at that time is still somewhat different from today's, but it is basically the same.
Tip1 : Obviously, I have slightly encapsulated the server and client to make the code easier to understand and read. I will uniformly set the class initialization parameter port to 9999 in the future. Of course, this can be changed arbitrarily for readers, but it must be guaranteed The ports of the client and server are the same.
Tip2 : I encountered a bug when I was writing, that is, the server can transmit data normally, but the client cannot receive normally, but there is no problem with the program when I turn on the debug mode, so I subconsciously check the socket parameters, and also It is the place where the bug is specially marked in the above code. This place is setblocking(0) in the reference article. Since 0 is equivalent to False, I changed it to False, that is, non-blocking mode, and non-blocking is the problem. In non-blocking mode, the client does not wait for the data packet sent by the server, but executes it downwards, which naturally triggers an exception. So just delete this sentence or set it to True blocking mode.
Tip3 : There are multi-threaded situations in this program, which is to comply with the topic, because the threads about the socket will be blocked because of receiving or sending events, so it is necessary to use concurrency to perform different tasks at the same time. For example, the starting position of the produce_thread thread is also particular. The purpose of not putting the while loop in the lower layer is to let the thread start generating random integers when the program starts, instead of starting to generate random integers when the client initiates a request.

3. Effect demonstration

Use Pycharm to run two py programs at the same time for easy observation.
In order to speed up the program, set the server to generate a random integer sleep time of 0.3s, and set the client request time interval to 1s. It can be seen that the time interval of each log is exactly 1s.
insert image description here
Since it is impossible to start the client so quickly after startup, the number accumulated in the queue on the server side will be relatively large, and it will tend to be stable later.
insert image description here

Guess you like

Origin blog.csdn.net/weixin_43594279/article/details/116070733