Skip to content

Commit 52b7a2f

Browse files
author
Raphael Dumusc
committed
Improved network version exchange protocol
This commit relaxes the check on the client side to allow connecting to servers with a more recent network protocol in the future. Clients also send their protocol version to the server. This information is simply ignored by older servers and does not break compatibility. The idea is to create a transition period after which the protocol can be modified (to exchange more information at connection time, such as support for stereo streams) without breaking compatibilty for clients and servers based on deflect >= 0.12.1.
1 parent 2c41044 commit 52b7a2f

File tree

8 files changed

+73
-60
lines changed

8 files changed

+73
-60
lines changed

deflect/ServerWorker.cpp

+21-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*********************************************************************/
2-
/* Copyright (c) 2013-2016, EPFL/Blue Brain Project */
2+
/* Copyright (c) 2013-2017, EPFL/Blue Brain Project */
33
/* Raphael Dumusc <[email protected]> */
44
55
/* All rights reserved. */
@@ -47,7 +47,10 @@
4747

4848
#include <QDataStream>
4949

50-
#define RECEIVE_TIMEOUT_MS 3000
50+
namespace
51+
{
52+
const int RECEIVE_TIMEOUT_MS = 3000;
53+
}
5154

5255
namespace deflect
5356
{
@@ -56,6 +59,7 @@ ServerWorker::ServerWorker( const int socketDescriptor )
5659
// Ensure that tcpSocket_ parent is *this* so it gets moved to thread
5760
: _tcpSocket( new QTcpSocket( this ))
5861
, _sourceId( socketDescriptor )
62+
, _clientProtocolVersion( NETWORK_PROTOCOL_VERSION )
5963
, _registeredToEvents( false )
6064
{
6165
if( !_tcpSocket->setSocketDescriptor( socketDescriptor ))
@@ -223,6 +227,9 @@ void ServerWorker::_handleMessage( const MessageHeader& messageHeader,
223227
return;
224228
}
225229
_streamId = uri;
230+
// The version is only sent by deflect clients since v. 0.13.0
231+
if( !byteArray.isEmpty( ))
232+
_parseClientProtocolVersion( byteArray );
226233
emit addStreamSource( _streamId, _sourceId );
227234
break;
228235

@@ -263,17 +270,22 @@ void ServerWorker::_handleMessage( const MessageHeader& messageHeader,
263270
}
264271
}
265272

266-
void ServerWorker::_handlePixelStreamMessage( const QByteArray& byteArray )
273+
void ServerWorker::_parseClientProtocolVersion( const QByteArray& message )
267274
{
268-
const SegmentParameters* parameters =
269-
reinterpret_cast< const SegmentParameters* >( byteArray.data( ));
275+
bool ok = false;
276+
const int version = message.toInt( &ok );
277+
if( ok )
278+
_clientProtocolVersion = version;
279+
}
270280

281+
void ServerWorker::_handlePixelStreamMessage( const QByteArray& message )
282+
{
271283
Segment segment;
272-
segment.parameters = *parameters;
273284

274-
QByteArray imageData =
275-
byteArray.right( byteArray.size() - sizeof( SegmentParameters ));
276-
segment.imageData = imageData;
285+
const auto data = message.data();
286+
segment.parameters = *reinterpret_cast<const SegmentParameters*>( data );
287+
segment.imageData = message.right( message.size() -
288+
sizeof( SegmentParameters ));
277289

278290
emit( receivedSegment( _streamId, _sourceId, segment ));
279291
}

deflect/ServerWorker.h

+4-2
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ private slots:
9696

9797
QString _streamId;
9898
int _sourceId;
99+
int _clientProtocolVersion;
99100

100101
bool _registeredToEvents;
101102
QQueue<Event> _events;
@@ -105,8 +106,9 @@ private slots:
105106
QByteArray _receiveMessageBody( int size );
106107

107108
void _handleMessage( const MessageHeader& messageHeader,
108-
const QByteArray& byteArray );
109-
void _handlePixelStreamMessage( const QByteArray& byteArray );
109+
const QByteArray& message );
110+
void _parseClientProtocolVersion( const QByteArray& message );
111+
void _handlePixelStreamMessage( const QByteArray& message );
110112

111113
void _sendProtocolVersion();
112114
void _sendBindReply( bool successful );

deflect/Socket.cpp

+26-29
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ const unsigned short Socket::defaultPortNumber = DEFAULT_PORT_NUMBER;
5959
Socket::Socket( const std::string& host, const unsigned short port )
6060
: _host( host )
6161
, _socket( new QTcpSocket( ))
62-
, _remoteProtocolVersion( INVALID_NETWORK_PROTOCOL_VERSION )
62+
, _serverProtocolVersion( INVALID_NETWORK_PROTOCOL_VERSION )
6363
{
6464
// disable warnings which occur if no QCoreApplication is present during
6565
// _connect(): QObject::connect: Cannot connect (null)::destroyed() to
@@ -91,6 +91,11 @@ bool Socket::isConnected() const
9191
return _socket->state() == QTcpSocket::ConnectedState;
9292
}
9393

94+
int32_t Socket::getServerProtocolVersion() const
95+
{
96+
return _serverProtocolVersion;
97+
}
98+
9499
int Socket::getFileDescriptor() const
95100
{
96101
return _socket->socketDescriptor();
@@ -172,11 +177,6 @@ bool Socket::receive( MessageHeader& messageHeader, QByteArray& message )
172177
return true;
173178
}
174179

175-
int32_t Socket::getRemoteProtocolVersion() const
176-
{
177-
return _remoteProtocolVersion;
178-
}
179-
180180
bool Socket::_receiveHeader( MessageHeader& messageHeader )
181181
{
182182
while( _socket->bytesAvailable() < qint64(MessageHeader::serializedSize) )
@@ -193,45 +193,42 @@ bool Socket::_receiveHeader( MessageHeader& messageHeader )
193193

194194
bool Socket::_connect( const std::string& host, const unsigned short port )
195195
{
196-
// make sure we're disconnected
197-
_socket->disconnectFromHost();
198-
199-
// open connection
200196
_socket->connectToHost( host.c_str(), port );
201-
202197
if( !_socket->waitForConnected( RECEIVE_TIMEOUT_MS ))
203198
{
204-
std::cerr << "could not connect to host " << host << ":" << port
199+
std::cerr << "could not connect to " << host << ":" << port
205200
<< std::endl;
206201
return false;
207202
}
208203

209-
// handshake
210-
if( _checkProtocolVersion( ))
211-
return true;
204+
if( !_receiveProtocolVersion( ))
205+
{
206+
std::cerr << "server protocol version was not received" << std::endl;
207+
_socket->disconnectFromHost();
208+
return false;
209+
}
210+
211+
if( _serverProtocolVersion < NETWORK_PROTOCOL_VERSION )
212+
{
213+
std::cerr << "server uses unsupported protocol: "
214+
<< _serverProtocolVersion << " < "
215+
<< NETWORK_PROTOCOL_VERSION << std::endl;
216+
_socket->disconnectFromHost();
217+
return false;
218+
}
212219

213-
std::cerr << "Protocol version check failed for host: " << host << ":"
214-
<< port << std::endl;
215-
_socket->disconnectFromHost();
216-
return false;
220+
return true;
217221
}
218222

219-
bool Socket::_checkProtocolVersion()
223+
bool Socket::_receiveProtocolVersion()
220224
{
221225
while( _socket->bytesAvailable() < qint64(sizeof(int32_t)) )
222226
{
223227
if( !_socket->waitForReadyRead( RECEIVE_TIMEOUT_MS ))
224228
return false;
225229
}
226-
227-
_socket->read((char *)&_remoteProtocolVersion, sizeof(int32_t));
228-
229-
if( _remoteProtocolVersion == NETWORK_PROTOCOL_VERSION )
230-
return true;
231-
232-
std::cerr << "unsupported protocol version " << _remoteProtocolVersion
233-
<< " != " << NETWORK_PROTOCOL_VERSION << std::endl;
234-
return false;
230+
_socket->read((char*)&_serverProtocolVersion, sizeof(int32_t));
231+
return true;
235232
}
236233

237234
}

deflect/Socket.h

+11-12
Original file line numberDiff line numberDiff line change
@@ -85,18 +85,21 @@ class Socket : public QObject
8585
/** Is the Socket connected */
8686
DEFLECT_API bool isConnected() const;
8787

88-
/**
89-
* Is there a pending message
90-
* @param messageSize Minimum size of the message
91-
*/
92-
bool hasMessage( const size_t messageSize = 0 ) const;
88+
/** @return the protocol version of the server. */
89+
int32_t getServerProtocolVersion() const;
9390

9491
/**
9592
* Get the FileDescriptor for the Socket (for use by poll())
9693
* @return The file descriptor if available, otherwise return -1.
9794
*/
9895
int getFileDescriptor() const;
9996

97+
/**
98+
* Is there a pending message
99+
* @param messageSize Minimum size of the message
100+
*/
101+
bool hasMessage( const size_t messageSize = 0 ) const;
102+
100103
/**
101104
* Send a message.
102105
* @param messageHeader The message header
@@ -113,23 +116,19 @@ class Socket : public QObject
113116
*/
114117
bool receive( MessageHeader& messageHeader, QByteArray& message );
115118

116-
/** Get the protocol version of the remote host */
117-
int32_t getRemoteProtocolVersion() const;
118-
119119
signals:
120120
/** Signal that the socket has been disconnected. */
121121
void disconnected();
122122

123123
private:
124124
const std::string _host;
125125
QTcpSocket* _socket;
126-
int32_t _remoteProtocolVersion;
127126
mutable QMutex _socketMutex;
128-
129-
bool _connect( const std::string &host, const unsigned short port );
130-
bool _checkProtocolVersion();
127+
int32_t _serverProtocolVersion;
131128

132129
bool _receiveHeader( MessageHeader& messageHeader );
130+
bool _connect( const std::string &host, const unsigned short port );
131+
bool _receiveProtocolVersion();
133132
};
134133

135134
}

deflect/StreamPrivate.cpp

+4-2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141

4242
#include "StreamPrivate.h"
4343

44+
#include "NetworkProtocol.h"
4445
#include "Segment.h"
4546
#include "SegmentParameters.h"
4647
#include "SizeHints.h"
@@ -119,8 +120,9 @@ StreamPrivate::~StreamPrivate()
119120

120121
void StreamPrivate::sendOpen()
121122
{
122-
const MessageHeader mh( MESSAGE_TYPE_PIXELSTREAM_OPEN, 0, id );
123-
socket.send( mh, QByteArray( ));
123+
const auto message = QByteArray::number( NETWORK_PROTOCOL_VERSION );
124+
const MessageHeader mh( MESSAGE_TYPE_PIXELSTREAM_OPEN, message.size(), id );
125+
socket.send( mh, message );
124126
}
125127

126128
void StreamPrivate::sendClose()

doc/Changelog.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ Changelog {#Changelog}
33

44
## Deflect 0.12
55

6-
### 0.12.1 (git master)
6+
### 0.12.1 (01-02-2017)
77

88
* [147](https://github.com/BlueBrain/Deflect/pull/147):
9+
Improved handling of network protocol updates. Future updates should be
10+
possible without breaking any client/server based on this release.
911
Deflect server: better reporting of JPEG decompression errors.
1012
* [146](https://github.com/BlueBrain/Deflect/pull/146):
1113
Unified the command line options and help message of applications.

tests/cpp/SocketTests.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ namespace ut = boost::unit_test;
4444
#include "MinimalGlobalQtApp.h"
4545
#include "MockServer.h"
4646

47+
#include <deflect/NetworkProtocol.h>
4748
#include <deflect/Socket.h>
4849

4950
#include <QThread>
@@ -53,14 +54,14 @@ BOOST_GLOBAL_FIXTURE( MinimalGlobalQtApp );
5354
void testSocketConnect( const int32_t versionOffset )
5455
{
5556
QThread thread;
56-
MockServer* server = new MockServer( NETWORK_PROTOCOL_VERSION + versionOffset );
57+
auto server = new MockServer( NETWORK_PROTOCOL_VERSION + versionOffset );
5758
server->moveToThread( &thread );
5859
server->connect( &thread, &QThread::finished, server, &QObject::deleteLater );
5960
thread.start();
6061

6162
deflect::Socket socket( "localhost", server->serverPort( ));
6263

63-
BOOST_CHECK( socket.isConnected() == (versionOffset == 0));
64+
BOOST_CHECK( socket.isConnected() == (versionOffset >= 0));
6465

6566
thread.quit();
6667
thread.wait();

tests/mock/MockServer.h

+1-3
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ typedef __int32 int32_t;
4646

4747
#include <deflect/mock/api.h>
4848
#include <deflect/config.h>
49-
#include <deflect/NetworkProtocol.h>
5049

5150
#include <QtNetwork/QTcpServer>
5251

@@ -55,8 +54,7 @@ class MockServer : public QTcpServer
5554
Q_OBJECT
5655

5756
public:
58-
DEFLECT_API explicit MockServer( int32_t protocolVersion =
59-
NETWORK_PROTOCOL_VERSION );
57+
DEFLECT_API explicit MockServer( int32_t protocolVersion );
6058
DEFLECT_API virtual ~MockServer();
6159

6260
protected:

0 commit comments

Comments
 (0)