2011-01-18 3 views
21

저는 라이브러리를 Boost.Asio (지금까지 잘 진행된 라이브러리)로 변환하는 중입니다.하지만 저는 디자인 결정과 관련하여 뭔가 걸림돌이되었습니다.Boost.Asio에서 SSL 소켓과 비 SSL 소켓을 동시에 사용 하시겠습니까?

Boost.Asio는 SSL을 지원하지만 소켓에는 boost::asio::ssl::stream<boost::asio::ip::tcp::socket> 유형을 사용해야합니다. 내 라이브러리는 SSL 서버에 연결하거나 정상적으로 연결하는 옵션이 있습니다, 그래서 나는이 같은 두 개의 소켓 클래스를 만들었어요 :

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()이되어야합니다.)

누구나 가장 좋은 방법을 제안 할 수 있습니까? 나는이 목적을 위해서만 별도의 클래스를 생성하지 않을 것입니다. 왜냐하면 그것은 많은 코드를 복제해야하기 때문입니다.

편집 : OpenSSL 기능의 목적을 잘못 이해했기 때문에 원래 질문을 다시 말했습니다.

답변

14

할 수있는 방법이 몇 가지 있습니다. 과거에는, 나는 if/else 문의 많은 꽤 빨리 더러워 얻을 수

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

같은 일을했습니다.

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을 만들 - 당신은 또한 추상 클래스 (과도하게 단순화 의사 코드)를 만들 수 있습니다. 나는 많은 코드를 복제하지 않을 것이라고 생각하는데 async_read 또는 async_write이 필요할 때마다 if/else보다 깨끗합니다.

+0

첫 번째 방법은 내가하고 결국 무엇인가,하지만 난 추상 클래스의 생각처럼 수행

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

이 기능은 정상적인 TCP :: 소켓과도 보안 SSL 소켓 작업을 작동합니다. 감사합니다. 고마워요. – DSB

17

나는이 질문에 대답하기가 다소 늦었지만, 다른 사람들에게 도움이되기를 바랍니다. 샘의 대답은 아이디어의 세균을 포함하고 있지만 제 의견으로는 그만 두지는 않습니다.

이 아이디어는 asio가 스트림에서 SSL 소켓을 래핑한다는 관찰로부터 나온 것입니다. 이 모든 해결책은 비 SSL 소켓을 비슷하게 감싸는 것입니다.

SSL 소켓과 비 SSL 소켓간에 균일 한 외부 인터페이스를 갖는 결과는 세 가지 클래스로 수행됩니다. 하나는 기본으로 인터페이스를 효과적으로 정의합니다.

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)); 
    } 
}; 

두 개의 하위 클래스가 SSL 및 비 SSL 소켓을 래핑합니다.

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)); 

소켓이 포인터로 저장된다는 점에 유의하십시오. 다형성을 숨길 수있는 방법을 생각할 수 없습니다.

페널티 (위대하다고 생각하지는 않습니다)는 SSL이 아닌 소켓을 얻는 데 사용되는 간접 지정의 추가 수준입니다.

+0

은'Socket_t'에 대한'typedef'가 누락 된 것 같습니다. 또한 이것이 작동하는 이유, 즉 각 소켓 유형에 대해'next_layer() '가 정확히 무엇을하는지 설명 할 수 있습니까? – yhager

+0

고마워요, 로마. – Nicole

+1

@yhager, 내가이 작업을 한 방법은 SSL 소켓이 SSL이 아닌 소켓에서 파생되는 방식을 복사하는 것입니다. Asio는 몇 가지 추가 기능을 제공하고 SSL 향미를 추가하여 일부 액티비티를 가로채는 컨테이너에 SSL 소켓을 래핑하여 SSL 소켓을 만듭니다. 이러한 SSL 소켓을 사용하는 코드는 SSL 소켓과 비 SSL 소켓에 공통적 인 일부 활동을 위해 기본 소켓에 액세스하기 위해'next_layer() '를 사용해야합니다. 내 모든 제안은 SSL 소켓으로 취급 할 수 있지만 SSL 관련 활동을 구현하지 않고 소켓을 포장하는 것입니다. – Nicole

1

그것은이 같은 컴파일합니다 :

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

3

물론 문제는 어떤 공통 조상을 공유하지 않는 TCP :: 소켓과 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 ...