KRTS网络模块:TCP服务端、客户端实例

news2024/9/22 7:29:48

KRTS网络模块:TCP服务端、客户端实例


目录

  • KRTS网络模块:TCP服务端、客户端实例
    • TCP简介
    • KRST服务端简介
        • 核心特性
        • 界面设计
        • 核心代码
    • KRTS客户端简介
        • 核心特性
        • 界面设置
        • 核心代码
    • 运行实例


Socket模块基于Packet模块,实时提供更高的协议,如RAW-IP、TCP 和 UDP(参见 以太网)。相关API的使用,请查阅 SocketAPI。

TCP简介

TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。TCP的主要功能包括:

  1. 连接管理:
    建立连接:三次握手(three-way handshake)来建立一个TCP连接。
    终止连接:四次挥手(four-way handshake)来终止一个TCP连接。

  2. 数据传输:
    可靠传输:通过序列号、确认应答、重传机制等确保数据可靠传输。
    流量控制:通过滑动窗口机制防止发送方发送速率过快导致接收方来不及处理。
    拥塞控制:通过拥塞窗口大小动态调整来避免网络拥塞。

  3. 错误检测:
    使用校验和来检测数据包中的错误。

  4. 排序:
    确保数据包按发送顺序到达接收端。

  5. 多路复用:
    支持在一个TCP连接上进行多个数据流的传输,通过端口号区分不同的应用进程。

TCP是Internet中最主要的协议之一,它位于TCP/IP协议栈的传输层,位于IP协议之上,为应用层提供服务。TCP协议确保了数据在网络上传输时的可靠性,这对于许多需要高可靠性的应用来说至关重要,例如Web浏览器、电子邮件、文件传输等。
在实际应用中,TCP通常与IP协议结合使用,共同构成了TCP/IP协议族的核心部分。TCP/IP协议族是互联网的基础,它定义了数据如何在网络中传输和寻址。

KRST服务端简介

KRTS服务端作为一个支持TCP协议的服务端程序,其主要功能包括支持IP地址和端口配置、实时数据处理以及能够接受来自通用TCP客户端和特定KRTS客户端的连接请求。下面是KRTS服务端的一些核心特性和实现细节:

核心特性
  1. IP 和端口配置:
    • 支持指定服务端监听的IP地址和端口号。
    • 支持绑定到特定的网络接口。
  2. 实时数据处理:
    • 实现高效的数据处理逻辑,确保低延迟和高吞吐量。
  3. 客户端连接管理:
    • 支持同时处理多个客户端连接。
    • 能够识别并处理来自不同类型的客户端(通用TCP客户端和KRTS客户端)的请求。
界面设计

Qt开发应用层界面

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

核心代码

内核层代码:

/*
 *     KRTS 网络服务器 - 内核程序
 *     版本: 0.1
 *     版权所有: 山东易码智能科技股份有限公司
 */

#include "Base/SharedData.h"

SharedData *kernel_data_ {nullptr};
constexpr int CLIENT_COUNT {10};                                        // 允许连接的客户端最大个数
KSHandle accept_socket_handle_[CLIENT_COUNT]{};                         // 连接的客户端

/* 套接字回调函数 */
KSError __stdcall SocketCallBack(void * /*pArgs*/, void *context);

extern "C" KSError __declspec(dllexport) __stdcall InitKernel(void *args, void * /*pContext*/)
{
    KS_printK("-------------- InitKernel \n");
    kernel_data_ = static_cast<SharedData *>(args);
    kernel_data_->start_more_client = true;
    /* 为传入数据创建管道。这将是一个消息管道 */
    KSError error = KS_createPipe(&kernel_data_->pipe_handle, "NetworkTcpServerPipe", 1, BUFFER_SIZE * 4, KS_INVALID_HANDLE, KSF_MESSAGE_PIPE);
    if (error != KS_OK) { return error; }

    /* 创建接收事件。 */
    error = KS_createEvent(&kernel_data_->receive_event_handle, "TcpServerReceiveEvent", KSF_NO_FLAGS);
    if (error != KS_OK) { return error; }

    // 创建关闭事件。
    error = KS_createEvent(&kernel_data_->close_event_handle, "TcpServerCloseEvent", KSF_NO_FLAGS);
    if (error != KS_OK) { return error; }

    // 为了对套接字上的事件做出反应,我们使用带有 KSF_DIRECT_EXEC 的回调。
    error = KS_createCallBack(&kernel_data_->socket_call_back, SocketCallBack, nullptr, KSF_DIRECT_EXEC, 0);
    if (error != KS_OK) { return error; }

    /* 打开网络适配器 */
    error = KS_openNetworkAdapter(&kernel_data_->adapter_handle, kernel_data_->device_name, nullptr, KSF_NO_FLAGS);
    if (error != KS_OK) { return error; }

    /* 网络将配置为使用在用户空间的应用程序中指定的 IP 地址、子网和网关。 */
    error = KS_execNetworkCommand(kernel_data_->adapter_handle, KS_NETWORK_SET_IP_CONFIG, &kernel_data_->ip_config, KSF_NO_FLAGS);
    if (error != KS_OK) { return error; }

    /* 在端口 80 上创建一个 TCP 服务器套接字。 */
    KSSocketAddr socket_addr = {0};
    socket_addr.family = KS_FAMILY_IPV4;
    socket_addr.port = KS_htons(kernel_data_->port_config);
    *socket_addr.address = kernel_data_->ip_config.localAddress;

    error = KS_openSocket(&kernel_data_->socket_handle, kernel_data_->adapter_handle, &socket_addr, KS_PROTOCOL_TCP, KSF_SERVER);
    if (error != KS_OK) return error;

    /* 安装 3 个用于连接、断开连接和接收的套接字处理器,它们都将使用相同的回调。 在回调内部,我们可以通过不同的上下文类型来区分触发条件 */
    error = KS_installSocketHandler(kernel_data_->socket_handle, KS_SOCKET_CONNECTED, kernel_data_->socket_call_back, KSF_NO_FLAGS);
    if (error != KS_OK) { return error; }

    error = KS_installSocketHandler(kernel_data_->socket_handle, KS_SOCKET_DISCONNECTED, kernel_data_->socket_call_back, KSF_NO_FLAGS);
    if (error != KS_OK) { return error; }

    error = KS_installSocketHandler(kernel_data_->socket_handle, KS_SOCKET_RECV, kernel_data_->socket_call_back, KSF_NO_FLAGS);
    if (error != KS_OK) { return error; }

    return KS_OK;
}

extern "C" KSError __declspec(dllexport) __stdcall ExitKernel(void * /*pArgs*/, void * /*pContext*/)
{
    KS_printK("-------------- ExitKernel \n");
    if (kernel_data_ == nullptr) { return KSERROR_FUNCTION_NOT_AVAILABLE; }

    /* 卸载三个套接字处理程序。 */
    // for (const auto& handle : accept_socket_handle_)
    // {
    //     if (handle != NULL)
    //     {
    //         KS_installSocketHandler(handle, KS_SOCKET_DISCONNECTED, KS_INVALID_HANDLE, KSF_NO_FLAGS);
    //         KS_installSocketHandler(handle, KS_SOCKET_RECV, KS_INVALID_HANDLE, KSF_NO_FLAGS);
    //     }
    // }

    KS_installSocketHandler(kernel_data_->socket_handle, KS_SOCKET_CONNECTED, KS_INVALID_HANDLE, KSF_NO_FLAGS);
    KS_installSocketHandler(kernel_data_->socket_handle, KS_SOCKET_DISCONNECTED, KS_INVALID_HANDLE, KSF_NO_FLAGS);
    KS_installSocketHandler(kernel_data_->socket_handle, KS_SOCKET_RECV, KS_INVALID_HANDLE, KSF_NO_FLAGS);
    /* 关闭套接字。 */
    KS_closeSocket(kernel_data_->socket_handle);
    /* 关闭网络适配器。 */
    KS_closeNetwork(kernel_data_->adapter_handle,KSF_NO_FLAGS);
    /* 移除回调。 */
    KS_removeCallBack(kernel_data_->socket_call_back);
    /*关闭事件 */
    KS_closeEvent(kernel_data_->receive_event_handle);
    KS_closeEvent(kernel_data_->close_event_handle);
    /* 删除消息管道 */
    KS_removePipe(kernel_data_->pipe_handle);

    return KS_OK;
}

KSError SendBufferContent(const KSHandle accept_socket, const byte *chr, const int length)
{
    if (kernel_data_ == nullptr) { return KSERROR_FUNCTION_NOT_AVAILABLE; }

    if (length != 0)
    {
        if (!kernel_data_->start_more_client)
        {
            KSError error = KS_sendSocket(accept_socket, chr, length, nullptr, KSF_WAIT);
            if (error != KS_OK) {return error;}

            const auto chr_end = "\r\n";
            error = KS_sendSocket(accept_socket, chr_end, 2, nullptr, KSF_WAIT);
            if (error != KS_OK){ return error;}
        }
        else
        {
            // 发个自己以外的其他人
            for (const auto& handle : accept_socket_handle_)
            {
                KS_printK("-------------- send handle %d \n", handle);
                if (handle != NULL /* && handle != accept_socket */)
                {
                    KSError error = KS_sendSocket(handle, chr, length, nullptr, KSF_WAIT);
                    if (error != KS_OK) {return KS_OK;}
                    const auto chr_end = "\r\n";
                    error = KS_sendSocket(handle, chr_end, 2, nullptr, KSF_WAIT);
                    if (error != KS_OK) {return error;}
                }
            }
        }
    }

    return KS_OK;
}

KSError __stdcall SocketCallBack(void * /*pArgs*/, void *context)
{
    KS_printK("-------------- SocketCallBack \n");
    const auto *socket_context = static_cast<KSSocketContext *>(context);

    /* 在数据接收时,我们将数据复制到管道中,并通过事件向用户空间中的接收线程发出信号。 */
    KSError error;
    switch (socket_context->ctxType)
    {
        case KS_SOCKET_CONNECTED:
        {
            /* 获取连接的远程地址, 即客户端地址 */
            KSSocketAddr remote_address;
            if (!kernel_data_->start_more_client)
            {
                /* 单个用户连接 */
                error = KS_acceptSocket(socket_context->hSocket, &remote_address, KSF_USE_TIMEOUTS);
                if (error != KS_OK)
                {
                    KS_printK("Error: 0x%X \n", error);
                    return KS_OK;
                }
            }
            else
            {
                /* 多客户端连接 */
                KSHandle accept_socket;
                error = KS_acceptSocketEx(socket_context->hSocket, &accept_socket, &remote_address, KSF_USE_TIMEOUTS);
                if (error != KS_OK)
                {
                    KS_printK("Error: 0x%X \n", error);
                    return KS_OK;
                }

                KS_printK("CLIENT: %d.%d.%d.%d:%d%s \n", *remote_address.address >> 0 & 0xFF, *remote_address.address >> 8 & 0xFF,
                    *remote_address.address >> 16 & 0xFF, *remote_address.address >> 24 & 0xFF, remote_address.port, "\n");

                // 将数据放入管道中以将其传输到应用程序。
                //KS_putPipe(kernel_data_->pipe_handle, remote_address.address, length, nullptr, KSF_NO_FLAGS);

                //向应用程序的接收线程发出信号,以便从管道中获取数据。
                //KS_setEvent(kernel_data_->receive_event_handle);

                error = KS_installSocketHandler(accept_socket, KS_SOCKET_DISCONNECTED, kernel_data_->socket_call_back, KSF_NO_FLAGS);
                if (error != KS_OK) { return error; }

                error = KS_installSocketHandler(accept_socket, KS_SOCKET_RECV, kernel_data_->socket_call_back, KSF_NO_FLAGS);
                if (error != KS_OK) { return error; }

                /* 判断客户端连接是否已存在 */
                bool is_exist{false};
                for (const auto& handle : accept_socket_handle_)
                {
                    if (handle == accept_socket)
                    {
                        is_exist = true;
                        break;
                    }
                }

                if (!is_exist)
                {
                    for (auto& handle : accept_socket_handle_)
                    {
                        if (handle == NULL)
                        {
                            KS_printK("-------------- accept_socket_handle_ add handle %d \n", accept_socket);
                            handle = accept_socket;
                            break;
                        }
                    }
                }
            }

            break;
        }
        case KS_SOCKET_DISCONNECTED:
        {
            /* 获取断开连接的远程地址, 即客户端地址 */
            if (kernel_data_->start_more_client)
            {
                /* 多客户端连接 */
                const KSHandle accept_socket = socket_context->hSocket;
                /* 判断客户端连接是否已存在,然后清空连接 */
                for (auto &handle: accept_socket_handle_)
                {
                    if (handle == accept_socket)
                    {
                        KS_printK("-------------- accept_socket_handle_ remove handle %d \n", accept_socket);
                        handle = NULL;
                    }
                }
            }
            break;
        }
        case KS_SOCKET_RECV:
        {
            byte read_buffer[BUFFER_SIZE];
            int length;
            error = KS_recvSocket(socket_context->hSocket, read_buffer, BUFFER_SIZE, &length, KSF_NO_FLAGS);
            if (error != KS_OK) { return KS_OK; }

            // 将数据放入管道中以将其传输到应用程序。
            KS_putPipe(kernel_data_->pipe_handle, read_buffer, length, nullptr, KSF_NO_FLAGS);

            //向应用程序的接收线程发出信号,以便从管道中获取数据。
            KS_setEvent(kernel_data_->receive_event_handle);

            // 回复数据
            byte send_buffer[BUFFER_SIZE];
            KSRTL_memcpy(send_buffer, read_buffer, length);
            if (SendBufferContent(socket_context->hSocket,send_buffer, length) != KS_OK) { return KS_OK; }
        }
        default:
        {
            break;
        }
    }
    return KS_OK;
}

#pragma pack(push, 8)
#include <windows.h>
#pragma pack(pop)

BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD reason, LPVOID pReserved)
{
    return TRUE;
}


应用层代码:

#include "KitharaUser.h"
#include "Universal/Universal.h"
#include <QtConcurrent>
#include <windows.h>

KitharaUser::KitharaUser(QObject *parent) : QObject(parent)
{
    /*  打开驱动  */
    if (const KSError error = KS_openDriver(customer_number_); error != KS_OK)
    {
        Universal::OutputErr(error, "KS_openDriver", "Unable to open the driver!");
        return;
    }
}

KitharaUser::~KitharaUser()
{
    /* 关闭驱动 */
    if (const KSError error = KS_closeDriver(); error != KS_OK)
    {
        Universal::OutputErr(error, "KS_closeDriver", "Unable to close the driver!");
    }
    Sleep(3000);
};

bool KitharaUser::Init()
{
    /* 创建共享内存 */
    KSError error = KS_createSharedMemEx(&shared_mem_handle_, "", sizeof(SharedData),KSF_NO_FLAGS);
    if (error != KS_OK)
    {
        Universal::OutputErr(error, "KS_createSharedMemEx", "Unable to create the shared memory!");
        return false;
    }

    /* 获取共享内存句柄,并于用户层数据绑定 */
    error = KS_getSharedMemEx(shared_mem_handle_, reinterpret_cast<void **>(&user_data_), KSF_NO_FLAGS);
    if (error != KS_OK)
    {
        Universal::OutputErr(error, "KS_getSharedMemEx", "Unable to get the shared memory!");
        return false;
    }

    /* 加载内核层DLl */
    error = KS_loadKernel(&user_data_->kernel_handle, "KernelServer.dll", nullptr, nullptr,KSF_KERNEL_EXEC | KSF_SAVE_FPU);
    if (error != KS_OK)
    {
        Universal::OutputErr(error, "KS_loadKernel", "Unable to load the kernel!");
        return false;
    }

    return true;
}

bool KitharaUser::Start()
{
    /* 遍历网卡 */
    /* 获取网卡名称 */
    KSError error = KS_enumDevices("NET", network_index_, user_data_->device_name, KSF_NO_FLAGS);
    if (error != KS_OK)
    {
        Universal::OutputErr(error, "KS_enumDevices", "Unable to query network device name!");
        return false;
    }

    // 为了发送 IP 数据包,我们必须设置自己的 IP 地址和子网掩码。
    // 注意:以太网帧中的数据必须是“大端”!
    KS_makeIPv4(&user_data_->ip_config.localAddress, 192, 168, 0, 181);
    KS_makeIPv4(&user_data_->ip_config.subnetMask, 255, 255, 255, 0);
    KS_makeIPv4(&user_data_->ip_config.gatewayAddress, 192, 168, 0, 1);
    user_data_->port_config = 80;

    error = KS_execKernelFunctionEx(user_data_->kernel_handle, "InitKernel", shared_mem_handle_, KS_INVALID_HANDLE, KSF_NO_FLAGS);
    if (error != KS_OK)
    {
        Universal::OutputErr(error, "KS_execKernelFunctionEx", "Unable to initialize the kernel DLL!");
        return false;
    }

    /* 创建线程 */
    future_ = QtConcurrent::run(&KitharaUser::RecevicePipeData,this);
    return true;
}

QStringList KitharaUser::GetNetworkList() const
{
    QStringList network_list;
    for (int i = 0;; ++i)
    {
        char device_name[256];
        if (const KSError error = KS_enumDevices("NET", i, device_name, KSF_NO_FLAGS); error != KS_OK)
        {
            if (KSERROR_CODE(error) != KSERROR_DEVICE_NOT_FOUND)
            {
                Universal::OutputErr(error, "KS_enumDevices", "Unable to query network device name!");
            }
            if (KSERROR_CODE(error) == KSERROR_DEVICE_NOT_FOUND && !i)
            {
                Universal::OutputTxt("No network adapters found");
                return {};
            }

            break;
        }
        network_list.append(device_name);
    }

    return network_list;
}

void KitharaUser::SetNetworkIndex(const int index)
{
    if (index < 0)
    {
        return;
    }

    network_index_ = index;
}

void KitharaUser::WaitForStop() const
{
    while (true)
    {
        if (const KSError error = KS_waitForEvent(user_data_->close_event_handle, KSF_NO_FLAGS, 50 * MS);
            error != KSERROR_WAIT_TIMEOUT && error != KSERROR_TIMEOUT || user_data_->is_finished != 0)
        {
            break;
        }
    }
}

void KitharaUser::Stop()
{
    if (user_data_ != nullptr)
    {
        user_data_->is_finished = 1;
    }
}

void KitharaUser::UnInit()
{
    // 等待线程退出
    future_.waitForFinished();

    KSError error;
    /* 卸载内核层DLL */
    if (shared_mem_handle_ != NULL && user_data_->kernel_handle != NULL)
    {
        if (error = KS_execKernelFunctionEx(user_data_->kernel_handle, "ExitKernel", KS_INVALID_HANDLE, KS_INVALID_HANDLE, KSF_NO_FLAGS)
            ; error != KS_OK)
        {
            Universal::OutputErr(error, "KS_execKernelFunctionEx", "Error while deallocating resources on kernel level!");
            return;
        }

        error = KS_freeKernel(user_data_->kernel_handle);
        if (error != KS_OK)
        {
            Universal::OutputErr(error, "KS_freeKernel", "Unable to unload the kernel!");
        }
    }

    /* 释放共享内存 */
    if (shared_mem_handle_ != NULL)
    {
        error = KS_freeSharedMemEx(shared_mem_handle_, KSF_NO_FLAGS);
        if (error != KS_OK)
        {
            Universal::OutputErr(error, "KS_freeSharedMemEx", "Unable to free the shared memory!");
        }
        else
        {
            user_data_ = nullptr;
        }
    }
}

void KitharaUser::RecevicePipeData()
{
    KSError error = KS_OK;
    char read_buffer[BUFFER_SIZE];
    int length;

    while (true)
    {
        if (user_data_->is_finished != 0)
        {
            break;
        }

        error = KS_waitForEvent(user_data_->receive_event_handle, KSF_NO_FLAGS, 5 * MS);
        if (error != KS_OK) { continue; }

        while (KS_OK == KS_getPipe(user_data_->pipe_handle, read_buffer, BUFFER_SIZE, &length, KSF_NO_FLAGS))
        {
            read_buffer[length] = '\0';
            Universal::OutputTxt(read_buffer, false);
            if (auto str = QString(read_buffer); !str.isEmpty())
            {
                emit SendData(str);
            }
        }
    }

    Universal::OutputTxt("Receiving thread has been finished.");
}

KRTS客户端简介

KRTS客户端作为一个支持TCP协议的客户端程序,其主要功能包括支持IP地址和端口配置、实时数据处理以及能够接受来自通用TCP服务端和特定KRTS服务端的连接。下面是KRTS客户端的一些核心特性和实现细节:

核心特性
  1. IP 和端口配置:
    • 支持指定连接IP地址和端口号。
    • 支持绑定到特定的网络接口。
  2. 实时数据处理:
    • 实现高效的数据处理逻辑,确保低延迟和高吞吐量。
界面设置

Qt开发应用层界面
在这里插入图片描述

核心代码

内核层代码:

/*
 *     KRTS 网络客户端 - 内核程序
 *     版本: 0.1
 *     版权所有: 山东易码智能科技股份有限公司
 */

#include "Base/SharedData.h"

SharedData *kernel_data_ {nullptr};
int64 send_time_{0};           // 发送时间
int64 last_jitter_time_{0};    // 上次抖动
/* 套接字回调函数 */
KSError __stdcall SocketCallBack(void * /*pArgs*/, void *context);

extern "C" KSError __declspec(dllexport) __stdcall InitKernel(void *args, void * /*pContext*/)
{
    KS_printK("-------------- InitKernel");
    kernel_data_ = static_cast<SharedData *>(args);

    /* 为传入数据创建管道。这将是一个消息管道 */
    KSError error = KS_createPipe(&kernel_data_->pipe_handle, "NetworkTcpClientPipe", 1, BUFFER_SIZE * 4, KS_INVALID_HANDLE, KSF_MESSAGE_PIPE);
    if (error != KS_OK) { return error; }

    /* 创建接收事件。 */
    error = KS_createEvent(&kernel_data_->receive_event_handle, "TcpClientReceiveEvent", KSF_NO_FLAGS);
    if (error != KS_OK) { return error; }

    // 打开网络适配器
    error = KS_openNetworkAdapter(&kernel_data_->adapter_handle, kernel_data_->device_name, nullptr, KSF_NO_FLAGS);
    if (error != KS_OK) { return error; }

    // 网络将配置为使用在用户空间的应用程序中指定的 IP 地址、子网和网关。
    error = KS_execNetworkCommand(kernel_data_->adapter_handle,KS_NETWORK_SET_IP_CONFIG, &kernel_data_->ip_config, KSF_NO_FLAGS);
    if (error != KS_OK) { return error; }

    // 创建客户端套接字
    KSSocketAddr socket_addr = {0};
    socket_addr.family = KS_FAMILY_IPV4;
    socket_addr.port = KS_htons(0);
    *socket_addr.address = kernel_data_->ip_config.localAddress;
    error = KS_openSocket(&kernel_data_->socket_handle, kernel_data_->adapter_handle, &socket_addr, KS_PROTOCOL_TCP,KSF_CLIENT);
    if (error != KS_OK) { return error; }

    // 连接服务器
    KSSocketAddr remote_addr = {0};
    remote_addr.family = KS_FAMILY_IPV4;
    remote_addr.port = KS_htons(kernel_data_->server_port);
    *remote_addr.address = kernel_data_->server_ip;
    error = KS_connectSocket(kernel_data_->socket_handle, &remote_addr, KSF_USE_TIMEOUTS);
    if (error != KS_OK) { return error; }

    // 为了对套接字上的事件做出反应,我们使用带有 KSF_DIRECT_EXEC 的回调。
    error = KS_createCallBack(&kernel_data_->socket_call_back, SocketCallBack, nullptr, KSF_DIRECT_EXEC, 0);
    if (error != KS_OK) { return error; } else{KS_printK("-------------- Connect Success!");}

    // 安装Socket回调
    /* 安装 3 个用于连接、断开连接和接收的套接字处理器,它们都将使用相同的回调。 在回调内部,我们可以通过不同的上下文类型来区分触发条件 */
    error = KS_installSocketHandler(kernel_data_->socket_handle, KS_SOCKET_CONNECTED, kernel_data_->socket_call_back, KSF_NO_FLAGS);
    if (error != KS_OK) { return error; }

    error = KS_installSocketHandler(kernel_data_->socket_handle, KS_SOCKET_DISCONNECTED, kernel_data_->socket_call_back, KSF_NO_FLAGS);
    if (error != KS_OK) { return error; }

    error = KS_installSocketHandler(kernel_data_->socket_handle, KS_SOCKET_RECV, kernel_data_->socket_call_back, KSF_NO_FLAGS);
    if (error != KS_OK) { return error; }

    return KS_OK;
}

extern "C" KSError __declspec(dllexport) __stdcall ExitKernel(void * /*pArgs*/, void * /*pContext*/)
{
    KS_printK("-------------- ExitKernel");
    if (kernel_data_ == nullptr) { return KSERROR_FUNCTION_NOT_AVAILABLE; }

    if (kernel_data_->socket_handle != NULL)
    {
        /* 卸载三个套接字处理程序。 */
        KSError error = KS_installSocketHandler(kernel_data_->socket_handle, KS_SOCKET_CONNECTED, KS_INVALID_HANDLE, KSF_NO_FLAGS);
        if (error != KS_OK) { return error; }

        error = KS_installSocketHandler(kernel_data_->socket_handle, KS_SOCKET_DISCONNECTED, KS_INVALID_HANDLE, KSF_NO_FLAGS);
        if (error != KS_OK) { return error; }

        error = KS_installSocketHandler(kernel_data_->socket_handle, KS_SOCKET_RECV, KS_INVALID_HANDLE, KSF_NO_FLAGS);
        if (error != KS_OK) { return error; }

        KS_closeSocket(kernel_data_->socket_handle);
        kernel_data_->socket_handle = NULL;
    }

    if (kernel_data_->adapter_handle != NULL)
    {
        KS_closeNetwork(kernel_data_->adapter_handle, KSF_NO_FLAGS);
        kernel_data_->adapter_handle = NULL;
    }

    /* 移除回调。 */
    if (kernel_data_->socket_call_back != NULL)
    {
        KS_removeCallBack(kernel_data_->socket_call_back);
    }

    /*关闭事件 */
    if (kernel_data_->receive_event_handle != NULL)
    {
        KS_closeEvent(kernel_data_->receive_event_handle);
    }

    /* 删除消息管道 */
    if (kernel_data_->pipe_handle != NULL)
    {
        KS_removePipe(kernel_data_->pipe_handle);
    }

    return KS_OK;
}

extern "C" __declspec(dllexport) KSError __stdcall SendBufferContent(void * /*pArgs*/, void * /*pContext*/)
{
    if (kernel_data_ == nullptr) { return KSERROR_FUNCTION_NOT_AVAILABLE; }

    if (kernel_data_->send_length != 0)
    {
        KS_getClock(&send_time_,KS_CLOCK_MEASURE_HIGHEST);

        KSError error = KS_sendSocket(kernel_data_->socket_handle, kernel_data_->send_buffer, kernel_data_->send_length, nullptr, KSF_WAIT);
        if (error != KS_OK) return error;

        const auto chr_end = "\r\n";
        error = KS_sendSocket(kernel_data_->socket_handle, chr_end , 2, nullptr, KSF_WAIT);
        if (error != KS_OK) return error;
    }

    return KS_OK;
}

KSError __stdcall SocketCallBack(void * /*pArgs*/, void *context)
{
    KS_printK("-------------- SocketCallBack");

    /* 在数据接收时,我们将数据复制到管道中,并通过事件向用户空间中的接收线程发出信号。 */
    switch (const auto *socket_context = static_cast<KSSocketContext *>(context); socket_context->ctxType)
    {
        case KS_SOCKET_CONNECTED:
        case KS_SOCKET_DISCONNECTED:
        {
            break;
        }
        case KS_SOCKET_RECV:
        {
            byte read_buffer[BUFFER_SIZE];
            int length;
            if (KS_recvSocket(kernel_data_->socket_handle, read_buffer, BUFFER_SIZE, &length, KSF_NO_FLAGS) != KS_OK) { return KS_OK; }

            if (length > 5)
            {
                int64 rec_time;
                KS_getClock(&rec_time,KS_CLOCK_MEASURE_HIGHEST);
                int64 jitter_time = rec_time - send_time_;
                if (last_jitter_time_ != 0)
                {
                    // 取绝对值
                    int64 diff_tamp = jitter_time - last_jitter_time_ > 0? jitter_time - last_jitter_time_: last_jitter_time_ - jitter_time;
                    KS_convertClock( &diff_tamp,KS_CLOCK_MEASURE_HIGHEST,KS_CLOCK_MACHINE_TIME,KSF_NO_FLAGS);
                    const int64 temp = diff_tamp;
                    KS_printK("--- jitter_time: %d",temp);
                    // 计算抖动保留最大值最小值,并进行分类
                    if (temp / US < 9) // 单位 us
                    {
                        kernel_data_->jitter_data.classes[temp / US]++;
                    }
                    else
                    {
                        kernel_data_->jitter_data.classes[9]++;
                    }

                    if (kernel_data_->jitter_data.max_value < temp)
                    {
                        kernel_data_->jitter_data.max_value = temp;
                    }

                    if (kernel_data_->jitter_data.min_value > temp)
                    {
                        kernel_data_->jitter_data.min_value = temp;
                    }
                    kernel_data_->jitter_data.count++;
                }

                last_jitter_time_ = jitter_time;

            }

            // 将数据放入管道中以将其传输到应用程序。
            KS_putPipe(kernel_data_->pipe_handle, read_buffer, length, nullptr, KSF_NO_FLAGS);

            //向应用程序的接收线程发出信号,以便从管道中获取数据。
            KS_setEvent(kernel_data_->receive_event_handle);
            break;
        }
        default:
        {
            break;
        }
    }
    return KS_OK;
}

#pragma pack(push, 8)
#include <windows.h>
#pragma pack(pop)

BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD reason, LPVOID pReserved)
{
    return TRUE;
}

应用层代码

#include "KitharaTcp.h"
#include <windows.h>
#include "Universal/Universal.h"
#include <QtConcurrent>

KitharaTcp::KitharaTcp(const QString &host, const quint16 port, QObject *parent): QObject(parent)
{
    server_address_ = host;
    server_port_ = port;
    Init();
}

KitharaTcp::~KitharaTcp()
{
    UnInit();
}

bool KitharaTcp::SendData(const QString &message) const
{
    if (user_data_ == nullptr)
    {
        return false;
    }

    strcpy_s(user_data_->send_buffer, message.toStdString().c_str());
    user_data_->send_length = (int)message.length();
    if (const KSError error = KS_execKernelFunctionEx(user_data_->kernel_handle, "SendBufferContent", KS_INVALID_HANDLE, KS_INVALID_HANDLE, KSF_NO_FLAGS); error != KS_OK)
    {
        Universal::OutputErr(error, "SendBufferContent", "Error while sending!");
        return false;
    }

    return true;
}

JitterTestData KitharaTcp::GetJitterTestDataResults() const
{
    if (user_data_ == nullptr)
    {
        return {};
    }
    const JitterTestData data = user_data_->jitter_data;
    user_data_->jitter_data.count = 0;
    return data;
}

bool KitharaTcp::Init()
{
    KSError error = KS_openDriver(customer_number_);
    if (error != KS_OK)
    {
        Universal::OutputErr(error, "KS_openDriver", "Unable to open the driver!");
        return false;
    }

    /* 创建共享内存 */
    error = KS_createSharedMemEx(&shared_mem_handle_, "", sizeof(SharedData),KSF_NO_FLAGS);
    if (error != KS_OK)
    {
        Universal::OutputErr(error, "KS_createSharedMemEx", "Unable to create the shared memory!");
        return false;
    }

    /* 获取共享内存句柄,并于用户层数据绑定 */
    error = KS_getSharedMemEx(shared_mem_handle_, reinterpret_cast<void **>(&user_data_), KSF_NO_FLAGS);
    if (error != KS_OK)
    {
        Universal::OutputErr(error, "KS_getSharedMemEx", "Unable to get the shared memory!");
        return false;
    }

    /* 加载内核层DLl */
    error = KS_loadKernel(&user_data_->kernel_handle, "KernelClient.dll", nullptr, nullptr,KSF_KERNEL_EXEC | KSF_SAVE_FPU);
    if (error != KS_OK)
    {
        Universal::OutputErr(error, "KS_loadKernel", "Unable to load the kernel!");
        return false;
    }
    return true;
}

bool KitharaTcp::Start()
{
    /* 遍历网卡 */
    // for (int i = 0;; ++i)
    // {
    //     char device_name[256];
    //     error = KS_enumDevices("NET", i, device_name, KSF_NO_FLAGS);
    //     if (error != KS_OK)
    //     {
    //         if (KSERROR_CODE(error) != KSERROR_DEVICE_NOT_FOUND)
    //         {
    //             Universal::OutputErr(error, "KS_enumDevices", "Unable to query network device name!");
    //         }
    //         if (KSERROR_CODE(error) == KSERROR_DEVICE_NOT_FOUND && !i)
    //         {
    //             Universal::OutputTxt("No network adapters found");
    //             return false;
    //         }
    //         break;
    //     }
    // }

    /* 获取网卡名称 */
    KSError error = KS_enumDevices("NET", 0, user_data_->device_name, KSF_NO_FLAGS);
    if (error != KS_OK)
    {
        Universal::OutputErr(error, "KS_enumDevices", "Unable to query network device name!");
        return false;
    }

    // 为了发送 IP 数据包,我们必须设置自己的 IP 地址和子网掩码。
    // 注意:以太网帧中的数据必须是“大端”!
    KS_makeIPv4(&user_data_->ip_config.localAddress, 192, 168, 0, 251);
    KS_makeIPv4(&user_data_->ip_config.subnetMask, 255, 255, 255, 0);
    KS_makeIPv4(&user_data_->ip_config.gatewayAddress, 192, 168, 0, 1);
    QStringList string_list = server_address_.split(".");
    byte b3{},b2{},b1{},b0{};
    for (int i = 0; i < string_list.size();i++)
    {
        if (i == 0){b3 = static_cast<byte>(string_list[i].toUInt());}
        if (i == 1){b2 = static_cast<byte>(string_list[i].toUInt());}
        if (i == 2){b1 = static_cast<byte>(string_list[i].toUInt());}
        if (i == 3){b0 = static_cast<byte>(string_list[i].toUInt());}
    }

    KS_makeIPv4(&user_data_->server_ip, b3, b2, b1, b0);
    user_data_->server_port = server_port_;

    error = KS_execKernelFunctionEx(user_data_->kernel_handle, "InitKernel", shared_mem_handle_, KS_INVALID_HANDLE, KSF_NO_FLAGS);
    if (error != KS_OK)
    {
        Universal::OutputErr(error, "KS_execKernelFunctionEx", "Unable to initialize the kernel DLL!");
        return false;
    }
    /* 创建管道数据接收线程 */
    is_run_pipe_ = true;
    future_ = QtConcurrent::run(&KitharaTcp::RecevicePipeData,this);
    return true;
}

void KitharaTcp::UnInit()
{
    is_run_pipe_ = false;
    // 等待线程退出
    future_.waitForFinished();

    KSError error;
    /* 卸载内核层DLL */
    if (shared_mem_handle_ != NULL && user_data_->kernel_handle != NULL)
    {
        if (error = KS_execKernelFunctionEx(user_data_->kernel_handle, "ExitKernel", KS_INVALID_HANDLE, KS_INVALID_HANDLE, KSF_NO_FLAGS);
            error != KS_OK)
        {
            Universal::OutputErr(error, "KS_execKernelFunctionEx", "Error while deallocating resources on kernel level!");
            return;
        }

        error = KS_freeKernel(user_data_->kernel_handle);
        if (error != KS_OK)
        {
            Universal::OutputErr(error, "KS_freeKernel", "Unable to unload the kernel!");
        }
    }

    /* 释放共享内存 */
    if (shared_mem_handle_ != NULL)
    {
        error = KS_freeSharedMemEx(shared_mem_handle_, KSF_NO_FLAGS);
        if (error != KS_OK)
        {
            Universal::OutputErr(error, "KS_freeSharedMemEx", "Unable to free the shared memory!");
        }
        else
        {
            user_data_ = nullptr;
        }
    }

    /* 关闭驱动 */
    error = KS_closeDriver();
    if (error != KS_OK)
    {
        Universal::OutputErr(error, "KS_closeDriver", "Unable to close the driver!");
    }

    Sleep(3000);
}

void KitharaTcp::RecevicePipeData()
{
    KSError error = KS_OK;
    char read_buffer[BUFFER_SIZE];
    int length;

    while (true)
    {
        if (!is_run_pipe_)
        {
            break;
        }

        error = KS_waitForEvent(user_data_->receive_event_handle, KSF_NO_FLAGS, 5 * MS);
        if (error != KS_OK) { continue; }

        while (KS_OK == KS_getPipe(user_data_->pipe_handle, read_buffer, BUFFER_SIZE, &length, KSF_NO_FLAGS))
        {
            read_buffer[length] = '\0';
            Universal::OutputTxt(read_buffer, false);
            emit SendKitharaMessage(QString(read_buffer));
        }
    }

    Universal::OutputTxt("Receiving thread has been finished.");
}

运行实例

  1. 多端连接测试
    在这里插入图片描述
    KRTS实时TCP服务器不仅兼容通用TCP客户端连接,还具备强大的多连接支持能力。通过简单的协议定义,即可轻松实现多客户端间的高效通信。这一特性极大地增强了服务器的应用灵活性和扩展性,使得KRTS成为处理复杂网络通信场景的理想选择。

  2. 稳定性测试
    在这里插入图片描述

KRTS实时TCP连接抖动测试,从测试结果可以看出,最大抖动:173.50us 最小抖动:0.00us ,详细测试结果如下:

0 - 1之间的抖动:106 占比:17.67%
1 - 2之间的抖动:105 占比:17.50%
2 - 3之间的抖动:76 占比:12.67%
3 - 4之间的抖动:51 占比:8.50%
4 - 5之间的抖动:47 占比:7.83%
5 - 6之间的抖动:20 占比:3.33%
6 - 7之间的抖动:12 占比:2.00%
7 - 8之间的抖动:13 占比:2.17%
8 - 9之间的抖动:8 占比:1.33%
10us以外的个数:162 占比:27.00%

根据KRTS实时TCP连接的抖动测试结果,可以清晰地看到在9微秒范围内的抖动占比高达73%,这表明KRTS TCP连接在传输过程中表现出了极高的稳定性和可靠性。尤其值得注意的是,抖动值在1到3微秒之间的数据包占到了整体的47.83%,这进一步证明了系统的稳健性能。总体而言,KRTS TCP连接的抖动控制表现出色,确保了数据传输的高度平滑与连续。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2071966.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【求助帖】用PyTorch搭建MLP网络时遇到奇怪的问题

求助&#xff1a;我在测试自己搭建的通用MLP网络时&#xff0c;发现它与等价的参数写死的MLP网络相比效果奇差无比&#xff0c;不知道是哪里出了问题&#xff0c;请大佬们帮忙看下。 我写的通用MLP网络&#xff1a; class MLP(nn.Module):def __init__(self, feature_num, cl…

3、Unity【基础】Resources资源场景动态加载

文章目录 一、Resources资源动态加载1、Unity中特殊文件夹1、工程路径获取2、Resources资源文件夹3、StreamingAssets流动资源文件夹4、persistentDataPath持久数据文件夹5、Plugins插件文件夹6、Editor编辑器文件夹7、默认资源文件夹StandardAssets 2、Resources同步加载1、Re…

Auto-Editor

文章目录 一、关于 Auto-Editor安装系统兼容性版权 二、切割自动切割的方法看看自动编辑器删掉了什么 三、导出到编辑器命名时间线按 Clip 分割 四、手工编辑五、更多的选择 一、关于 Auto-Editor github : https://github.com/WyattBlue/auto-editor (2.8k star – 2408)主页…

ubuntu 20.04系统安装pytorch

1.1 安装gcc 安装cuda之前&#xff0c;首先应该安装gcc&#xff0c;安装cuda需要用到gcc&#xff0c;否则报错。可以先使用下方指令在终端查看是否已经安装gcc。 gcc --version 如果终端打印如下则说明已经安装。 如果显示“找不到命令 “gcc”......”使用下方指令安装 su…

阅读笔记5:董超底层视觉之美|时空的交错与融合——论视频超分辨率

原文链接&#xff1a;https://mp.weixin.qq.com/s/pmJ56Y0-dbIlYbHbJyrfAA 1. 多帧超分和时空超分 视频超分的本质就是多帧超分&#xff0c;多帧超分的历史远早于视频超分。 在早期&#xff0c;Super Resolution专指多帧超分&#xff0c;因为只有多帧超分才能补充进入真实的信…

Golang | Leetcode Golang题解之第368题最大整除子集

题目&#xff1a; 题解&#xff1a; func largestDivisibleSubset(nums []int) (res []int) {sort.Ints(nums)// 第 1 步&#xff1a;动态规划找出最大子集的个数、最大子集中的最大整数n : len(nums)dp : make([]int, n)for i : range dp {dp[i] 1}maxSize, maxVal : 1, 1fo…

对讲模块升级的重要性-OTA空中升级与串口升级

在现代通信设备的设计中&#xff0c;灵活的升级能力已成为评估模块性能的重要标准。无论是在开发过程中&#xff0c;还是在产品的生命周期内&#xff0c;支持OTA和串口升级的模块可以极大地提高设备的可维护性和适应性。 SA618F30&#xff0c;作为一款高性价比、高集成度的大功…

SSRF 302跳转攻击redis写入ssh公钥实现远程登录

目录 SSRF漏洞 SSRF攻击Redis 302跳转 漏洞复现&#xff1a; index.html: index.php: 攻击步骤&#xff1a; 1.生成ssh公钥数据&#xff1a; 2.用SSH公钥数据伪造Redis数据&#xff1a; 3.在自己的服务器上写302跳转&#xff1a; 4.最后尝试在.ssh目录下登录&#…

Golang | Leetcode Golang题解之第371题两整数之和

题目&#xff1a; 题解&#xff1a; func getSum(a, b int) int {for b ! 0 {carry : uint(a&b) << 1a ^ bb int(carry)}return a }

MySQL主从复制之GTID模式

目录 1 MySQL 主从复制 GTID 模式介绍 2 传统复制模式与GTID复制模式的区别 3 GTID模式核心参数 4 GTID 实现自动复制原理 4.1 GTID基本概念 4.2 GTID复制流程 5 GTID 实现自动定位 5.1 配置 my.cnf 5.2 配置 SLAVE 实现自动定位 5.3 测试 6 GTID 模式 故障转移的方法流程 6.1…

如何使用ssm实现宠物领养系统+vue

TOC ssm103宠物领养系统vue 课题背景 在当今的社会&#xff0c;可以说是信息技术的发展时代&#xff0c;在社会的方方面面无不涉及到各种信息的处理。信息是人们对客观世界的具体描述&#xff0c;是人们进行交流与联系的重要途径。人类社会就处在一个对信息进行有效合理的加…

mysql数据库----简单认识库的操作

目录 1.区分概念 2.什么是数据库 3.数据库的创建和销毁 4.数据库编码初识 5.查询系统默认编码配置 6.两个查询编码表的指令 7.创建指定编码的数据库 8.不同编码的区别 第一个编码方式&#xff1a; 第二个编码方式&#xff1a; 查询结果说明&#xff1a; 9.数据库的增…

QT Quick QML 网络助手——TCP客户端

GitHub 源码: QmlLearningPro &#xff0c;选择子工程 Nettools.pro QML 其它文章请点击这里: QT QUICK QML 学习笔记 ● 运行效果&#xff1a; 左侧为常用的网络调试工具&#xff0c;右侧为本项目 UI 效果&#xff0c;前端使用 QML &#xff0c;后端使用C &#xff…

ArkTs之:数据懒加载——LazyForEach的用法

官方描述 LazyForEach从提供的数据源中按需迭代数据&#xff0c;并在每次迭代过程中创建相应的组件。当在滚动容器中使用了LazyForEach&#xff0c;框架会根据滚动容器可视区域按需创建组件&#xff0c;当组件滑出可视区域外时&#xff0c;框架会进行组件销毁回收以降低内存占…

我在某日重新下载了idea

# 1 Maven设置 2 字体样式,字体颜色 3 插件 1,fitten code和通义灵码 2,one dark theme主题 3,mybatisX 4,Rainbow Brackets 5,Key Promoter X 设置 自动导入包

Ps:首选项 - 常规

Ps菜单&#xff1a;编辑/首选项 Edit/Preferences 快捷键&#xff1a;Ctrl K Photoshop 首选项中的“常规” General选项卡主要用于调整 Photoshop 的整体工作行为和用户体验。这些设置让用户可以根据个人习惯和工作流程定制软件的响应方式和界面布局&#xff0c;从而提高工作…

下载的word中的mathtype公式双击无法打开编辑器

原因分析&#xff1a; 该word中的此公式不是通过word内置的mathtype插入公式的&#xff0c;而是从mathtype编辑器中复制粘贴到word中的。 后者的方式当被其他人下载接收后&#xff0c;无法修改此公式&#xff0c;而且该公式也不能被其他人复制&#xff0c;会报错如下&#xff…

GPT-4o System Card is released

GPT-4o System Card is released, including red teaming, frontier risk evaluations, and other key practices for industrial-strength Large Language Models. https://openai.com/index/gpt-4o-system-card/ 报告链接 企业级生成式人工智能LLM大模型技术、算法及案例实战…

5G毫米波测试助力突破高频段设备局限,实现高效外场测试

作者介绍 一、方案背景 随着业务对带宽需求的不断增加&#xff0c;通信频谱不断向更高频谱延伸&#xff0c;5G毫米波具有丰富的频率资源&#xff0c;是移动通信技术演进的必然方向。下图是ITU的WRC-19会议发布的目前5G所占用频段。 从图中可以看出&#xff0c;在5G毫米波测试中…

AgentQ,超越人类的人工智能代理

MultiOn 宣布推出一款新代理AgentQ&#xff0c;这是一款令人惊叹的产品&#xff0c;它整合了我最近一直在讨论的大部分内容&#xff1a;将 LLM 与搜索相结合。 但这个经纪人很特殊。 与其他代理不同的是&#xff0c;由于巧妙地使用了对齐技术&#xff0c;该代理可以从好的和坏的…