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

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.rhkb.cn/news/411208.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

国科大 矩阵论2023秋季 叶世伟老师 考试试卷

叶老师的考试很难&#xff0c;图源一位胆大的勇者~ 希望能帮助大家&#xff01;

AD7606芯片驱动-FPGA实现

简介 AD7606是一款16位ADC芯片&#xff0c;可实现8通道并行采集&#xff0c;每通道最大速度可达1M&#xff0c;可实现多种模式数据采集。 介绍 本次FPGA使用的是8通道串行采样模式&#xff0c;设计中所用到的AD7606引脚说明如下&#xff1a; 名称定义CONVST同步采集转换开始信…

Mysql语句性能优化

SQL查询过程 查询缓存&#xff1a; 执行查询语句的时候&#xff0c;会先查询缓存&#xff08;MySQL 8.0 版本后移除&#xff0c;因为这个功能不太实用&#xff09;。分析器&#xff1a; 没有命中缓存的话&#xff0c;SQL 语句就会经过分析器&#xff0c;分析器说白了就是要先看…

算法之二分查找法和双指针

用二分查找法刷leetcode算法题目的时候&#xff0c;经常遇到视频看着理解很透彻&#xff0c;当上手写时一看就会&#xff0c;一写就废。二分查找法涉及边界条件很多&#xff0c;逻辑很简单&#xff0c;就是写不好。何时写 while(left<right)&#xff0c;while(left<right…

通过写文件方式写入 Hive 数据

通过写文件方式写入 Hive 数据 Hive最简单的写入数据方式就是通过Hive Jdbc写入Hive数据&#xff0c;但这并不是写入Hive最高效的方法。 Hive通过读取相关Hdfs的文件来获取数据信息&#xff0c;而通过直接写入Hdfs文件数据达到写入Hive数据的效果&#xff0c;这是目前最高效的…

nerfstudio半离线配置踩坑记录

安装torch2.1.2 with cuda11.8 由于清华镜像源&#xff08;包括阿里源和豆瓣源&#xff09;都没有torch2.1.2cu118的包&#xff0c;因此只能从pytorch官网下载。 服务器上直接通过下面pip的方式安装会由于网络原因中断&#xff0c;无奈只能在本地先把torch的包下载下来再上传到…

SAP与生产制造MPM系统集成案例

一、需求介绍 某公司为保证企业内部生产管理系统的多项基础数据的同步更新&#xff0c;确保各模块间信息的一致性和准确性&#xff0c;对后续的生产计划和物料管理打下基础&#xff0c;该公司将MPM系统和SAP系统经过SAP PO中间件集成平台进行了集成。MPM全称为Manufacturing…

blender--二维平面图标变为三维网格

有时候我们希望把一些二维图片能变成三维网格&#xff0c;本案例我们就针对这一场景进行实现。 首先我们可以先去找一张需要的图片(注意&#xff0c;本例需要图片是svg格式)&#xff0c;我们可以在阿里巴巴矢量图标库等平台进行搜索&#xff0c;如图所示&#xff0c;找到需要的…

diffusion model(扩散模型)DDPM解析

DDPM 前向阶段 重复 2-5 步骤 x 0 ∼ q ( x 0 ) \mathbf{x}_0\sim q(\mathbf{x}_0) x0​∼q(x0​)从数据集中采样一张图片 t ∼ U n i f o r m ( { 1 , … , T } ) t\sim\mathrm{Uniform}(\{1,\ldots,T\}) t∼Uniform({1,…,T})&#xff0c;从 1~T 中随机挑选一个时间步 t ϵ …

三种tcp并发服务器实现程序

都需先进行tcp连接 1、多进程并发 2、多线程并发 3、IO多路复用并发 &#xff08;1&#xff09;select &#xff08;2&#xff09;epoll

SAP ERP与长城汽车EDI业务集成案例(SAP CPI平台)

一、项目背景 某智能座舱公司是国内领先的智能座舱领域科技公司&#xff0c;致力于成为智能网联行业变革的领导者和推动者&#xff0c;聚焦整车域控制器产品、智能网联软件产品和运营服务产品&#xff1b; 已建成首条先进的数智化域控制器生产线&#xff0c;为客户提供最优…

大刀阔斧改革之后,阅文距离“东方迪士尼”更近了吗?

当前&#xff0c;网文IP的确是“富矿”。中国社会科学院文学研究所发布的《2023中国网络文学发展研究报告》显示&#xff0c;截至2023年底&#xff0c;网络文学IP市场规模2605亿元&#xff0c;同比增长近百亿元。 近日&#xff0c;网文产业中的头部企业阅文集团也披露数据称&a…

Android U WMShell动画调用堆栈

本文主要简单介绍WMShell动画调用堆栈 代码环境&#xff1a;repo init -u https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/platform/manifest -b android-14.0.0_r7 Systemserver侧 TAG: at com.android.server.wm.Transition.onTransactionReady(Transition.java:1575) TA…

爆改YOLOv8|利用分层特征融合策略MSBlock改进yolov8,暴力涨点

1&#xff0c;本文介绍 MSBlock 是一种分层特征融合策略&#xff0c;用于改进卷积神经网络中的特征融合效果。它通过分层次地融合不同尺度的特征图来提高网络的表达能力和性能。MSBlock 采用多尺度特征融合的方法&#xff0c;确保网络能够有效地捕捉不同层次和尺度的信息&…

Neo4j导入csv数据,并创建节点

Neo4j 是一种图数据库&#xff0c;特别适合管理和分析复杂的关系数据。 数据来源&#xff1a;http://openkg.cn/ 导入到 Neo4j 的合适场景&#xff1a; 需要在物种分类中查找层级关系&#xff08;如物种的科、属等&#xff09;。 需要进行关系查询和图结构的分析。 想在分类树…

【Axure高保真原型】输入框控制多选下拉列表选项

今天和大家分享输入框控制多选下拉列表选项选项的原型模板&#xff0c;效果包括&#xff1a; 点击下拉框可以弹出选项列表&#xff0c;点击可以切换选中或取消选中 根据选中项在外框出自动生成标签&#xff0c;可以自适应调整高度 下拉列表的选项由左侧多行输入框里的内容控制…

数据结构—— 再探二叉树

1. TOP-K问题 TOP-K问题&#xff1a;求数据结合中前K个最大或者最小的数据 比如&#xff1a;专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等 思路&#xff1a; 1. 用数据集合中前K个数据来建堆&#xff1a; …

WEB服务器-Nginx源码安装及相关配置

一、web服务的常用种类 Apache HTTP Server 简介&#xff1a;Apache是一款广泛使用的Web服务器软件&#xff0c;支持多种操作系统&#xff0c;包括Linux。​​​​​​​特点&#xff1a; 支持多个虚拟主机。 模块化架构&#xff0c;可以根据需要加载不同的模块。 强大的安全…

多态(虚构的整体,具体的个体)(多态的基本概念/多态的原理剖析/纯虚函数和抽象类/虚析构和纯虚析构)

多态的基本概念 #define _CRT_SECURE_NO_WARNINGS #include<iostream> using namespace std; // 多态的基本概念 // 多态分为静态多态和动态多态 // 静态多态&#xff1a; 函数重载还运算符重载属于静态多态&#xff0c;服用函数名 // 动态多态&#xff1a; 派生派和虚函…

VUE使用websocket

在之前搭建好的项目的基础上新版security demo&#xff08;二&#xff09;前端-CSDN博客 目录 一、代码改造 1、后端改造 2、VUE使用websocket 3、测试 二、按用户推送 1、完整代码如下 1.1、前端 1.2、后端&#xff1a; 2、测试 一、代码改造 1、后端改造 &#x…