CustusX  18.04
An IGT application
cxSocketConnection.cpp
Go to the documentation of this file.
1 /*=========================================================================
2 This file is part of CustusX, an Image Guided Therapy Application.
3 
4 Copyright (c) SINTEF Department of Medical Technology.
5 All rights reserved.
6 
7 CustusX is released under a BSD 3-Clause license.
8 
9 See Lisence.txt (https://github.com/SINTEFMedtek/CustusX/blob/master/License.txt) for details.
10 =========================================================================*/
11 
12 #include "cxSocketConnection.h"
13 #include <QTcpServer>
14 #include <QTcpSocket>
15 #include <QNetworkInterface>
16 #include <QThread>
17 
18 #include "cxSocket.h"
19 #include "cxLogger.h"
20 
22 {
23  "inactive",
24  "connected",
25  "listening",
26  "connecting"
27 }
29 
30 
31 
32 namespace cx
33 {
34 
35 
37  {
38  return (this->role == rhs.role)
39  && (this->protocol == rhs.protocol)
40  && (this->host == rhs.host)
41  && (this->port == rhs.port);
42  }
43 
44  bool SocketConnection::ConnectionInfo::isServer() const { return role.toLower()=="server"; }
45  bool SocketConnection::ConnectionInfo::isClient() const { return !this->isServer(); }
46  bool SocketConnection::ConnectionInfo::isLocalhostConnection() const { return host.toLower().contains("localhost"); }
47 
49  {
50  QString postfix;
51  QString name = host;
52  if (isServer())
53  name = "listen";
54  if (!protocol.isEmpty())
55  postfix = QString("[%1]").arg(protocol);
56  return QString("%1:%2%3").arg(name).arg(port).arg(postfix);
57  }
58 
62 
63 
65  QObject(parent)
66 {
68  qRegisterMetaType<boost::function<void()> >("boost::function<void()>");
69  qRegisterMetaType<CX_SOCKETCONNECTION_STATE>("CX_SOCKETCONNECTION_STATE");
70 
71  mNextConnectionInfo.host = "localhost";
72  mNextConnectionInfo.port = 18944;
73  mNextConnectionInfo.role = "client";
74 
75  mSocket = new QTcpSocket(this);
76  connect(mSocket, &QTcpSocket::connected, this, &SocketConnection::internalConnected);
77  connect(mSocket, &QTcpSocket::disconnected, this, &SocketConnection::internalDisconnected);
78  connect(mSocket, &QTcpSocket::readyRead, this, &SocketConnection::internalDataAvailable);
79 
80  //see http://stackoverflow.com/questions/26062397/qt-connect-function-signal-disambiguation-using-lambdas
81  void (QTcpSocket::* errorOverloaded)(QAbstractSocket::SocketError) = &QTcpSocket::error;
82  connect(mSocket, errorOverloaded, this, &SocketConnection::error);
83  connect(mSocket, errorOverloaded, this, &SocketConnection::internalError);
84 }
85 
87 {
88  QMutexLocker locker(&mNextConnectionInfoMutex);
89  return mNextConnectionInfo;
90 }
91 
93 {
94  QMutexLocker locker(&mNextConnectionInfoMutex);
95  if (info==mNextConnectionInfo)
96  return;
97  mNextConnectionInfo = info;
98  locker.unlock();
99 
100  emit connectionInfoChanged();
101 }
102 
104 {
105  if (mCurrentState==newState)
106  return;
107 
108  if (newState==scsCONNECTED)
109  emit connected();
110  else if (mCurrentState==scsCONNECTED)
111  emit disconnected();
112 
113  mCurrentState = newState;
114 
116 }
117 
119 {
120  return mCurrentState;
121 }
122 
124 {
126 
127  ConnectionInfo info = this->getConnectionInfo();
128  mConnector = this->createConnector(info);
129  this->setProtocol(info.protocol);
130  mConnector->activate();
131 }
132 
134 {
135  SocketConnectorPtr retval;
136 
137  if (info.isClient())
138  retval.reset(new SocketClientConnector(info, mSocket));
139  else
140  retval.reset(new SocketServerConnector(info, mSocket));
141 
142  connect(retval.get(), &SocketConnector::stateChanged, this, &SocketConnection::stateChange);
143  return retval;
144 }
145 
147 {
149 
150  if (mConnector)
151  {
152  mConnector->deactivate();
153  mConnector.reset();
154  }
155  mSocket->close();
156 }
157 
158 bool SocketConnection::sendData(const char *data, qint64 maxSize)
159 {
160  if(!this->socketIsConnected())
161  return false;
162  qint64 writtenBytes = mSocket->write(data, maxSize);
163  if(writtenBytes != maxSize)
164  return false;
165  return true;
166 }
167 
168 void SocketConnection::internalConnected()
169 {
170 // CX_LOG_SUCCESS() << "Connected to " << mCurrentConnectionInfo.getDescription();
171  this->stateChange(this->computeState());
172 }
173 
174 void SocketConnection::internalDisconnected()
175 {
176 // CX_LOG_SUCCESS() << "Disconnected";
177  this->stateChange(this->computeState());
178 }
179 
180 void SocketConnection::internalError(QAbstractSocket::SocketError socketError)
181 {
182  CX_LOG_ERROR() << QString("[%1] Socket error [code=%2]: %3")
183  .arg(mSocket->peerName())
184  .arg(QString::number(socketError))
185  .arg(mSocket->errorString());
186 
187  this->stateChange(this->computeState());
188 }
189 
191 {
192  return mSocket->state() == QAbstractSocket::ConnectedState;
193 }
194 
196 {
197  bool enoughBytes = mSocket->bytesAvailable() >= bytes;
198  if(!enoughBytes)
199  CX_LOG_DEBUG() << "Want " << bytes << " but only "<< mSocket->bytesAvailable() << " are available on the socket atm.";
200  return enoughBytes;
201 }
202 
203 bool SocketConnection::socketReceive(void *packPointer, int packSize) const
204 {
205  if(!this->enoughBytesAvailableOnSocket(packSize))
206  return false;
207 
208  char* charPointer = reinterpret_cast<char*>(packPointer);
209  int r = mSocket->read(charPointer, packSize);
210  if(r <= 0)
211  {
212  CX_LOG_ERROR() << "Error when receiving data from socket.";
213  return false;
214  }
215  return true;
216 }
217 
219 {
220  if (mConnector)
221  return mConnector->getState();
222  return scsINACTIVE;
223 }
224 
225 
226 } //namespace cx
virtual void setConnectionInfo(ConnectionInfo info)
thread-safe
CX_SOCKETCONNECTION_STATE
bool sendData(const char *data, qint64 maxSize)
not thread-safe
SNW_DEFINE_ENUM_STRING_CONVERTERS_BEGIN(cx, CX_SOCKETCONNECTION_STATE, scsCOUNT)
bool socketReceive(void *packPointer, int packSize) const
bool enoughBytesAvailableOnSocket(int bytes) const
CX_SOCKETCONNECTION_STATE getState()
thread-safe
ConnectionInfo mNextConnectionInfo
info to be used for the next connect(), mutexed.
void stateChange(CX_SOCKETCONNECTION_STATE newState)
CX_SOCKETCONNECTION_STATE mCurrentState
void stateChanged(CX_SOCKETCONNECTION_STATE status)
virtual void requestDisconnect()
not thread-safe: use invoke
ConnectionInfo getConnectionInfo()
thread-safe
SocketConnection(QObject *parent=0)
virtual void requestConnect()
not thread-safe: use invoke
#define CX_LOG_ERROR
Definition: cxLogger.h:99
SocketConnectorPtr createConnector(ConnectionInfo info)
virtual void setProtocol(QString protocolname)=0
boost::shared_ptr< class SocketConnector > SocketConnectorPtr
void stateChanged(CX_SOCKETCONNECTION_STATE)
#define CX_LOG_DEBUG
Definition: cxLogger.h:95
#define assertRunningInObjectThread()
CX_SOCKETCONNECTION_STATE computeState()
SocketConnectorPtr mConnector
bool operator==(const ConnectionInfo &rhs) const
SNW_DEFINE_ENUM_STRING_CONVERTERS_END(cx, ORIENTATION_TYPE, otCOUNT)
Namespace for all CustusX production code.