ClientSkt
是一个基于 Qt 的网络通信基类,提供了 TCP 和 UDP 两种网络协议的客户端实现。
#ifndef __CLIENTSKT_H__
#define __CLIENTSKT_H__
#include <QTcpSocket>
#include <QUdpSocket>
class ClientSkt : public QObject
{
Q_OBJECT
public:
ClientSkt(QObject *parent=0);
virtual ~ClientSkt();
virtual QString name() const { return "General"; };
bool plug(const QHostAddress& ip, quint16 port);
void unplug();
void send(const QString& data);
const QHostAddress& addr() const { return m_ip; };
quint16 port() const { return m_port; };
signals:
void unpluged();
void message(const QString& msg);
void dumpbin(const QString& title, const char* data, quint32 len);
void countRecv(qint32 bytes);
void countSend(qint32 bytes);
protected:
void dump(const char* buf, qint32 len, bool isSend);
void show(const QString& msg);
void setError(const QString& err);
void recordRecv(qint32 bytes);
void recordSend(qint32 bytes);
virtual bool open() =0;
virtual void close() =0;
virtual void send(const QByteArray& data) =0;
private:
QHostAddress m_ip;
quint16 m_port;
QString m_error;
};
class ClientSktTcp : public ClientSkt
{
Q_OBJECT
public:
ClientSktTcp(QObject *parent=0);
~ClientSktTcp();
virtual QString name() const { return "TCP"; };
protected:
virtual bool open();
virtual void close();
virtual void send(const QByteArray& bin);
private slots:
void asynConn();
void newData();
void closed();
void error();
private:
QTcpSocket m_socket;
};
class ClientSktUdp : public ClientSkt
{
Q_OBJECT
public:
ClientSktUdp(QObject *parent=0);
~ClientSktUdp();
virtual QString name() const { return "UDP"; };
protected:
virtual bool open();
virtual void close();
virtual void send(const QByteArray& bin);
private slots:
void asynConn();
void newData();
void closed();
void error();
private:
QUdpSocket m_socket;
};
#endif // __CLIENTSKT_H__
#include <QTcpSocket>
#include "toolkit.h"
#include "clientskt.h"
#define MAXBUFFER 1024*1024
ClientSkt::ClientSkt(QObject *parent)
: QObject(parent),m_port(0)
{
}
ClientSkt::~ClientSkt()
{
}
bool ClientSkt::plug(const QHostAddress& ip, quint16 port)
{
m_ip = ip;
m_port = port;
m_error.clear();
return open();
}
void ClientSkt::unplug()
{
close();
emit unpluged();
}
void ClientSkt::setError(const QString& err)
{
m_error = err;
}
void ClientSkt::recordRecv(qint32 bytes)
{
emit countRecv(bytes);
}
void ClientSkt::recordSend(qint32 bytes)
{
emit countSend(bytes);
}
void ClientSkt::send(const QString& data)
{
QString err;
QByteArray bin;
if (!TK::ascii2bin(data, bin, err))
{
show("bad data format to send: "+err);
return;
}
send(bin);
}
void ClientSkt::dump(const char* buf, qint32 len, bool isSend)
{
emit dumpbin(QString("DAT %1").arg(isSend?"<---":"--->"), buf, (quint32)len);
}
void ClientSkt::show(const QString& msg)
{
emit message(msg);
}
ClientSktTcp::ClientSktTcp(QObject *parent)
:ClientSkt(parent)
{
}
ClientSktTcp::~ClientSktTcp()
{
}
bool ClientSktTcp::open()
{
connect(&m_socket, SIGNAL(readyRead()), this, SLOT(newData()));
connect(&m_socket, SIGNAL(disconnected()), this, SLOT(closed()));
connect(&m_socket, SIGNAL(connected()), this, SLOT(asynConn()));
connect(&m_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(error()));
m_socket.connectToHost(addr(), port());
return true;
}
void ClientSktTcp::close()
{
m_socket.close();
m_socket.disconnect(this);
}
void ClientSktTcp::error()
{
QTcpSocket* s = qobject_cast<QTcpSocket*>(sender());
show(QString("TCP socket error %1, %2").arg(s->error()).arg(s->errorString()));
unplug();
}
void ClientSktTcp::asynConn()
{
show(QString("TCP connection to %1:%2 opened!")
.arg(addr().toString()).arg(port()));
}
void ClientSktTcp::closed()
{
show(QString("TCP connection closed!"));
}
void ClientSktTcp::newData()
{
QTcpSocket* s = qobject_cast<QTcpSocket*>(sender());
if (!s) return;
qint64 bufLen = s->bytesAvailable();
char* buf = TK::createBuffer(bufLen, MAXBUFFER);
if (!buf) return;
qint64 readLen = 0;
qint64 ioLen = s->read(buf, bufLen);
while (ioLen > 0)
{
readLen += ioLen;
ioLen = s->read(buf+readLen, bufLen-readLen);
}
if (ioLen >= 0)
{
recordRecv(readLen);
dump(buf, readLen, false);
}
TK::releaseBuffer(buf);
}
void ClientSktTcp::send(const QByteArray& bin)
{
const char * src = bin.constData();
qint64 srcLen = bin.length();
qint64 writeLen = 0;
qint64 ioLen = m_socket.write(src, srcLen);
while (ioLen > 0)
{
writeLen += ioLen;
ioLen = m_socket.write(src+writeLen, srcLen-writeLen);
}
if (writeLen != srcLen)
{
show(QString("failed to send data to %1:%2 [%3]")
.arg(addr().toString()).arg(port()).arg(writeLen));
return;
}
recordSend(writeLen);
dump(src, srcLen, true);
}
ClientSktUdp::ClientSktUdp(QObject *parent)
:ClientSkt(parent)
{
}
ClientSktUdp::~ClientSktUdp()
{
}
void ClientSktUdp::asynConn()
{
show(QString("UDP channel to %1:%2 opened!")
.arg(addr().toString()).arg(port()));
}
void ClientSktUdp::closed()
{
show(QString("UDP channel closed!"));
}
void ClientSktUdp::close()
{
m_socket.close();
m_socket.disconnect(this);
}
void ClientSktUdp::error()
{
QUdpSocket* s = qobject_cast<QUdpSocket*>(sender());
show(QString("UDP socket error %1, %2").arg(s->error()).arg(s->errorString()));
unplug();
}
bool ClientSktUdp::open()
{
connect(&m_socket, SIGNAL(readyRead()), this, SLOT(newData()));
connect(&m_socket, SIGNAL(disconnected()), this, SLOT(closed()));
connect(&m_socket, SIGNAL(connected()), this, SLOT(asynConn()));
connect(&m_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(error()));
m_socket.connectToHost(addr(), port());
return true;
}
void ClientSktUdp::newData()
{
QUdpSocket* s = qobject_cast<QUdpSocket*>(sender());
if (!s) return;
qint64 bufLen = s->bytesAvailable();
char* buf = TK::createBuffer(bufLen, MAXBUFFER);
if (!buf) return;
qint64 readLen = 0;
qint64 ioLen = s->read(buf, bufLen);
while (ioLen > 0)
{
readLen += ioLen;
ioLen = s->read(buf+readLen, bufLen-readLen);
}
if (ioLen >= 0)
{
recordRecv(readLen);
dump(buf, readLen, false);
}
TK::releaseBuffer(buf);
}
void ClientSktUdp::send(const QByteArray& bin)
{
const char * src = bin.constData();
qint64 srcLen = bin.length();
qint64 writeLen = 0;
qint64 ioLen = m_socket.write(src, srcLen);
while (ioLen > 0)
{
writeLen += ioLen;
ioLen = (writeLen >= srcLen) ? 0 :
m_socket.write(src+writeLen, srcLen-writeLen);
}
if (writeLen != srcLen)
{
show(QString("failed to send data to %1:%2 [%3]")
.arg(addr().toString()).arg(port()).arg(writeLen));
return;
}
recordSend(writeLen);
dump(src, srcLen, true);
}
基类 ClientSkt
主要功能
-
提供网络通信的通用接口和基础功能
-
作为 TCP 和 UDP 实现的抽象基类
-
管理连接状态和错误处理
-
提供数据发送和接收的统计功能
核心方法
-
plug()
: 连接到指定IP和端口 -
unplug()
: 断开连接 -
send()
: 发送数据(字符串或二进制) -
dump()
: 记录数据转储(用于调试) -
show()
: 显示消息 -
setError()
: 设置错误信息
保护成员
-
m_ip
: 存储目标IP地址 -
m_port
: 存储目标端口 -
m_error
: 存储错误信息
纯虚函数
-
open()
: 打开连接 -
close()
: 关闭连接 -
send(const QByteArray&)
: 发送二进制数据
信号
-
unpluged()
: 连接断开信号 -
message()
: 消息通知信号 -
dumpbin()
: 二进制数据转储信号 -
countRecv/countSend
: 接收/发送字节数统计信号
TCP 实现 (ClientSktTcp)
特性
-
基于
QTcpSocket
实现可靠的面向连接的通信 -
提供有序、无差错的数据传输
实现细节
-
连接管理:
-
open()
建立TCP连接并连接相关信号槽 -
close()
关闭socket并断开信号槽
-
-
数据处理:
-
newData()
处理接收到的数据,确保完整读取 -
send()
确保数据完整发送
-
-
状态处理:
-
asynConn()
处理连接成功事件 -
closed()
处理连接关闭事件 -
error()
处理错误事件
-
UDP 实现 (ClientSktUdp)
特性
-
基于
QUdpSocket
实现无连接的通信 -
提供不可靠但高效的数据传输
实现细节
-
连接管理:
-
open()
建立UDP连接并连接相关信号槽 -
close()
关闭socket并断开信号槽
-
-
数据处理:
-
newData()
处理接收到的数据报 -
send()
发送数据报
-
-
状态处理:
-
asynConn()
处理连接成功事件 -
closed()
处理连接关闭事件 -
error()
处理错误事件
-
共同特点
数据统计:
记录发送和接收的字节数
通过信号通知统计信息
调试支持:
提供二进制数据转储功能
支持消息显示
错误处理:
统一的错误信息管理
自动断开连接并通知
缓冲区管理:
使用
TK
工具类管理缓冲区限制最大缓冲区大小(1MB)
使用场景
这个类适合需要同时支持TCP和UDP协议的客户端应用,例如:
-
网络调试工具
-
需要同时支持可靠和不可靠传输的应用
-
需要监控网络流量的应用
-
需要调试网络数据的应用
扩展性
可以通过继承 ClientSkt
类来实现其他协议的支持,只需实现纯虚函数 open()
、close()
和 send()
即可。