POCO庫中文程式設計參考指南(10)如何使用TCPServer框架?

鍾超發表於2012-04-21

POCO庫中文程式設計參考指南(10)如何使用TCPServer框架?

  • Author: 柳大·Poechant(鍾超)
  • Email: zhongchao.ustc@gmail.com
  • Blog:Blog.CSDN.net/Poechant
  • Date: April 21th, 2012

1 TCPServer 框架概述

POCO 庫提供TCPServer框架,用以搭建自定義的 TCP 伺服器。TCPServer維護一個連線佇列、一個連線執行緒池。連線執行緒用於處理連線,連線執行緒只要一空閒就不斷地從連線佇列中取連線並進行處理。一旦連線執行緒從連線佇列中取到一個連線,就會建立一個TCPServerConnection連線物件,並且呼叫該物件的start()方法,直到start()方法返回,這個連線物件就被刪除了。

連線執行緒的數量是動態的,其取決於連線佇列中排隊的連線數。當然,你使用的時候可以設定連線佇列的最大容量,以防止在高併發應用的伺服器上出現連線太多而使連線佇列溢位的悲劇發生。當連線佇列滿了,卻還有新的連線到來時,新來的連線就會被立即悄無聲息地關閉。

現在我們總結一下,就是要有一個可執行的 TCP 服務應用程式(命名為PoechantTCPServer),還有很多 TCP 連線(命名為PoechantTCPConnection)。而這裡我們用到工廠模式(準確說是TCPServerConnectionFactory要我們用的),所以還有一個PoechantTCPConnectionFactory

2 光說不練假把式

2.1 建立一個 PoechantTCPServer

或許你還不熟悉 POCO 中的 Application,沒關係,這不影響本文的敘述。下面先建立一個 ServerApplication 如下:

// PoechantTCPServer.h

#ifndef POECHANT_TCP_SERVER
#define POECHANT_TCP_SERVER

#include "Poco/Util/ServerApplication.h"
#include "Poco/Util/Application.h"

using Poco::Util::ServerApplication;
using Poco::Util::Application;

class PoechantTCPServer: public ServerApplication
{
public:
    PoechantTCPServer() {}
    ~PoechantTCPServer() {}
protected:
    void initialize(Application& self);
    void uninitialize();
    int main(const std::vector<std::string>& args)
};

#endif

這樣在呼叫啟動PoechantTCPServer時,會先呼叫initialize,然後呼叫main,在main結束後會呼叫uninitialize。其實現很簡單:

// PoechantTCPServer.cpp

#include "PoechantTCPServer.h"

void PoechantTCPServer::initialize(Application& self)
{
    ServerApplication::loadConfiguration();
    ServerApplication::initialize(self);
}

void PoechantTCPServer::uninitialize()
{
    ServerApplication::uninitialize();
}

int PoechantTCPServer::main(const std::vector<std::string>& args)
{
    // 這個我們最後說

    return Application::EXIT_OK;
}

2.2 PoechantTCPConnection

連線類的定義很簡單,建構函式要傳入一個 StreamSocket 和其他你需要的引數。

// PoechantTCPConnection.h

#ifndef POECHANT_TCP_CONNECTION_H
#define POECHANT_TCP_CONNECTION_H

#include "Poco/Net/TCPServerConnection.h"
#include "Poco/Net/StreamSocket.h"
#include <string>

class PoechantTCPConnection: public TCPServerConnection
{
public:
    PoechantTCPConnection(const StreamSocket& s,
        const std::string& arg1,
        int arg2,
        double arg3);

    void run();
private:

    std::string _arg1;
    int _arg2;
    double _arg3;
};

#endif

實現如下:

// PoechantTCPConnection.cpp

#include "PoechantTCPConnection.h"
#include "Poco/Util/Application"
#include "Poco/Timestamp.h"
#include "Poco/Exception.h"
#include "Poco/DateTimeFormatter.h"

PoechantTCPConnection(const StreamSocket& s, const std::string& arg1, int arg2, double arg3):
    TCPServerConnection(s), _arg1(arg1), _arg2(arg2), _arg3(arg3)
{
}
void run()
{
    Application& app = Application::instance();
    // 日誌輸出連線的TCP使用者的地址(IP和埠)
    app.logger().information("Request from " + this->socket().peerAddress().toString());
    try
    {
        // 向客戶端傳送資料,這裡以傳送一個表示時間的字串為例
        Timestamp now;
        std::string dt(DateTimeFormatter::format(now, _format));
        dt.append("\r\n");
        socket().sendBytes(dt.data(), (int) dt.length());
    }
    catch (Poco::Exception& e)
    {
        app.logger().log(e);
    }
}

2.3 PoechantTCPConnectionFactory

工廠模式不必多說,名字唬人,其實非常非常簡單(準確的說設計模式大部分名字都唬人,但大部分都很有用,設計模式本身並不牛B,能把設計模式抽象提煉出來成我們現在認為很簡單的這些模式的那幾個人很牛B)。具體如下:

// PoechantTCPConnectionFactory.h

#ifndef POECHANT_TCP_CONNECTION_FACTORY_H
#define POECHANT_TCP_CONNECTION_FACTORY_H

#include "Poco/Net/TCPServerConnectionFactory.h"
#include "Poco/Net/TCPServerConnection.h"
#include "Poco/Net/StreamSocket.h"
#include <string>

class PoechantTCPConnectionFactory: public TCPServerConnectionFactory
{
    public:
    PoechantTCPConnectionFactory(const std::string arg1, int arg2, double arg3)
        : _arg1(arg1), _arg2(arg2), _arg3(arg3)
    {
    }

    TCPServerConnection* createConnection(const StreamSocket& socket)
    {
        return new PoechantTCPConnection(socket, arg1, arg2, arg3);
    }

private:
    std::string arg1;
    int arg2;
    double arg3;
};

#endif

2.4 啟動

回頭來說PoechantTCPServer::main(const std::vector<std::string>& args),其過程就是建立一個繫結了地址的ServerSocket,把它傳給TCPServer,當然別忘了把工程物件也給你的TCPServer傳一個。最後就start()waitForTerminationRequeststop()就行了。

int PoechantTCPServer::main(const std::vector<std::string>& args)
{
    unsigned short port = (unsigned short) config().getInt("PoechantTCPServer.port", 12346);
    std::string format(config().getString("PoechantTCPServer.format",
        DateTimeFormat::ISO8601_FORMAT));

    // 1. Bind a ServerSocket with an address
    ServerSocket serverSocket(port);

    // 2. Pass the ServerSocket to a TCPServer
    TCPServer server(new PoechantTCPConnectionFactory(format), serverSocket);

    // 3. Start the TCPServer
    server.start();

    // 4. Wait for termination
    waitForTerminationRequest();

    // 5. Stop the TCPServer
    server.stop();

    return Application::EXIT_OK;
}

然後寫一個程式入口:

#include "PoechantTCPServer.h"

int main(int argc, char **argv)
{
    return PoechantTCPServer().run(argc, argv);
}

3 寫一個 Client 測測

TCPServer 要用 TCP 的客戶端來測試。在 POCO 中有豐富的 Socket,其中 TCP 方式的 Socket 有:

  • Poco::Net::ServerSocket
  • Poco::Net::StreamSocket
  • Poco::Net::DialogSocket
  • Poco::Net::SecureServerSocket
  • Poco::Net::SecureStreamSocket

UDP 方式的 Socket 有:

  • Poco::Net::DatagramSocket
  • Poco::Net::MulticastSocket

一個 TCP 方式 Client 如下(這裡用了 while 迴圈,其實可以在收到資料後就關閉的)

#include <iostream>
#include "Poco/Net/StreamSocket.h"
#include "Poco/Net/SocketAddress.h"

#define BUFFER_SIZE 1024

using Poco::Net::SocketAddress;
using Poco::Net::StreamSocket;

int main (int argc, const char * argv[])
{
    SocketAddress address("127.0.0.1", 12346);
    StreamSocket socket(address);
    char buffer[BUFFER_SIZE];
    while (true)
    {
        if (socket.available())
        {
            int len = socket.receiveBytes(buffer, BUFFER_SIZE);
            buffer[len] = '\0';
            std::cout << "" << buffer << std::endl;
        }
    }
    return 0;
}

-

轉載請著名來自柳大的CSDN部落格:Blog.CSDN.net/Poechant

-

相關文章