1//
2// Socket.cpp
3//
4// Library: Net
5// Package: Sockets
6// Module: Socket
7//
8// Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH.
9// and Contributors.
10//
11// SPDX-License-Identifier: BSL-1.0
12//
13
14
15#include "Poco/Net/Socket.h"
16#include "Poco/Net/StreamSocketImpl.h"
17#include "Poco/Timestamp.h"
18#include <algorithm>
19#include <string.h> // FD_SET needs memset on some platforms, so we can't use <cstring>
20#if defined(POCO_HAVE_FD_EPOLL)
21#include <sys/epoll.h>
22#elif defined(POCO_HAVE_FD_POLL)
23#include "Poco/SharedPtr.h"
24#include <poll.h>
25#endif
26
27
28namespace Poco {
29namespace Net {
30
31
32Socket::Socket():
33 _pImpl(new StreamSocketImpl)
34{
35}
36
37
38Socket::Socket(SocketImpl* pImpl):
39 _pImpl(pImpl)
40{
41 poco_check_ptr (_pImpl);
42}
43
44
45Socket::Socket(const Socket& socket):
46 _pImpl(socket._pImpl)
47{
48 poco_check_ptr (_pImpl);
49
50 _pImpl->duplicate();
51}
52
53
54Socket& Socket::operator = (const Socket& socket)
55{
56 if (&socket != this)
57 {
58 if (_pImpl) _pImpl->release();
59 _pImpl = socket._pImpl;
60 if (_pImpl) _pImpl->duplicate();
61 }
62 return *this;
63}
64
65
66Socket::~Socket()
67{
68 _pImpl->release();
69}
70
71
72int Socket::select(SocketList& readList, SocketList& writeList, SocketList& exceptList, const Poco::Timespan& timeout)
73{
74#if defined(POCO_HAVE_FD_EPOLL)
75
76 int epollSize = readList.size() + writeList.size() + exceptList.size();
77 if (epollSize == 0) return 0;
78
79 int epollfd = -1;
80 {
81 struct epoll_event eventsIn[epollSize];
82 memset(eventsIn, 0, sizeof(epoll_event) * epollSize );
83
84 struct epoll_event* eventLast = eventsIn;
85 for (SocketList::iterator it = readList.begin(); it != readList.end(); ++it)
86 {
87 poco_socket_t sockfd = it->sockfd();
88 if (sockfd != POCO_INVALID_SOCKET)
89 {
90 struct epoll_event* e = eventsIn;
91 for (; e != eventLast; ++e)
92 {
93 if (reinterpret_cast<Socket*>(e->data.ptr)->sockfd() == sockfd)
94 break;
95 }
96 if (e == eventLast)
97 {
98 e->data.ptr = &(*it);
99 ++eventLast;
100 }
101 e->events |= EPOLLIN;
102 }
103 }
104
105 for (SocketList::iterator it = writeList.begin(); it != writeList.end(); ++it)
106 {
107 poco_socket_t sockfd = it->sockfd();
108 if (sockfd != POCO_INVALID_SOCKET)
109 {
110 struct epoll_event* e = eventsIn;
111 for (; e != eventLast; ++e)
112 {
113 if (reinterpret_cast<Socket*>(e->data.ptr)->sockfd() == sockfd)
114 break;
115 }
116 if (e == eventLast)
117 {
118 e->data.ptr = &(*it);
119 ++eventLast;
120 }
121 e->events |= EPOLLOUT;
122 }
123 }
124
125 for (SocketList::iterator it = exceptList.begin(); it != exceptList.end(); ++it)
126 {
127 poco_socket_t sockfd = it->sockfd();
128 if (sockfd != POCO_INVALID_SOCKET)
129 {
130 struct epoll_event* e = eventsIn;
131 for (; e != eventLast; ++e)
132 {
133 if (reinterpret_cast<Socket*>(e->data.ptr)->sockfd() == sockfd)
134 break;
135 }
136 if (e == eventLast)
137 {
138 e->data.ptr = &(*it);
139 ++eventLast;
140 }
141 e->events |= EPOLLERR;
142 }
143 }
144
145 epollSize = eventLast - eventsIn;
146 if (epollSize == 0) return 0;
147
148 epollfd = epoll_create(1);
149 if (epollfd < 0)
150 {
151 SocketImpl::error("Can't create epoll queue");
152 }
153
154 for (struct epoll_event* e = eventsIn; e != eventLast; ++e)
155 {
156 poco_socket_t sockfd = reinterpret_cast<Socket*>(e->data.ptr)->sockfd();
157 if (sockfd != POCO_INVALID_SOCKET)
158 {
159 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, e) < 0)
160 {
161 ::close(epollfd);
162 SocketImpl::error("Can't insert socket to epoll queue");
163 }
164 }
165 }
166 }
167
168 struct epoll_event eventsOut[epollSize];
169 memset(eventsOut, 0, sizeof(epoll_event) * epollSize );
170
171 Poco::Timespan remainingTime(timeout);
172 int rc;
173 do
174 {
175 Poco::Timestamp start;
176 rc = epoll_wait(epollfd, eventsOut, epollSize, remainingTime.totalMilliseconds());
177 if (rc < 0 && SocketImpl::lastError() == POCO_EINTR)
178 {
179 Poco::Timestamp end;
180 Poco::Timespan waited = end - start;
181 if (waited < remainingTime)
182 remainingTime -= waited;
183 else
184 remainingTime = 0;
185 }
186 }
187 while (rc < 0 && SocketImpl::lastError() == POCO_EINTR);
188
189 ::close(epollfd);
190 if (rc < 0) SocketImpl::error();
191
192 SocketList readyReadList;
193 SocketList readyWriteList;
194 SocketList readyExceptList;
195 for (int n = 0; n < rc; ++n)
196 {
197 if (eventsOut[n].events & EPOLLERR)
198 readyExceptList.push_back(*reinterpret_cast<Socket*>(eventsOut[n].data.ptr));
199 if (eventsOut[n].events & EPOLLIN)
200 readyReadList.push_back(*reinterpret_cast<Socket*>(eventsOut[n].data.ptr));
201 if (eventsOut[n].events & EPOLLOUT)
202 readyWriteList.push_back(*reinterpret_cast<Socket*>(eventsOut[n].data.ptr));
203 }
204 std::swap(readList, readyReadList);
205 std::swap(writeList, readyWriteList);
206 std::swap(exceptList, readyExceptList);
207 return readList.size() + writeList.size() + exceptList.size();
208
209#elif defined(POCO_HAVE_FD_POLL)
210 typedef Poco::SharedPtr<pollfd, Poco::ReferenceCounter, Poco::ReleaseArrayPolicy<pollfd> > SharedPollArray;
211
212 nfds_t nfd = readList.size() + writeList.size() + exceptList.size();
213 if (0 == nfd) return 0;
214
215 SharedPollArray pPollArr = new pollfd[nfd];
216
217 int idx = 0;
218 for (SocketList::iterator it = readList.begin(); it != readList.end(); ++it)
219 {
220 pPollArr[idx].fd = int(it->sockfd());
221 pPollArr[idx++].events |= POLLIN;
222 }
223
224 SocketList::iterator begR = readList.begin();
225 SocketList::iterator endR = readList.end();
226 for (SocketList::iterator it = writeList.begin(); it != writeList.end(); ++it)
227 {
228 SocketList::iterator pos = std::find(begR, endR, *it);
229 if (pos != endR)
230 {
231 pPollArr[pos-begR].events |= POLLOUT;
232 --nfd;
233 }
234 else
235 {
236 pPollArr[idx].fd = int(it->sockfd());
237 pPollArr[idx++].events |= POLLOUT;
238 }
239 }
240
241 SocketList::iterator begW = writeList.begin();
242 SocketList::iterator endW = writeList.end();
243 for (SocketList::iterator it = exceptList.begin(); it != exceptList.end(); ++it)
244 {
245 SocketList::iterator pos = std::find(begR, endR, *it);
246 if (pos != endR) --nfd;
247 else
248 {
249 SocketList::iterator pos = std::find(begW, endW, *it);
250 if (pos != endW) --nfd;
251 else pPollArr[idx++].fd = int(it->sockfd());
252 }
253 }
254
255 Poco::Timespan remainingTime(timeout);
256 int rc;
257 do
258 {
259 Poco::Timestamp start;
260 rc = ::poll(pPollArr, nfd, timeout.totalMilliseconds());
261 if (rc < 0 && SocketImpl::lastError() == POCO_EINTR)
262 {
263 Poco::Timestamp end;
264 Poco::Timespan waited = end - start;
265 if (waited < remainingTime) remainingTime -= waited;
266 else remainingTime = 0;
267 }
268 }
269 while (rc < 0 && SocketImpl::lastError() == POCO_EINTR);
270 if (rc < 0) SocketImpl::error();
271
272 SocketList readyReadList;
273 SocketList readyWriteList;
274 SocketList readyExceptList;
275
276 SocketList::iterator begE = exceptList.begin();
277 SocketList::iterator endE = exceptList.end();
278 for (int idx = 0; idx < nfd; ++idx)
279 {
280 SocketList::iterator slIt = std::find_if(begR, endR, Socket::FDCompare(pPollArr[idx].fd));
281 if (POLLIN & pPollArr[idx].revents && slIt != endR) readyReadList.push_back(*slIt);
282 slIt = std::find_if(begW, endW, Socket::FDCompare(pPollArr[idx].fd));
283 if (POLLOUT & pPollArr[idx].revents && slIt != endW) readyWriteList.push_back(*slIt);
284 slIt = std::find_if(begE, endE, Socket::FDCompare(pPollArr[idx].fd));
285 if (POLLERR & pPollArr[idx].revents && slIt != endE) readyExceptList.push_back(*slIt);
286 }
287 std::swap(readList, readyReadList);
288 std::swap(writeList, readyWriteList);
289 std::swap(exceptList, readyExceptList);
290 return readList.size() + writeList.size() + exceptList.size();
291
292#else
293
294 fd_set fdRead;
295 fd_set fdWrite;
296 fd_set fdExcept;
297 int nfd = 0;
298 FD_ZERO(&fdRead);
299 for (SocketList::const_iterator it = readList.begin(); it != readList.end(); ++it)
300 {
301 poco_socket_t fd = it->sockfd();
302 if (fd != POCO_INVALID_SOCKET)
303 {
304 if (int(fd) > nfd)
305 nfd = int(fd);
306 FD_SET(fd, &fdRead);
307 }
308 }
309 FD_ZERO(&fdWrite);
310 for (SocketList::const_iterator it = writeList.begin(); it != writeList.end(); ++it)
311 {
312 poco_socket_t fd = it->sockfd();
313 if (fd != POCO_INVALID_SOCKET)
314 {
315 if (int(fd) > nfd)
316 nfd = int(fd);
317 FD_SET(fd, &fdWrite);
318 }
319 }
320 FD_ZERO(&fdExcept);
321 for (SocketList::const_iterator it = exceptList.begin(); it != exceptList.end(); ++it)
322 {
323 poco_socket_t fd = it->sockfd();
324 if (fd != POCO_INVALID_SOCKET)
325 {
326 if (int(fd) > nfd)
327 nfd = int(fd);
328 FD_SET(fd, &fdExcept);
329 }
330 }
331 if (nfd == 0) return 0;
332 Poco::Timespan remainingTime(timeout);
333 int rc;
334 do
335 {
336 struct timeval tv;
337 tv.tv_sec = (long) remainingTime.totalSeconds();
338 tv.tv_usec = (long) remainingTime.useconds();
339 Poco::Timestamp start;
340 rc = ::select(nfd + 1, &fdRead, &fdWrite, &fdExcept, &tv);
341 if (rc < 0 && SocketImpl::lastError() == POCO_EINTR)
342 {
343 Poco::Timestamp end;
344 Poco::Timespan waited = end - start;
345 if (waited < remainingTime)
346 remainingTime -= waited;
347 else
348 remainingTime = 0;
349 }
350 }
351 while (rc < 0 && SocketImpl::lastError() == POCO_EINTR);
352 if (rc < 0) SocketImpl::error();
353
354 SocketList readyReadList;
355 for (SocketList::const_iterator it = readList.begin(); it != readList.end(); ++it)
356 {
357 poco_socket_t fd = it->sockfd();
358 if (fd != POCO_INVALID_SOCKET)
359 {
360 if (FD_ISSET(fd, &fdRead))
361 readyReadList.push_back(*it);
362 }
363 }
364 std::swap(readList, readyReadList);
365 SocketList readyWriteList;
366 for (SocketList::const_iterator it = writeList.begin(); it != writeList.end(); ++it)
367 {
368 poco_socket_t fd = it->sockfd();
369 if (fd != POCO_INVALID_SOCKET)
370 {
371 if (FD_ISSET(fd, &fdWrite))
372 readyWriteList.push_back(*it);
373 }
374 }
375 std::swap(writeList, readyWriteList);
376 SocketList readyExceptList;
377 for (SocketList::const_iterator it = exceptList.begin(); it != exceptList.end(); ++it)
378 {
379 poco_socket_t fd = it->sockfd();
380 if (fd != POCO_INVALID_SOCKET)
381 {
382 if (FD_ISSET(fd, &fdExcept))
383 readyExceptList.push_back(*it);
384 }
385 }
386 std::swap(exceptList, readyExceptList);
387 return rc;
388
389#endif // POCO_HAVE_FD_EPOLL
390}
391
392
393} } // namespace Poco::Net
394