udpsocketpool.cpp
1 //------------------------------------------------------------------------------
2 // udpsocketpool.cpp
3 //------------------------------------------------------------------------------
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 // 02110-1301 USA
19 //
20 //------------------------------------------------------------------------------
21 // Copyright (C) 2017 "Zalewa" <zalewapl@gmail.com>
22 //------------------------------------------------------------------------------
23 #include "udpsocketpool.h"
24 
25 #include "refresher/hostport.h"
26 #include <QList>
27 #include <QUdpSocket>
28 
29 DClass<UdpSocketPool>
30 {
31 public:
32  class Asset
33  {
34  public:
35  QUdpSocket *socket;
36  QSet<HostPort> addresses;
37  bool valid;
38 
39  Asset()
40  {
41  socket = new QUdpSocket();
42  #ifdef Q_OS_OPENBSD
43  valid = socket->bind(QHostAddress("0.0.0.0"));
44  #else
45  valid = socket->bind();
46  #endif
47  }
48 
49  ~Asset()
50  {
51  delete socket;
52  }
53 
54  void addAddress(const HostPort &hostPort)
55  {
56  addresses.insert(hostPort);
57  }
58 
59  bool hasAddress(const HostPort &hostPort) const
60  {
61  return addresses.contains(hostPort);
62  }
63 
64  int size() const
65  {
66  return addresses.size();
67  }
68  };
69 
70  QList<Asset *> pool;
71  int sliceSize;
72 };
73 DPointeredNoCopy(UdpSocketPool)
74 
75 UdpSocketPool::UdpSocketPool(int sliceSize)
76 {
77  d->sliceSize = qMax(1, sliceSize);
78 }
79 
80 UdpSocketPool::~UdpSocketPool()
81 {
82  qDeleteAll(d->pool);
83 }
84 
85 QUdpSocket *UdpSocketPool::acquire(const QHostAddress &address, quint16 port)
86 {
87  HostPort hostPort(address, port);
88  // Return if already acquired.
89  for (PrivData<UdpSocketPool>::Asset *asset : d->pool)
90  {
91  if (asset->hasAddress(hostPort))
92  {
93  return asset->socket;
94  }
95  }
96  // Acquire existing.
97  for (PrivData<UdpSocketPool>::Asset *asset : d->pool)
98  {
99  if (asset->size() < d->sliceSize)
100  {
101  asset->addAddress(hostPort);
102  return asset->socket;
103  }
104  }
105  // Acquire new.
106  auto asset = new PrivData<UdpSocketPool>::Asset();
107  if (asset->valid)
108  {
109  this->connect(asset->socket, SIGNAL(readyRead()), SIGNAL(readyRead()));
110  asset->addAddress(hostPort);
111  d->pool << asset;
112  return asset->socket;
113  }
114  else
115  {
116  delete asset;
117  return nullptr;
118  }
119 }
120 
121 QUdpSocket *UdpSocketPool::acquireMasterSocket()
122 {
123  return acquire(QHostAddress("0.0.0.0"), 0);
124 }
125 
126 void UdpSocketPool::releaseAll()
127 {
128  qDeleteAll(d->pool);
129  d->pool.clear();
130 }
131 
132 bool UdpSocketPool::hasPendingDatagrams() const
133 {
134  for (PrivData<UdpSocketPool>::Asset *asset : d->pool)
135  {
136  if (asset->socket->hasPendingDatagrams())
137  return true;
138  }
139  return false;
140 }
141 
142 QByteArray UdpSocketPool::readNextDatagram(QHostAddress *address, quint16 *port)
143 {
144  for (PrivData<UdpSocketPool>::Asset *asset : d->pool)
145  {
146  QUdpSocket *socket = asset->socket;
147  if (socket->hasPendingDatagrams())
148  {
149  int size = socket->pendingDatagramSize();
150  char *buffer = new char[size];
151  socket->readDatagram(buffer, size, address, port);
152  QByteArray data(buffer, size);
153  delete [] buffer;
154  return data;
155  }
156  }
157  return QByteArray();
158 }