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连接的抖动控制表现出色,确保了数据传输的高度平滑与连续。