本来是个Qt项目,按理说使用QTcpSocket岂不是更好,但是,QTcpSocket在多线程方式下使用时,各种报错或警告,原因是非多线程安全,如果改造成多线程安全,各种信号槽,又不怎么优雅。所以,基于ZLMediaKit写了一个,代码,贴在此处,大家帮忙参谋一下,存在哪些问题?
TcpClient.h
#pragma once
#include <QString>
#include <QObject>
#include <atomic>
#include <mutex>
#include <condition_variable>
extern "C" {
#include "mk_mediakit.h"
}
struct tcp_client_user_data
{
int count = 0;
std::condition_variable conncectCv;
std::mutex connectMtx;
bool connected = false;
std::condition_variable cv;
std::mutex bufMtx;
QByteArray buffer;
void Reset()
{
count = 0;
{
std::lock_guard locker(connectMtx);
connected = false;
}
{
std::lock_guard locker(bufMtx);
buffer.clear();
}
}
};
class TcpClient : public QObject
{
Q_OBJECT
public:
TcpClient(bool bAsync = true);
~TcpClient();
bool Connect(QString ip, std::uint16_t port);
void DisConnect();
QByteArray Request(const QByteArray& msg);
private:
mk_tcp_client m_client;
tcp_client_user_data* m_userData;
bool m_bAsync;
};
TcpClient.cpp
#include "TcpClient.h"
#include <chrono>
#define LOG_LEV 4
//修改此宏,可以选择协议类型
#define TCP_TYPE mk_type_tcp
/**
* tcp客户端连接服务器成功或失败回调
* @param client tcp客户端
* @param code 0为连接成功,否则为失败原因
* @param msg 连接失败错误提示
*/
void API_CALL on_mk_tcp_client_connect(mk_tcp_client client, int code, const char* msg)
{
log_printf(LOG_LEV, "connect result:%d %s", code, msg);
tcp_client_user_data* user_data = (tcp_client_user_data*)mk_tcp_client_get_user_data(client);
if (code == 0)
{
//连接上后我们发送一个hello world测试数据
//mk_tcp_client_send(client, "hello world", 0);
std::lock_guard locker(user_data->connectMtx);
user_data->connected = true;
user_data->conncectCv.notify_all();
}
else
{
mk_tcp_client_release(client);
}
}
/**
* tcp客户端与tcp服务器之间断开回调
* 一般是eof事件导致
* @param client tcp客户端
* @param code 错误代码
* @param msg 错误提示
*/
void API_CALL on_mk_tcp_client_disconnect(mk_tcp_client client, int code, const char* msg)
{
log_printf(LOG_LEV, "disconnect:%d %s", code, msg);
tcp_client_user_data* user_data = (tcp_client_user_data*)mk_tcp_client_get_user_data(client);
mk_tcp_client_release(client);
std::lock_guard locker(user_data->connectMtx);
user_data->connected = false;
}
/**
* 收到tcp服务器发来的数据
* @param client tcp客户端
* @param data 数据指针
* @param len 数据长度
*/
void API_CALL on_mk_tcp_client_data(mk_tcp_client client, mk_buffer buffer)
{
log_printf(LOG_LEV, "data[%d]:%s", mk_buffer_get_size(buffer), mk_buffer_get_data(buffer));
size_t incomingSize = mk_buffer_get_size(buffer);
tcp_client_user_data* user_data = (tcp_client_user_data*)mk_tcp_client_get_user_data(client);
{
std::lock_guard locker(user_data->bufMtx);
user_data->buffer.append(mk_buffer_get_data(buffer), incomingSize);
user_data->cv.notify_all();
}
}
/**
* 每隔2秒的定时器,用于管理超时等任务
* @param client tcp客户端
*/
void API_CALL on_mk_tcp_client_manager(mk_tcp_client client)
{
tcp_client_user_data* user_data = (tcp_client_user_data*)mk_tcp_client_get_user_data(client);
//printf("on_mk_tcp_client_manager:%d\n", user_data->count++);
}
TcpClient::TcpClient(bool bAsync):
m_client(nullptr),
m_bAsync(bAsync)
{
m_userData = new tcp_client_user_data();
}
TcpClient::~TcpClient()
{
bool conncected = false;
{
std::lock_guard locker(m_userData->connectMtx);
conncected = m_userData->connected;
}
if (conncected)
{
DisConnect();
}
}
bool TcpClient::Connect(QString ip, std::uint16_t port)
{
char* ini_path = mk_util_get_exe_dir("c_api.ini");
char* ssl_path = mk_util_get_exe_dir("ssl.p12");
mk_config config;
{
config.ini = ini_path;
config.ini_is_path = 1;
config.log_level = 0;
config.log_mask = LOG_CONSOLE;
config.ssl = ssl_path;
config.ssl_is_path = 1;
config.ssl_pwd = NULL;
config.thread_num = 0;
};
mk_env_init(&config);
free(ini_path);
free(ssl_path);
mk_tcp_client_events events_clent;
{
events_clent.on_mk_tcp_client_connect = on_mk_tcp_client_connect;
events_clent.on_mk_tcp_client_data = on_mk_tcp_client_data;
events_clent.on_mk_tcp_client_disconnect = on_mk_tcp_client_disconnect;
events_clent.on_mk_tcp_client_manager = on_mk_tcp_client_manager;
};
m_client = mk_tcp_client_create(&events_clent, TCP_TYPE);
m_userData->Reset();
mk_tcp_client_set_user_data(m_client, m_userData);
std::unique_lock locker(m_userData->connectMtx);
mk_tcp_client_connect(m_client, ip.toStdString().c_str(), port, 3);
m_userData->conncectCv.wait_for(locker, std::chrono::milliseconds(3000));
return m_userData->connected;
}
void TcpClient::DisConnect()
{
mk_tcp_client_release(m_client);
std::lock_guard locker(m_userData->connectMtx);
m_userData->connected = false;
}
QByteArray TcpClient::Request(const QByteArray& msg)
{
std::unique_lock locker(m_userData->bufMtx);
if (m_bAsync) m_userData->buffer.clear();
mk_tcp_client_send(m_client, msg.data(), msg.size());
m_userData->cv.wait_for(locker, std::chrono::milliseconds(30000));
return std::move(m_userData->buffer);
}
main.cpp
#include <iostream>
#include <TcpClient.h>
#include <QDebug>
int main(int argc, char *argv[])
{
TcpClient client;
bool ret = client.Connect("127.0.0.1", 60000);
std::cout << ret << std::endl;
printf("enter any key to exit\n");
std::string input;
while (std::cin >> input)
{
if (input == "exit") break;
else
{
qDebug() << client.Request(input.c_str());
}
}
return 0;
}