The encapsulation of tcpserver4--client and the use of network library in QT step-by-step construction

Source: Internet
Author: User
Tags emit int size mutex sessions

To undertake the previous chapter: QT Step by step to build tcpserver3--shutdown and start

After the security startup and shutdown of the server side in the previous chapter, I sorted out the code and suddenly found that the content was not allocated properly, so this chapter has a little more to say:

encapsulation clientmanager-> netapi Static Library project-> uses the NETAPI library in other projects. 1. Create Clientmanager:

Clientmanager is actually similar to the server, maintaining a session thread pool itself, and the client is responsible for the creation of a conversation with sessions. After the upper level to get the session, and then encapsulation or use, the advantage is that the client can connect multiple terminals, but also more convenient for the upper design:

Header file:

#ifndef clientmanager_h
#define CLIENTMANAGER_H
#include "SessionThreads.h"
//Client Manager
Class Clientmanager
{public
:
    clientmanager () = default;
    ~clientmanager ();

    Boot, default start of a thread, non-thread safe
    bool Start (uint32_t threadnum = 1);
    Turn off non-thread safe
    void Stop ();
    Gets the number of sessions
    std::vector<uint32_t> getsessionsize () const;
    Create a client
    std::shared_ptr<tcpsession> createclient ();

Private:
    //thread session pool
    sessionthreads Sessionthreads_;
    is running
    bool Isrunning_ = false;
#endif//Clientmanager_h

From the beginning of the file can be seen, and the server compared to the same formula, familiar taste. The only difference is that the client is responsible for creating the client, and the upper tier needs an disconnected client to connect to the server. and the server needs to start a listener thread.
CPP:

#include "ClientManager.h" Clientmanager::~clientmanager () {} bool Clientmanager::start (uint32_t threadnum) {if (I
    Srunning_) return false;
    Verify if (threadnum = = 0) Threadnum = std::thread::hardware_concurrency (); Start This->sessionthreads_.
    Start (threadnum);
    Isrunning_ = true;
return true; } void Clientmanager::stop () {if (!
    Isrunning_) return; Stops the session thread pool This->sessionthreads_.
    Stop ();
Isrunning_ = false; } std::vector<uint32_t> clientmanager::getsessionsize () const {return This->sessionthreads_.
Getsessionsize (); } std::shared_ptr<tcpsession> clientmanager::createclient () {Tcpthread *thread = This->SessionThreads_.
    Pickminthread ();
    std::shared_ptr<tcpsession> session = std::make_shared<tcpsession> (thread); This->sessionthreads_.
    Addsession (session);
    Session->movetothread (thread);
    if (thread) Thread->addone ();
return session;
 }

Here the code is relatively simple, need to explain the following is, I will tcpsession and tcpthread and so on reorganization adjusted. But the whole does not affect, so directly in the final code in the annex to the embodiment of good, no need to carry out alone, are small changes. 2, packaging into a static library: 2.1, the new NETAPI Static Library project:


and add the previously written files to the project (figure). 2.2. Namespaces:

Since all packaged into Lib library, it may be better for our netapi to take a namespace, so it is more convenient to use, more modular.
To create a new header file in the NETAPI project:
NetApiNameSpace.h:

#ifndef netapinamespace_h
#define NETAPINAMESPACE_H
//namespace
#ifndef netapi_namespace_begin
#define Netapi_namespace_begin  NAMESPACE netapi {
#define Netapi_namespace_end}
#endif

#endif// Netapinamespace_h

Here are two macro definitions, so you can use macros directly in your class.
Like what:
Tcpthread:

#ifndef tcpthread_h
#define TCPTHREAD_H
#include <QThread>
#include <atomic>
#include " NetApiNameSpace.h "

Netapi_namespace_begin
class Tcpthread:public qthread
{
    q_object public
:
    Tcpthread ();
    ~tcpthread ();

    virtual void run () override;

    void Subone ();
    void AddOne ();
    Number of sessions
    std::atomic_uint32_t sessioncount = 0;
};
Netapi_namespace_end
#endif//Tcpthread_h
2.3, Apifactory:

Create an interface class that is responsible for creating Clientmanager and TCPServer:
NetApiFactory.h:

#ifndef netapi_h
#define NETAPI_H
#include "ClientManager.h"
#include "TcpServer.h"

netapi_ Namespace_begin
class netapifactory
{public
:
    static Clientmanager *createclientmanager ();
    Static TCPServer *createserver ();
Netapi_namespace_end
#endif//Netapi_h

Cpp:

#include "NetApiFactory.h"

netapi_namespace_begin

clientmanager *netapifactory::createclientmanager ()
{return
    new Clientmanager ();
}

TCPServer *netapifactory::createserver ()
{return
    new TCPServer ();
}

Netapi_namespace_end

When used outside, direct call is OK. 2.4, compile:


See the Lib file in the figure is complete. 3, the use of Netapi:

Well, now that Netapi is done, how do we use it?
Server End: 3.1. Add Library:

First, add the Netapi lib file to the project:
3.2, the encapsulation of a sessioninfo and sessioninfolist:

Sessioninfo:

#ifndef sessioninfo_h
#define SESSIONINFO_H
#include <QObject>
#include "NetApiFactory.h"

Class Sessioninfo:public QObject
{
    q_object
signals:
    void Signalread (sessioninfo*, const Qbytearray int);
    void Signaldisconnect ();
    void signalconnected ();

Public:
    sessioninfo (std::shared_ptr<netapi::tcpsession> &session);
    ~sessioninfo ();


    Connect to service
    -side void Connect (const QString &host, quint16 port);
    Disconnect
    void Disconnect ();
    Send data default encryption
    void Write (const char*buffer, int size);

Public:
    //Disconnect callback
    std::function<void (void*) > ondisconnected = nullptr;

Private slots:
    void Slotread (const qbytearray &data, int size);
    void slotdisconnected ();

Private:
    std::shared_ptr<netapi::tcpsession> session_;

#endif//Sessioninfo_h

Cpp:

#include "SessionInfo.h" Sessioninfo::sessioninfo (std::shared_ptr<netapi::tcpsession> &session) {this-& Gt
    Session_ = session; Connect (This->session_.get (), &netapi::tcpsession::d isconnected, this, &ses
    sioninfo::slotdisconnected); Connect (This->session_.get (), &netapi::tcpsession::signalread, this, &sessi
    Oninfo::slotread); Connect (This->session_.get (), &netapi::tcpsession::connected, this, &sessio
ninfo::signalconnected);
    } sessioninfo::~sessioninfo () {if (!this->session_) return;
               Disconnect (This->session_.get (), &netapi::tcpsession::d isconnected, this,
    &sessioninfo::slotdisconnected);
               Disconnect (This->session_.get (), &netapi::tcpsession::signalread, this,
    &sessioninfo::slotread); DisConnect (This->session_.get (), &netapi::tcpsession::connected, this, &sessio
    ninfo::signalconnected);
This->session_ = nullptr; } void Sessioninfo::connect (const QString &host, quint16 port) {if (this->session_) this->session_-
>connecttoserver (host, Port);

} void Sessioninfo::D isconnect () {if (this->session_) This->session_->disconnect ();} void Sessioninfo::write (const char *buffer, int size) {if (this->session_) this->session_->write (buff
er, size);

} void Sessioninfo::slotread (const qbytearray &data, int size) {Emit this->signalread (this, data, size);
    void sessioninfo::slotdisconnected () {emit this->signaldisconnect ();
if (this->ondisconnected) this->ondisconnected (this);

 }

Briefly explain the effect of this sessioninfo: The bottom is accept a connection (TCPServer) or create a client (Clientmanager), the tcpsession will be sent up. Then we get the upper level of this session, of course, a variety of binding settings properties, then it is better to choose to encapsulate a layer of sessioninfo (you can also directly hold it, do not package), so that after the heartbeat packet Ah, delay AH, data forwarding Ah, etc. can be done at the sesioninfo level.

With Sessioninfo, it might be a good idea to have a list that specializes in managing this sessioninfo:
Sessioninfolist:

#ifndef sessioninfolist_h
#define SESSIONINFOLIST_H
#include <mutex>
#include <memory>
#include <vector>
#include "SessionInfo.h"

class sessioninfolist
{public
:
    Sessioninfolist ();
    ~sessioninfolist ();

    sessioninfo* Newsessioninfo (std::shared_ptr<netapi::tcpsession> &session);
    void Clear ();
    sessioninfo* FindByID (const void *id);

Private:
    void sessiondisconnected (void *id);
Private:
    Std::mutex lock_;
    Std::vector<sessioninfo*> list_;
};

#endif//Sessioninfolist_h

Cpp:

#include "SessionInfoList.h" Sessioninfolist::sessioninfolist () {} sessioninfolist::~sessioninfolist () {this->
Clear (); } sessioninfo *sessioninfolist::newsessioninfo (std::shared_ptr<netapi::tcpsession> &session) {Std::lock_
    guard<std::mutex> Lock (This->lock_);
    Sessioninfo *info = new Sessioninfo (session);
    This->list_.push_back (info);
                                     info->ondisconnected = Std::bind (&sessioninfolist::sessiondisconnected, this,
    std::p laceholders::_1);
return info;
    } void Sessioninfolist::clear () {std::lock_guard<std::mutex> lock (lock_);
    for (sessioninfo* info:this->list_) {if (info) Delete info;
} this->list_.clear ();
     } sessioninfo *sessioninfolist::findbyid (const void *id) {if (!id) return nullptr;
     std::lock_guard<std::mutex> Lock (This->lock_); for (size_t i = 0; i < This->lisT_.size ();
         ++i) {Sessioninfo *info = this->list_[i];
     if (info = = ID) return info;
return nullptr;
    } void sessioninfolist::sessiondisconnected (void *id) {sessioninfo *info = nullptr;
    std::lock_guard<std::mutex> Lock (This->lock_);
    Removes the session from the list std::vector<sessioninfo*>::iterator it;
        for (it = This->list_.begin (); it!= this->list_.end (); ++it) {info = *it;
            if (info = = id) {it = this->list_.erase (it);
            info->ondisconnected = nullptr;
            Delete info;
        Return
 }
    }
}

Sessioninfolist is responsible for managing Sessioninfo and deleting the connection when he disconnects. In short, the server side holds this sessioninfolist, after all, we can not let the upper level to manage too many things, even in the formal project, Sessioninfo received data, may be handed to the business manager, Let the business manager choose which specific business command to execute. So be sure to be flexible, of course, that's not the thing to say now.

In addition, this sessioninfolist and session can be placed separately in a shared directory where the server and client side can be shared. 3.3, add interface, use in the main interface:

In fact, should be Feng Yi layer, but I just demo, do not bother to dry, directly to the business logic and Interface logic mixed:

#include "MainWindow.h" #include "ui_mainwindow.h" Mainwindow::mainwindow (Qwidget *parent): Qmainwindow (parent),
    UI (New Ui::mainwindow) {ui->setupui (this);
    Server_ = Netapi::netapifactory::createserver ();
server_->onaccepted = Std::bind (&mainwindow::acceptsession, this, std::p laceholders::_1);
        } Mainwindow::~mainwindow () {if (Server_) {server_->stop ();
    Delete Server_;
    } server_ = nullptr;
Delete UI; } void Mainwindow::acceptsession (Std::shared_ptr<netapi::tcpsession> &tcpsession) {SessionInfo *info = thi S->sessionlist_.
    Newsessioninfo (tcpsession);
    Connect (info, &sessioninfo::signaldisconnect, this, &mainwindow::slotdisconnected);
    Connect (info, &sessioninfo::signalread, this, &mainwindow::slotread);
This->writelog ("Accept One");

} void Mainwindow::on_btn_clear_clicked () {this->ui->log->clear ();} void mainwindow::on_btn_stop_clicked () {if (!this->server_->isstart ()) return;
    This->server_->stop ();
This->writelog ("Server stop!");
    } void mainwindow::on_btn_start_clicked () {int port = This->ui->spinbox->value ();
    Netapi::serverdata Serverdata; Serverdata.
    Port = port;
    if (Server_->start (serverdata)) This->writelog ("Start success!");
else This->writelog ("Could not start server");

} void Mainwindow::slotdisconnected () {This->writelog ("Disconnected");}
    void Mainwindow::slotread (Sessioninfo *info, const qbytearray &data, int size) {QString msg = data;
    This->writelog (msg);
Info->write (Data.tostdstring (). C_STR (), size);
 } void Mainwindow::writelog (const QString &msg) {this->ui->log->appendplaintext (msg);}

This is just a simple process, what the server side receives, and what it throws back to the client.
Client: 3.4. Add Library:

With server end 3.5, add Sessioninfo:

With server end 3.6, used in the main interface:

#include "MainWindow.h" #include "ui_mainwindow.h" Mainwindow::mainwindow (Qwidget *parent): Qmainwindow (parent),
    UI (New Ui::mainwindow) {ui->setupui (this);
    Clientmanager_ = Netapi::netapifactory::createclientmanager ();
    Clientmanager_->start (1);
    This->sessioninfo_ = new Sessioninfo (Clientmanager_->createclient ());
    Connect (Sessioninfo_, &sessioninfo::signalconnected, this, &mainwindow::slotconnected);
    Connect (Sessioninfo_, &sessioninfo::signalread, this, &mainwindow::slotread);
Connect (Sessioninfo_, &sessioninfo::signaldisconnect, this, &mainwindow::slotdisconnected); } Mainwindow::~mainwindow () {if (this->sessioninfo_) {disconnect (Sessioninfo_, &sessioninfo::sign
        Alconnected, this, &mainwindow::slotconnected);
        Connect (Sessioninfo_, &sessioninfo::signalread, this, &mainwindow::slotread); Connect (Sessioninfo_, &sessioninfo::signaldisconnect, this, &mainwindow::slotdisconnected);
        Delete this->sessioninfo_;
    This->sessioninfo_ = nullptr;
        } if (This->clientmanager_) {this->clientmanager_->stop ();
        Delete this->clientmanager_;
    This->clientmanager_ = nullptr;
} Delete UI;
    } void mainwindow::on_btn_connect_clicked () {int port = This->ui->spin_port->value ();
    QString IP = this->ui->line_ip->text (). trimmed ();
if (this->sessioninfo_) This->sessioninfo_->connect (IP, (quint16) port);
    } void Mainwindow::on_btn_send_clicked () {QString msg = This->ui->line_msg->text (). trimmed ();
if (this->sessioninfo_) This->sessioninfo_->write (Msg.tostdstring (). C_STR (), msg.length ());

} void Mainwindow::slotconnected () {this->writelog ("Connected success!");}

void mainwindow::slotdisconnected () {This->writelog ("Disconnect");} void Mainwindow::slotread (Sessioninfo *info, const Qbytearray &Amp;data, int size) {QString msg = data;
This->writelog (msg);

} void Mainwindow::writelog (const QString &msg) {this->ui->log->appendplaintext (msg);}
 void mainwindow::on_btn_clear_clicked () {this->ui->log->clear ();}
3.7, sample diagram:

4, demo code and conclusion:

Code Address:

Demo
Attention:
because it is the display of ideas, so this netapi I spend a little time, not repeated inspection and testing, so just demo run pass. There is no guarantee that NETAPI has a potential bug. In addition, this library is just closer to being able to use the project directly, but will need to fill in some details if you want to use it formally.

Any questions, questions or suggestions, you can leave a message or contact qq:1281581259, thank you.

This project to here, has been considered a relatively complete demo, a more detailed display of each from the socket encapsulation, to the thread pool, network library encapsulation. Although it is still relatively simple, and because QT does a lot of things, so the code is not much. If you can see here, I am very grateful, this is a small test for me, hope to bring help.

at the same time this demo also has a lot of deficiencies: such as the data received and receive functions do not do a unified packaging; for example, because of the mechanism of the signal slot, do some around the transfer of work; for example, the code does not review optimization, such as without a lot of rigorous testing ... Too much, because time is limited, and too much detail of things can not one by one show, there is no balance.

But how to say, still can explain a certain mentality and knowledge, after to continue, may be some perfect work: such as data encapsulation, serialization, data filtering, business distribution. Let's see if I have anything on hand.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.