嵌入式开发日记(9)——多线程与socket通信

尝试用多线程,socket网络编程,心跳检测机制,实时绘图,丢包检测机制,校验位检测,超时重传机制,  数据库存储等功能优化项目


多线程与socket编程:

参考链接:

https://blog.csdn.net/qq_39687901/article/details/81531101 Python多线程socket通信

https://www.runoob.com/python3/python3-multithreading.html  Python多线程

首先,需要明白的是socket的accept和recv这两个方法是阻塞线程的。这就意味着我们需要新开线程来处理这两个方法。

具体的程序流程大概是这样的:

1.新开一个线程用于接收新的连接(socket.accept())

2.当有新的连接时,再新开一个线程,用于接收这个连接的消息(socket.recv())

3.主线程做为控制台,接收用户的输入,进行其他操作

也就是说,服务端需要为每一个连接创建一个线程。

服务端(上位机)功能:

1 创建接口

2 接收多客户端数据

3 分别记录到本地txt

import serial
import socket  # 导入 socket 模块
from threading import Thread
import os
import time

ADDRESS = ('127.0.0.1', 8712)  # 绑定地址

g_socket_server = None  # 负责监听的socket

g_conn_pool = []   # 连接池

def init():
    """
    初始化服务端
    """
    global g_socket_server
    g_socket_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 创建 socket 对象
    g_socket_server.bind(ADDRESS)
    g_socket_server.listen(5)  # 最大等待数(有很多人理解为最大连接数,其实是错误的)
    print("服务端已启动,等待客户端连接...")


def accept_client():
    """
    接收新连接
    """
    while True:
        client, _ = g_socket_server.accept()  # 阻塞,等待客户端连接
        # 加入连接池
        g_conn_pool.append(client)
        # 给每个客户端创建一个独立的线程进行管理
        thread = Thread(target=message_handle, args=(client,))
        # 设置成守护线程
        thread.setDaemon(True)
        thread.start()

def message_handle(client):
    """
    消息处理
    """
    client.sendall("连接服务器成功!".encode(encoding='utf8'))
    Time = time.strftime("%Y-%m-%d") + '设备'+str(g_conn_pool.index(client)) # 记录当前时间
    file_write_obj = open(Time + ".txt", 'w')  # 建立记录文件
    file_write_obj.writelines("=================================================")
    file_write_obj.write('\n')
    file_write_obj.writelines(time.strftime("%Y-%m-%d  %X "))
    file_write_obj.write('\n')
    file_write_obj.writelines("===============================================")
    file_write_obj.write('\n')
    while True:
        #bytes = ''
        bytes = client.recv(1024)
       # print("客户端消息:", bytes.decode(encoding='utf8'))
        file_write_obj.writelines(str(bytes.decode(encoding='utf8'))) #记录到本地
        file_write_obj.write('\n') 
        if len(bytes) == 0:
            client.close()
            # 删除连接
            g_conn_pool.remove(client)
            print("有一个客户端下线了。")
            file_write_obj.close()
            break
            

if __name__ == '__main__':  # 主函数
    init()
    # 新开一个线程,用于接收新连接
    thread = Thread(target=accept_client)
    thread.setDaemon(True)
    thread.start()
    # 主线程逻辑
    while True:
        cmd = input("""--------------------------
输入1:查看当前在线人数
输入2:给指定客户端发送消息
输入3:关闭服务端
                    """)
        if cmd == '1':
            print("--------------------------")
            print("当前在线人数:", len(g_conn_pool))
        elif cmd == '2':
            print("--------------------------")
            while (1):
                index, msg = input("请输入“索引,消息”的形式:").split(",")
                g_conn_pool[int(index)].sendall(msg.encode(encoding='utf8'))
        elif cmd == '3':
            exit()

客户端(Arm板)功能:

1 数据的处理

2 数据的回传

import socket  # 导入 socket 模块
import serial

ACCData = [0.0] * 8
GYROData = [0.0] * 8
AngleData = [0.0] * 8  # 定义三个数组,分别存储加速度角速度与角度的值

FrameState = 0  # 通过0x后面的值判断属于哪一种情况
Bytenum = 0  # 读取到这一段的第几位
CheckSum = 0  # 求和校验位


def DueData(inputdata):  # 新增的核心程序,对读取的数据进行划分,各自读到对应的数组里
    global FrameState  # 在局部修改全局变量,要进行global的定义
    global Bytenum
    global CheckSum
    result = []
    for data in inputdata:  # 在输入的数据进行遍历
        if FrameState == 0:  # 当未确定状态的时候,进入以下判断
            if data == 0x55 and Bytenum == 0:  # 0x55位于第一位时候,开始读取数据,增大bytenum
                CheckSum = data
                Bytenum = 1
                continue
            elif data == 0x51 and Bytenum == 1:  # 在byte不为0 且 识别到 0x51 的时候,改变frame
                CheckSum += data
                FrameState = 1
                Bytenum = 2
            elif data == 0x52 and Bytenum == 1:  # 同理
                CheckSum += data
                FrameState = 2
                Bytenum = 2
            elif data == 0x53 and Bytenum == 1:
                CheckSum += data
                FrameState = 3
                Bytenum = 2
        elif FrameState == 1:  # acc    #已确定数据代表加速度
            if Bytenum < 10:  # 读取8个数据
                ACCData[Bytenum - 2] = data  # 从0开始
                CheckSum += data
                Bytenum += 1
            else:
                if data == (CheckSum & 0xff):  # 假如校验位正确
                    #s.send(get_acc(ACCData).encode('utf8'))
                    result.append(get_acc(ACCData))
                CheckSum = 0  # 各数据归零,进行新的循环判断
                Bytenum = 0
                FrameState = 0
        elif FrameState == 2:  # gyro
            if Bytenum < 10:
                GYROData[Bytenum - 2] = data
                CheckSum += data
                Bytenum += 1
            else:
                if data == (CheckSum & 0xff):
                    #s.send(get_gyro(GYROData).encode('utf8'))
                    result.append(get_gyro(GYROData))
                CheckSum = 0
                Bytenum = 0
                FrameState = 0
        elif FrameState == 3:  # angle
            if Bytenum < 10:
                AngleData[Bytenum - 2] = data
                CheckSum += data
                Bytenum += 1
            else:
                if data == (CheckSum & 0xff):
                    #s.send(get_angle(AngleData).encode('utf8'))
                    result.append(get_angle(AngleData))
                CheckSum = 0
                Bytenum = 0
                FrameState = 0
    return result


def get_acc(datahex):  # 加速度
    axl = datahex[0]
    axh = datahex[1]
    ayl = datahex[2]
    ayh = datahex[3]
    azl = datahex[4]
    azh = datahex[5]

    k_acc = 16

    acc_x = (axh << 8 | axl) / 32768 * k_acc
    acc_y = (ayh << 8 | ayl) / 32768 * k_acc
    acc_z = (azh << 8 | azl) / 32768 * k_acc
    if acc_x >= k_acc:
        acc_x -= 2 * k_acc
    if acc_y >= k_acc:
        acc_y -= 2 * k_acc
    if acc_z >= k_acc:
        acc_z -= 2 * k_acc
    resa = [str(acc_x), str(acc_y), str(acc_z)]
    return 'ACC:'+ ' '.join(resa)


def get_gyro(datahex):  # 陀螺仪
    wxl = datahex[0]
    wxh = datahex[1]
    wyl = datahex[2]
    wyh = datahex[3]
    wzl = datahex[4]
    wzh = datahex[5]
    k_gyro = 2000

    gyro_x = (wxh << 8 | wxl) / 32768 * k_gyro
    gyro_y = (wyh << 8 | wyl) / 32768 * k_gyro
    gyro_z = (wzh << 8 | wzl) / 32768 * k_gyro
    if gyro_x >= k_gyro:
        gyro_x -= 2 * k_gyro
    if gyro_y >= k_gyro:
        gyro_y -= 2 * k_gyro
    if gyro_z >= k_gyro:
        gyro_z -= 2 * k_gyro
    resg = [str(gyro_x), str(gyro_y),str(gyro_z)]
    return 'GYRO:'+ ' '.join(resg)

def get_angle(datahex):  # 角度
    rxl = datahex[0]
    rxh = datahex[1]
    ryl = datahex[2]
    ryh = datahex[3]
    rzl = datahex[4]
    rzh = datahex[5]
    k_angle = 180

    angle_x = (rxh << 8 | rxl) / 32768 * k_angle
    angle_y = (ryh << 8 | ryl) / 32768 * k_angle
    angle_z = (rzh << 8 | rzl) / 32768 * k_angle
    if angle_x >= k_angle:
        angle_x -= 2 * k_angle
    if angle_y >= k_angle:
        angle_y -= 2 * k_angle
    if angle_z >= k_angle:
        angle_z -= 2 * k_angle

    resan = [str(angle_x), str(angle_y), str(angle_z)]
    return 'ANGLE:'+ ' '.join(resan)

if __name__ == '__main__':  # 主函数
    ser = serial.Serial("com5", 115200, timeout=0.5)  # 打开端口,改到循环外
    print(ser.is_open)
    s = socket.socket()  # 创建 socket 对象
    s.connect(('127.0.0.1', 8712))
    print(s.recv(1024).decode(encoding='utf8'))
    while True:
        datahex = ser.read(33)  # 不用hex()转化,直接用read读取的即是16进制
        news ='  |  '.join(DueData(datahex))
        s.send(news.encode('utf8'))

以上功能,实现了上位机以多线程连接多个客户端,并且分别记录到本地文件的过程。

Github地址:https://github.com/hanjintao1996/jy61-/blob/master/%E5%AE%A2%E6%88%B7%E7%AB%AF%E2%80%94%E2%80%94%E7%94%A8socket%E4%BC%A0%E8%BE%93%E5%B9%B6%E8%AE%B0%E5%BD%95

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
你好!关于C++多线程和Socket通信的问题,我可以帮你解答。首先,多线程是一种并发编程的方,可以同时执行多个任务,提高程序的性能和响应能力。而Socket通信是一种基于网络的通信,可以在不同的计算机之间进行数据传输。 在C++中,你可以使用标准库中的thread类来创建和管理多线程。下面是一个简单的例子: ```cpp #include <iostream> #include <thread> // 线程函数 void threadFunc(int n) { std::cout << "Hello from thread! n = " << n << std::endl; } int main() { // 创建线程并启动 std::thread t(threadFunc, 42); // 等待线程结束 t.join(); return 0; } ``` 上面的代码中,我们使用thread类创建了一个新线程,并指定了要执行的线程函数threadFunc。线程函数可以带有参数,这里我们传递了一个整数n。通过调用t.join()来等待线程执行完毕。 关于Socket通信,C++提供了一些库来方便地进行网络编程,如Winsock和POSIX Socket等。你可以使用这些库来创建服务器和客户端程序,实现数据的发送和接收。 下面是一个简单的例子,展示了如何使用Winsock库创建一个简单的TCP服务器: ```cpp #include <iostream> #include <winsock2.h> int main() { // 初始化Winsock库 WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { std::cerr << "Failed to initialize Winsock" << std::endl; return 1; } // 创建套接字 SOCKET listenSocket = socket(AF_INET, SOCK_STREAM, 0); if (listenSocket == INVALID_SOCKET) { std::cerr << "Failed to create socket" << std::endl; WSACleanup(); return 1; } // 绑定地址和端口 sockaddr_in serverAddress; serverAddress.sin_family = AF_INET; serverAddress.sin_addr.s_addr = INADDR_ANY; serverAddress.sin_port = htons(8888); // 使用端口8888 if (bind(listenSocket, (sockaddr*)&serverAddress, sizeof(serverAddress)) == SOCKET_ERROR) { std::cerr << "Failed to bind socket" << std::endl; closesocket(listenSocket); WSACleanup(); return 1; } // 监听连接请求 if (listen(listenSocket, SOMAXCONN) == SOCKET_ERROR) { std::cerr << "Failed to listen on socket" << std::endl; closesocket(listenSocket); WSACleanup(); return 1; } // 接受连接请求 SOCKET clientSocket = accept(listenSocket, NULL, NULL); if (clientSocket == INVALID_SOCKET) { std::cerr << "Failed to accept client connection" << std::endl; closesocket(listenSocket); WSACleanup(); return 1; } // 接收和发送数据 char buffer[1024]; int bytesRead = recv(clientSocket, buffer, sizeof(buffer), 0); if (bytesRead > 0) { std::cout << "Received data: " << buffer << std::endl; send(clientSocket, buffer, bytesRead, 0); } // 关闭套接字 closesocket(clientSocket); closesocket(listenSocket); WSACleanup(); return 0; } ``` 上面的例子中,我们使用了Winsock库来创建一个TCP服务器。首先,我们使用WSAStartup函数来初始化Winsock库,然后创建一个套接字listenSocket,并绑定地址和端口。接下来,我们使用listen函数开始监听连接请求,并使用accept函数接受客户端的连接。 一旦连接建立,我们可以使用recv函数接收客户端发送的数据,并使用send函数将数据发送回客户端。最后,我们关闭套接字并调用WSACleanup函数来清理Winsock库的资源。 这只是一个简单的示例,你可以根据自己的需求进行更复杂的多线程和Socket通信的实现。希望对你有所帮助!如果有任何疑问,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值