为了熟悉qt’中的tcp使用方法特意写了个,简单的聊天系统。消息传输利用json进行传输,客户端能显示当前在线的人。
效果图如下
客户端效果图
服务端
源代码
服务端的源代码
//这是server.h
#ifndef SERVER_H_
#define SERVER_H_
#include<QObject>
#include<QTcpServer>
#include<QTcpSocket>
#include<QHash>
struct User
{
User() = default;
User(QTcpSocket* sock) :sock(sock) {}
QString username;
QTcpSocket* sock;
};
class Server :public QObject
{
Q_OBJECT
public:
Server(QObject * parent=nullptr);
~Server();
void init();
public slots:
void onNewConnection();
void onClientReadyRead();
private:
QTcpServer* _tcpServer;
QHash<QTcpSocket*, User*> _hashMaps;
};
#endif // !SERVER_H_
//这是server.cpp
#include "server.h"
#include <QDebug>
#include "../common/SJsonData.h"
Server::Server(QObject* parent)
:QObject(parent),_tcpServer(new QTcpServer(this))
{
init();
}
Server::~Server()
{
for (auto user : _hashMaps)
{
user->sock->close();
}
_tcpServer->close();
}
void Server::init()
{
if (!_tcpServer->listen(QHostAddress::Any, 8888)){
qWarning() << "listen failed" << _tcpServer->errorString();
return;
}
connect(_tcpServer, &QTcpServer::newConnection, this, &Server::onNewConnection);
}
void Server::onNewConnection()
{
if (_tcpServer->hasPendingConnections()) {
auto sock = _tcpServer->nextPendingConnection();
connect(sock, &QTcpSocket::readyRead, this, &Server::onClientReadyRead);
_hashMaps.insert(sock, new User(sock));
//sock->write("hello client");
}
}
//onNewConnection
void Server::onClientReadyRead()
{
auto sock=dynamic_cast<QTcpSocket*>(sender()); //知道是谁发过来的
if (!sock)
return;
auto data = sock->readAll();
qInfo() << "server:\t" << data;
SJsonData jd = SJsonData::fromJson(data);
if (jd.stringValue("type") == "login") {
auto username = jd.stringValue("username");
_hashMaps.value(sock)->username = username;
if(_hashMaps.size()>1){
//这是发给当前登录的用户
jd.clear();
jd.addValue("type", "onLineUsers");
QStringList list;
for (auto user : _hashMaps) {
if (user->sock != sock)
list.append(user->username);
}
jd.addValue("users", list);
sock->write(jd.toJson());
//把当前登录的用户发给所有已经登录的用户
SJsonData online;
online.addValue("type", "userOnline");
online.addValue("username", username);
auto onlineData = online.toJson();
for (auto user : _hashMaps) {
if (user->sock != sock) {
user->sock->write(onlineData);
}
list.append(user->username);
}
}
}
else if (jd.stringValue("type")=="chatMsg")
{
//转发给所有人
for (auto user : _hashMaps)
{
if (user->sock != sock) {
user->sock->write(jd.toJson());
}
}
}
qInfo() << "user counts:"<<_hashMaps.size();
for (auto user : _hashMaps)
{
qInfo() << user->username;
}
}
客户端代码
tcp连接进行封装
//这是 STcpConnection.h
#ifndef STCPCONNECTION_H_
#define STCPCONNECTION_H_
#include<QObject>
#include<QTcpSocket>
#include<QList>
#define BIND_MEMBER_FUN(fun) std::bind(fun, this, std::placeholders::_1)
#define BIND_FUN(fun) std::bind(fun,std::placeholders::_1)
class STcpConnection :public QObject
{
Q_OBJECT
public:
using ListenFun = std::function<void(QTcpSocket*)>;
public:
STcpConnection(QObject *parent=nullptr);
~STcpConnection();
qint32 addListener(ListenFun fun);
QTcpSocket* tcpSocket() { return _tcpSocket; }
operator QTcpSocket* () { return _tcpSocket; }
static STcpConnection* mainCon();
public :
void onReadyread();
private:
QTcpSocket* _tcpSocket;
QList<ListenFun> _listener;
};
#endif // !STCPCONNECTION_H_
//这是 STcpConnection.cpp
#include "STcpConnection.h"
STcpConnection::STcpConnection(QObject* parent)
:QObject(parent),_tcpSocket(new QTcpSocket(this))
{
connect(_tcpSocket, &QTcpSocket::readyRead, this, &STcpConnection::onReadyread);
}
STcpConnection::~STcpConnection()
{
}
qint32 STcpConnection::addListener(ListenFun fun)
{
_listener.append(fun);
return _listener.size()-1;
}
STcpConnection* STcpConnection::mainCon()
{
static STcpConnection* con = nullptr;
if (!con) {
con = new STcpConnection;
//将localHost 更改为服务器地址
con->_tcpSocket->connectToHost(QHostAddress::LocalHost, 8888);
connect(*con, &QTcpSocket::connected, []() {
qInfo() << "connect server success";
});
connect(*con, &QTcpSocket::disconnected, []() {
qInfo() << "disconnect server success";
});
connect(*con, &QTcpSocket::errorOccurred, []() {
qInfo() << "has error" << static_cast<QTcpSocket*>(*con)->errorString();
});
}
return con;
}
void STcpConnection::onReadyread()
{
while (_tcpSocket->bytesAvailable())
{
for (auto& fun : _listener) {
fun(_tcpSocket);
}
}
}
关键代码已经贴出,整个项目的源码在文中附加资源 。