Qt编程:sokit工具中clientskt实现

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 实现可靠的面向连接的通信

  • 提供有序、无差错的数据传输

实现细节

  1. 连接管理:

    • open() 建立TCP连接并连接相关信号槽

    • close() 关闭socket并断开信号槽

  2. 数据处理:

    • newData() 处理接收到的数据,确保完整读取

    • send() 确保数据完整发送

  3. 状态处理:

    • asynConn() 处理连接成功事件

    • closed() 处理连接关闭事件

    • error() 处理错误事件

UDP 实现 (ClientSktUdp)

特性

  • 基于 QUdpSocket 实现无连接的通信

  • 提供不可靠但高效的数据传输

实现细节

  1. 连接管理:

    • open() 建立UDP连接并连接相关信号槽

    • close() 关闭socket并断开信号槽

  2. 数据处理:

    • newData() 处理接收到的数据报

    • send() 发送数据报

  3. 状态处理:

    • asynConn() 处理连接成功事件

    • closed() 处理连接关闭事件

    • error() 处理错误事件

共同特点

  1. 数据统计:

    • 记录发送和接收的字节数

    • 通过信号通知统计信息

  2. 调试支持:

    • 提供二进制数据转储功能

    • 支持消息显示

  3. 错误处理:

    • 统一的错误信息管理

    • 自动断开连接并通知

  4. 缓冲区管理:

    • 使用 TK 工具类管理缓冲区

    • 限制最大缓冲区大小(1MB)

使用场景

这个类适合需要同时支持TCP和UDP协议的客户端应用,例如:

  • 网络调试工具

  • 需要同时支持可靠和不可靠传输的应用

  • 需要监控网络流量的应用

  • 需要调试网络数据的应用

扩展性

可以通过继承 ClientSkt 类来实现其他协议的支持,只需实现纯虚函数 open()close()  和 send() 即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值