//dialog.h class Dialog : public QDialog { private slots: // void onSokConnected(); void onSokDisconnected(); // readyRead , ( ) void onSokReadyRead(); void onSokDisplayError(QAbstractSocket::SocketError socketError); private: QTcpSocket *_sok; // quint16 _blockSize;// QString _name;// };
In general, when working with sockets, you need to look at the data as a set of bytes, otherwise there may be problems with displaying information only partially (it was not a complete message, and the following is displayed with a piece of the previous one). To avoid these troubles, we will use data streams (QDataStream) and transfer blocks between sockets in which the first 2 bytes are the size of the current block, the 3rd byte is the client’s command to the server (or server response), and the rest is data depending on the command. It is worth saying that the tcp protocol guarantees the delivery of all packets, so you can safely wait for the full block size before processing it. //dialog.cpp Dialog::Dialog(QWidget *parent) :QDialog(parent),ui(new Ui::Dialog) { // _sok = new QTcpSocket(this); // connect(_sok, SIGNAL(readyRead()), this, SLOT(onSokReadyRead())); connect(_sok, SIGNAL(connected()), this, SLOT(onSokConnected())); connect(_sok, SIGNAL(disconnected()), this, SLOT(onSokDisconnected())); connect(_sok, SIGNAL(error(QAbstractSocket::SocketError)),this, SLOT(onSokDisplayError(QAbstractSocket::SocketError))); } // , , connectToHost() void, , onSokDisplayError void Dialog::on_pbConnect_clicked() { _sok->connectToHost(ui->leHost->text(), ui->sbPort->value()); } void Dialog::onSokConnected() { // QByteArray block; QDataStream out(&block, QIODevice::WriteOnly); // 2 . MyClient , - // - out << (quint16)0 << (quint8)MyClient::comAutchReq << ui->leName->text(); _name = ui->leName->text(); // out.device()->seek(0); // out << (quint16)(block.size() - sizeof(quint16)); _sok->write(block); } void Dialog::onSokReadyRead() { // QDataStream in(_sok); // 2 if (_blockSize == 0) { // 2 2 if (_sok->bytesAvailable() < (int)sizeof(quint16)) return; // (2 ) in >> _blockSize; } // if (_sok->bytesAvailable() < _blockSize) return; else // _blockSize = 0; //3 - quint8 command; in >> command; switch (command) { // , , MyClient::comUsersOnline case MyClient::comUsersOnline: { QString users; in >> users; if (users == "") return; // , ( QStringList) QStringList l = users.split(","); // ui->lwUsers->addItems(l); } break; // case MyClient::comPublicServerMessage: { // QString message; in >> message; AddToLog("[PublicServerMessage]: "+message, Qt::red); } ... } }
This is all with the client - the main points are described, the rest is pretty simple. //dialog.cpp Dialog::Dialog(QWidget *parent) :QDialog(parent), ui(new Ui::Dialog) { // . - parent, - , myclient _serv = new MyServer(this, this); // connect(this, SIGNAL(messageFromGui(QString,QStringList)), _serv, SLOT(onMessageFromGui(QString,QStringList))); ... // 127.0.0.1:1234 if (_serv->doStartServer(QHostAddress::LocalHost, 1234)) {...} else {...} }
//myserver.h class MyServer : public QTcpServer { public: bool doStartServer(QHostAddress addr, qint16 port); void doSendToAllUserJoin(QString name); // void doSendToAllUserLeft(QString name); void doSendToAllMessage(QString message, QString fromUsername); // void doSendToAllServerMessage(QString message);// void doSendServerMessageToUsers(QString message, const QStringList &users); // void doSendMessageToUsers(QString message, const QStringList &users, QString fromUsername); QStringList getUsersOnline() const; // bool isNameValid(QString name) const; // bool isNameUsed(QString name) const; // protected: void incomingConnection(int handle); private: QList<MyClient *> _clients; // QWidget *_widget; // myclient }; //myserver.cpp void MyServer::incomingConnection(int handle) { // , ( ), - parent MyClient *client = new MyClient(handle, this, this); // , if (_widget != 0) { connect(client, SIGNAL(addUserToGui(QString)), _widget, SLOT(onAddUserToGui(QString))); connect(client, SIGNAL(removeUserFromGui(QString)), _widget, SLOT(onRemoveUserFromGui(QString))); ... } _clients.append(client); } /* , _clients, , */ void MyServer::doSendServerMessageToUsers(QString message, const QStringList &users) { // QByteArray block; QDataStream out(&block, QIODevice::WriteOnly); out << (quint16)0 << MyClient::comPrivateServerMessage << message; out.device()->seek(0); out << (quint16)(block.size() - sizeof(quint16)); // ( , users ) for (int j = 0; j < _clients.length(); ++j) if (users.contains(_clients.at(j)->getName())) _clients.at(j)->_sok->write(block); }
//myclient.h class MyClient : public QObject { // MyServer _sok friend class MyServer; public: static const QString constNameUnknown; static const quint8 comAutchReq = 1; static const quint8 comUsersOnline = 2; ... static const quint8 comErrNameUsed = 202; void setName(QString name) {_name = name;} QString getName() const {return _name;} bool getAutched() const {return _isAutched;} void doSendCommand(quint8 comm) const; void doSendUsersOnline() const; signals: // void addUserToGui(QString name); void removeUserFromGui(QString name); void messageToGui(QString message, QString from, const QStringList &users); // QList void removeUser(MyClient *client); // private slots: void onConnect(); void onDisconnect(); void onReadyRead(); void onError(QAbstractSocket::SocketError socketError) const; private: QTcpSocket *_sok; // MyServer *_serv; // quint16 _blockSize; // QString _name; // bool _isAutched; // };
Source: https://habr.com/ru/post/131585/
All Articles