2011-01-18 8 views
21

मैं लाइब्रेरी को बूस्ट में बदलने की प्रक्रिया में हूं। एसीओ (जिसने अब तक बहुत अच्छा काम किया है), लेकिन मैंने डिज़ाइन निर्णय के संबंध में एक ठोकर खाई मार दी है।Boost.Asio में एक साथ एसएसएल सॉकेट और गैर-एसएसएल सॉकेट का उपयोग करना?

Boost.Asio SSL के लिए समर्थन प्रदान करता है, लेकिन boost::asio::ssl::stream<boost::asio::ip::tcp::socket> प्रकार सॉकेट के लिए उपयोग किया जाना चाहिए। मेरे पुस्तकालय एसएसएल सर्वर से कनेक्ट या सामान्य रूप से जोड़ने का विकल्प है, तो मैं दो सॉकेट इस तरह के साथ एक वर्ग बनाया है:

class client : public boost::enable_shared_from_this<client> 
{ 
public: 
    client(boost::asio::io_service & io_service, boost::asio::ssl::context & context) : socket_(io_service), secureSocket_(io_service, context) {} 
private: 
    boost::asio::ip::tcp::socket socket_; 
    boost::asio::ssl::stream<boost::asio::ip::tcp::socket> secureSocket_; 
}; 

और उस संदर्भ socket_ संचालकों का एक समूह देखते हैं के भीतर। (उदाहरण के लिए, मेरे पास कई स्थानों पर socket_.is_open() है, जो अन्य सॉकेट के लिए secureSocket_.lowest_layer().is_open() बनने की आवश्यकता होगी।)

क्या कोई इस बारे में जाने का सबसे अच्छा तरीका सुझा सकता है? मैं इस उद्देश्य के लिए सिर्फ एक अलग वर्ग नहीं बनाना चाहता, क्योंकि इसका मतलब बहुत सारे कोड को डुप्लिकेट करना होगा।

संपादित करें: मैंने अपना मूल प्रश्न दोहराया क्योंकि मैंने ओपनएसएसएल फ़ंक्शन के उद्देश्य को गलत समझा।

उत्तर

14

कुछ तरीके हैं जो आप कर सकते हैं। अतीत में, मैं की तरह

if (sslEnabled) 
    boost::asio::async_write(secureSocket_); 
} else { 
    boost::asio::async_write(secureSocket_.lowest_layer()); 
} 

कुछ कौन सा if/else बयान का एक बहुत साथ बहुत जल्दी से गन्दा प्राप्त कर सकते हैं किया है।

class Socket 
{ 
    public: 
     virtual void connect(...); 
     virtual void accept(...); 
     virtual void async_write(...); 
     virtual void async_read(...); 
    private: 
     boost::asio::ip::tcp::socket socket_; 
}; 

फिर एक secureSocket_ बजाय socket_ पर संचालित करने के लिए एक व्युत्पन्न वर्ग SecureSocket बना सकते हैं - तुम भी एक अमूर्त वर्ग (oversimplified छद्म कोड) बना सकते हैं। मुझे नहीं लगता कि यह बहुत सारे कोड को डुप्लिकेट करेगा, और जब भी आपको async_read या async_write की आवश्यकता हो तो यह शायद if/else से साफ़ हो।

+0

पहले दृष्टिकोण मैं क्या कर समाप्त हो गया है, लेकिन मैं सार वर्ग विचार की तरह है। मैं इसे देख लूंगा - धन्यवाद। – DSB

17

मुझे इस सवाल का जवाब देने में देर हो चुकी है, लेकिन मुझे आशा है कि इससे दूसरों की मदद मिलेगी। सैम के जवाब में एक विचार के रोगाणु शामिल हैं, लेकिन मेरी राय में काफी दूर नहीं जाते हैं।

विचार इस अवलोकन से आया कि एएसओ एक स्ट्रीम में एक एसएसएल सॉकेट लपेटता है। यह सब समाधान यह है कि यह गैर-एसएसएल सॉकेट को समान रूप से लपेटता है।

एसएसएल और गैर-एसएसएल सॉकेट के बीच एक समान बाहरी इंटरफेस रखने का वांछित परिणाम तीन वर्गों के साथ किया जाता है। एक, आधार प्रभावी रूप से इंटरफेस को परिभाषित करता है:

class Socket { 
public: 
    virtual boost::asio::ip::tcp::socket &getSocketForAsio() = 0; 

    static Socket* create(boost::asio::io_service& iIoService, boost::asio::ssl::context *ipSslContext) { 
     // Obviously this has to be in a separate source file since it makes reference to subclasses 
     if (ipSslContext == nullptr) { 
      return new NonSslSocket(iIoService); 
     } 
     return new SslSocket(iIoService, *ipSslContext); 
    } 

    size_t _read(void *ipData, size_t iLength) { 
     return boost::asio::read(getSocketForAsio(), boost::asio::buffer(ipData, iLength)); 
    } 
    size_t _write(const void *ipData, size_t iLength) { 
     return boost::asio::write(getSocketForAsio(), boost::asio::buffer(ipData, iLength)); 
    } 
}; 

दो उप-वर्ग एसएसएल और गैर-एसएसएल सॉकेट लपेटें।

typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> SslSocket_t; 
class SslSocket: public Socket, private SslSocket_t { 
public: 
    SslSocket(boost::asio::io_service& iIoService, boost::asio::ssl::context &iSslContext) : 
     SslSocket_t(iIoService, iSslContext) { 
    } 

private: 
    boost::asio::ip::tcp::socket &getSocketForAsio() { 
     return next_layer(); 
    } 
}; 

और

class NonSslSocket: public Socket, private Socket_t { 
public: 
    NonSslSocket(boost::asio::io_service& iIoService) : 
      Socket_t(iIoService) { 
    } 

private: 
    boost::asio::ip::tcp::socket &getSocketForAsio() { 
     return next_layer(); 
    } 
}; 

हर बार जब आप फोन एक asio फ़ंक्शन का उपयोग getSocketForAsio(), बजाय सॉकेट ऑब्जेक्ट के संदर्भ गुजरती हैं। उदाहरण के लिए:

boost::asio::async_read(pSocket->getSocketForAsio(), 
      boost::asio::buffer(&buffer, sizeof(buffer)), 
      boost::bind(&Connection::handleRead, 
        shared_from_this(), 
        boost::asio::placeholders::error, 
        boost::asio::placeholders::bytes_transferred)); 

ध्यान दें कि सॉकेट पॉइंटर के रूप में संग्रहीत है। मैं नहीं सोच सकता कि पॉलिमॉर्फिज्म कैसे छिपाया जा सकता है।

जुर्माना (जो मुझे महान नहीं लगता) गैर-एसएसएल सॉकेट प्राप्त करने के लिए उपयोग किए जाने वाले संकेत का अतिरिक्त स्तर है।

+0

'Socket_t' के लिए' typedef' की तरह लगता है गुम है। साथ ही, क्या आप यह समझा सकते हैं कि यह क्यों काम करता है, यानी प्रत्येक प्रकार की सॉकेट के लिए 'next_layer() 'वास्तव में क्या करता है? – yhager

+0

सुधार के लिए, रोमन, धन्यवाद। – Nicole

+1

@yhager, जिस तरह से मैंने यह काम किया है वह एक एसएसएल सॉकेट को गैर-एसएसएल सॉकेट से प्राप्त करने के तरीके की प्रतिलिपि बनाना है। एएसओ एक कंटेनर में लपेटकर एक एसएसएल सॉकेट बनाता है जो कुछ अतिरिक्त कार्यक्षमता प्रदान करता है और जो उन्हें एसएसएल स्वाद जोड़कर कुछ गतिविधियों को रोकता है। ऐसे एसएसएल सॉकेट का उपयोग करने वाले कोड को एसएसएल और गैर-एसएसएल सॉकेट दोनों के लिए सामान्य गतिविधियों के लिए अंतर्निहित सॉकेट तक पहुंच प्राप्त करने के लिए 'next_layer() 'का उपयोग करने की आवश्यकता होगी। मेरा सभी सुझाव सॉकेट को इस तरह से लपेटना है कि इसे एसएसएल सॉकेट के रूप में माना जा सकता है, लेकिन किसी भी एसएसएल से संबंधित गतिविधियों को लागू किए बिना। – Nicole

1

वह कुछ इस तरह के साथ संकलन होगा:

typedef boost::asio::buffered_stream<boost::asio::ip::tcp::socket> Socket_t;

3

निश्चित रूप से समस्या यह है कि टीसीपी :: सॉकेट और ssl "सॉकेट" किसी भी आम पूर्वज का हिस्सा नहीं है है। लेकिन खुले होने के बाद सॉकेट का उपयोग करने के लिए अधिकांश फ़ंक्शन सटीक समान वाक्यविन्यास साझा करते हैं। इस प्रकार टेम्पलेट के साथ सबसे साफ समाधान है।

template <typename SocketType> 
void doStuffWithOpenSocket(SocketType socket) { 
    boost::asio::write(socket, ...); 
    boost::asio::read(socket, ...); 
    boost::asio::read_until(socket, ...); 
    // etc... 
} 

इस समारोह सामान्य टीसीपी :: सॉकेट और भी सुरक्षित SSL सॉकेट के साथ काम करने के लिए काम करेंगे:

boost::asio::ip::tcp::socket socket_; 
// socket_ opened normally ... 
doStuffWithOpenSocket<boost::asio::ip::tcp::socket>(socket_); // works! 

boost::asio::ssl::stream<boost::asio::ip::tcp::socket> secureSocket_; 
// secureSocket_ opened normally (including handshake) ... 
doStuffWithOpenSocket(secureSocket_); // also works, with (different) implicit instantiation! 
// shutdown the ssl socket when done ...