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 | |
28 | namespace Poco { |
29 | namespace Net { |
30 | |
31 | |
32 | Socket::Socket(): |
33 | _pImpl(new StreamSocketImpl) |
34 | { |
35 | } |
36 | |
37 | |
38 | Socket::Socket(SocketImpl* pImpl): |
39 | _pImpl(pImpl) |
40 | { |
41 | poco_check_ptr (_pImpl); |
42 | } |
43 | |
44 | |
45 | Socket::Socket(const Socket& socket): |
46 | _pImpl(socket._pImpl) |
47 | { |
48 | poco_check_ptr (_pImpl); |
49 | |
50 | _pImpl->duplicate(); |
51 | } |
52 | |
53 | |
54 | Socket& 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 | |
66 | Socket::~Socket() |
67 | { |
68 | _pImpl->release(); |
69 | } |
70 | |
71 | |
72 | int 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 | |