KRTS网络模块:TCP服务端、客户端实例
目录
- KRTS网络模块:TCP服务端、客户端实例
- TCP简介
- KRST服务端简介
- 核心特性
- 界面设计
- 核心代码
- KRTS客户端简介
- 核心特性
- 界面设置
- 核心代码
- 运行实例
Socket模块基于Packet模块,实时提供更高的协议,如RAW-IP、TCP 和 UDP(参见 以太网)。相关API的使用,请查阅 SocketAPI。
TCP简介
TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。TCP的主要功能包括:
-
连接管理:
建立连接:三次握手(three-way handshake)来建立一个TCP连接。
终止连接:四次挥手(four-way handshake)来终止一个TCP连接。 -
数据传输:
可靠传输:通过序列号、确认应答、重传机制等确保数据可靠传输。
流量控制:通过滑动窗口机制防止发送方发送速率过快导致接收方来不及处理。
拥塞控制:通过拥塞窗口大小动态调整来避免网络拥塞。 -
错误检测:
使用校验和来检测数据包中的错误。 -
排序:
确保数据包按发送顺序到达接收端。 -
多路复用:
支持在一个TCP连接上进行多个数据流的传输,通过端口号区分不同的应用进程。
TCP是Internet中最主要的协议之一,它位于TCP/IP协议栈的传输层,位于IP协议之上,为应用层提供服务。TCP协议确保了数据在网络上传输时的可靠性,这对于许多需要高可靠性的应用来说至关重要,例如Web浏览器、电子邮件、文件传输等。
在实际应用中,TCP通常与IP协议结合使用,共同构成了TCP/IP协议族的核心部分。TCP/IP协议族是互联网的基础,它定义了数据如何在网络中传输和寻址。
KRST服务端简介
KRTS服务端作为一个支持TCP协议的服务端程序,其主要功能包括支持IP地址和端口配置、实时数据处理以及能够接受来自通用TCP客户端和特定KRTS客户端的连接请求。下面是KRTS服务端的一些核心特性和实现细节:
核心特性
- IP 和端口配置:
- 支持指定服务端监听的IP地址和端口号。
- 支持绑定到特定的网络接口。
- 实时数据处理:
- 实现高效的数据处理逻辑,确保低延迟和高吞吐量。
- 客户端连接管理:
- 支持同时处理多个客户端连接。
- 能够识别并处理来自不同类型的客户端(通用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客户端的一些核心特性和实现细节:
核心特性
- IP 和端口配置:
- 支持指定连接IP地址和端口号。
- 支持绑定到特定的网络接口。
- 实时数据处理:
- 实现高效的数据处理逻辑,确保低延迟和高吞吐量。
界面设置
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.");
}
运行实例
-
多端连接测试
KRTS实时TCP服务器不仅兼容通用TCP客户端连接,还具备强大的多连接支持能力。通过简单的协议定义,即可轻松实现多客户端间的高效通信。这一特性极大地增强了服务器的应用灵活性和扩展性,使得KRTS成为处理复杂网络通信场景的理想选择。 -
稳定性测试
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连接的抖动控制表现出色,确保了数据传输的高度平滑与连续。