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

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


目录


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开发应用层界面

核心代码

内核层代码:

cpp 复制代码
/*
 *     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;
}

应用层代码:

cpp 复制代码
#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开发应用层界面

核心代码

内核层代码:

cpp 复制代码
/*
 *     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;
}

应用层代码

cpp 复制代码
#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/https的简单认识
网络·http·https
Q8343158197 分钟前
JL6107S 7端口管理型MAC(1*RGMII+1*SGMII)千兆以太网交换机芯片P2P替代RTL8367S
网络·嵌入式硬件·网络协议·硬件工程·信息与通信·p2p
golove66620 分钟前
基于Prometheus和Grafana的现代服务器监控体系构建
服务器·grafana·prometheus
七夜zippoe25 分钟前
基于Prometheus和Grafana的现代服务器监控体系构建
服务器·grafana·prometheus
alsknv34 分钟前
代码签名证书有什么作用?
网络协议·http·https·php·ssl
安科瑞刘鸿鹏1 小时前
高低压配电系统中电弧光的危害有多大?
运维·网络·能源
巭犇1 小时前
Linux编译内核选项说明
linux·运维·服务器
小橞1 小时前
Linux之ansible简但应用(如何搭建yum,创建逻辑卷)
linux·运维·服务器·ansible
robin59111 小时前
CentOS 7下CX5-RDMA网络测试
linux·网络·centos·云计算
梦虬孙~1 小时前
HTTPS原理详解
网络协议·http·https