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 <poll.h> |
24 | #endif |
25 | |
26 | |
27 | namespace Poco { |
28 | namespace Net { |
29 | |
30 | |
31 | Socket::Socket(): |
32 | _pImpl(new StreamSocketImpl) |
33 | { |
34 | } |
35 | |
36 | |
37 | Socket::Socket(SocketImpl* pImpl): |
38 | _pImpl(pImpl) |
39 | { |
40 | poco_check_ptr (_pImpl); |
41 | } |
42 | |
43 | |
44 | Socket::Socket(const Socket& socket): |
45 | _pImpl(socket._pImpl) |
46 | { |
47 | poco_check_ptr (_pImpl); |
48 | |
49 | _pImpl->duplicate(); |
50 | } |
51 | |
52 | |
53 | Socket& Socket::operator = (const Socket& socket) |
54 | { |
55 | if (&socket != this) |
56 | { |
57 | if (_pImpl) _pImpl->release(); |
58 | _pImpl = socket._pImpl; |
59 | if (_pImpl) _pImpl->duplicate(); |
60 | } |
61 | return *this; |
62 | } |
63 | |
64 | |
65 | Socket::~Socket() |
66 | { |
67 | _pImpl->release(); |
68 | } |
69 | |
70 | |
71 | int Socket::select(SocketList& readList, SocketList& writeList, SocketList& exceptList, const Poco::Timespan& timeout) |
72 | { |
73 | #if defined(POCO_HAVE_FD_EPOLL) |
74 | |
75 | int epollSize = readList.size() + writeList.size() + exceptList.size(); |
76 | if (epollSize == 0) return 0; |
77 | |
78 | int epollfd = -1; |
79 | { |
80 | struct epoll_event eventsIn[epollSize]; |
81 | memset(eventsIn, 0, sizeof(epoll_event) * epollSize ); |
82 | |
83 | struct epoll_event* eventLast = eventsIn; |
84 | for (SocketList::iterator it = readList.begin(); it != readList.end(); ++it) |
85 | { |
86 | poco_socket_t sockfd = it->sockfd(); |
87 | if (sockfd != POCO_INVALID_SOCKET) |
88 | { |
89 | struct epoll_event* e = eventsIn; |
90 | for (; e != eventLast; ++e) |
91 | { |
92 | if (reinterpret_cast<Socket*>(e->data.ptr)->sockfd() == sockfd) |
93 | break; |
94 | } |
95 | if (e == eventLast) |
96 | { |
97 | e->data.ptr = &(*it); |
98 | ++eventLast; |
99 | } |
100 | e->events |= EPOLLIN; |
101 | } |
102 | } |
103 | |
104 | for (SocketList::iterator it = writeList.begin(); it != writeList.end(); ++it) |
105 | { |
106 | poco_socket_t sockfd = it->sockfd(); |
107 | if (sockfd != POCO_INVALID_SOCKET) |
108 | { |
109 | struct epoll_event* e = eventsIn; |
110 | for (; e != eventLast; ++e) |
111 | { |
112 | if (reinterpret_cast<Socket*>(e->data.ptr)->sockfd() == sockfd) |
113 | break; |
114 | } |
115 | if (e == eventLast) |
116 | { |
117 | e->data.ptr = &(*it); |
118 | ++eventLast; |
119 | } |
120 | e->events |= EPOLLOUT; |
121 | } |
122 | } |
123 | |
124 | for (SocketList::iterator it = exceptList.begin(); it != exceptList.end(); ++it) |
125 | { |
126 | poco_socket_t sockfd = it->sockfd(); |
127 | if (sockfd != POCO_INVALID_SOCKET) |
128 | { |
129 | struct epoll_event* e = eventsIn; |
130 | for (; e != eventLast; ++e) |
131 | { |
132 | if (reinterpret_cast<Socket*>(e->data.ptr)->sockfd() == sockfd) |
133 | break; |
134 | } |
135 | if (e == eventLast) |
136 | { |
137 | e->data.ptr = &(*it); |
138 | ++eventLast; |
139 | } |
140 | e->events |= EPOLLERR; |
141 | } |
142 | } |
143 | |
144 | epollSize = eventLast - eventsIn; |
145 | if (epollSize == 0) return 0; |
146 | |
147 | epollfd = epoll_create(1); |
148 | if (epollfd < 0) |
149 | { |
150 | SocketImpl::error("Can't create epoll queue" ); |
151 | } |
152 | |
153 | for (struct epoll_event* e = eventsIn; e != eventLast; ++e) |
154 | { |
155 | poco_socket_t sockfd = reinterpret_cast<Socket*>(e->data.ptr)->sockfd(); |
156 | if (sockfd != POCO_INVALID_SOCKET) |
157 | { |
158 | if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, e) < 0) |
159 | { |
160 | ::close(epollfd); |
161 | SocketImpl::error("Can't insert socket to epoll queue" ); |
162 | } |
163 | } |
164 | } |
165 | } |
166 | |
167 | struct epoll_event eventsOut[epollSize]; |
168 | memset(eventsOut, 0, sizeof(epoll_event) * epollSize ); |
169 | |
170 | Poco::Timespan remainingTime(timeout); |
171 | int rc; |
172 | do |
173 | { |
174 | Poco::Timestamp start; |
175 | rc = epoll_wait(epollfd, eventsOut, epollSize, remainingTime.totalMilliseconds()); |
176 | if (rc < 0 && SocketImpl::lastError() == POCO_EINTR) |
177 | { |
178 | Poco::Timestamp end; |
179 | Poco::Timespan waited = end - start; |
180 | if (waited < remainingTime) |
181 | remainingTime -= waited; |
182 | else |
183 | remainingTime = 0; |
184 | } |
185 | } |
186 | while (rc < 0 && SocketImpl::lastError() == POCO_EINTR); |
187 | |
188 | ::close(epollfd); |
189 | if (rc < 0) SocketImpl::error(); |
190 | |
191 | SocketList readyReadList; |
192 | SocketList readyWriteList; |
193 | SocketList readyExceptList; |
194 | for (int n = 0; n < rc; ++n) |
195 | { |
196 | if (eventsOut[n].events & EPOLLERR) |
197 | readyExceptList.push_back(*reinterpret_cast<Socket*>(eventsOut[n].data.ptr)); |
198 | if (eventsOut[n].events & EPOLLIN) |
199 | readyReadList.push_back(*reinterpret_cast<Socket*>(eventsOut[n].data.ptr)); |
200 | if (eventsOut[n].events & EPOLLOUT) |
201 | readyWriteList.push_back(*reinterpret_cast<Socket*>(eventsOut[n].data.ptr)); |
202 | } |
203 | std::swap(readList, readyReadList); |
204 | std::swap(writeList, readyWriteList); |
205 | std::swap(exceptList, readyExceptList); |
206 | return readList.size() + writeList.size() + exceptList.size(); |
207 | |
208 | #elif defined(POCO_HAVE_FD_POLL) |
209 | nfds_t nfd = readList.size() + writeList.size() + exceptList.size(); |
210 | if (0 == nfd) return 0; |
211 | |
212 | std::unique_ptr<pollfd[]> pPollArr(new pollfd[nfd]()); |
213 | |
214 | int idx = 0; |
215 | for (SocketList::iterator it = readList.begin(); it != readList.end(); ++it) |
216 | { |
217 | pPollArr[idx].fd = int(it->sockfd()); |
218 | pPollArr[idx++].events |= POLLIN; |
219 | } |
220 | |
221 | SocketList::iterator begR = readList.begin(); |
222 | SocketList::iterator endR = readList.end(); |
223 | for (SocketList::iterator it = writeList.begin(); it != writeList.end(); ++it) |
224 | { |
225 | SocketList::iterator pos = std::find(begR, endR, *it); |
226 | if (pos != endR) |
227 | { |
228 | pPollArr[pos-begR].events |= POLLOUT; |
229 | --nfd; |
230 | } |
231 | else |
232 | { |
233 | pPollArr[idx].fd = int(it->sockfd()); |
234 | pPollArr[idx++].events |= POLLOUT; |
235 | } |
236 | } |
237 | |
238 | SocketList::iterator begW = writeList.begin(); |
239 | SocketList::iterator endW = writeList.end(); |
240 | for (SocketList::iterator it = exceptList.begin(); it != exceptList.end(); ++it) |
241 | { |
242 | SocketList::iterator pos = std::find(begR, endR, *it); |
243 | if (pos != endR) --nfd; |
244 | else |
245 | { |
246 | SocketList::iterator pos = std::find(begW, endW, *it); |
247 | if (pos != endW) --nfd; |
248 | else pPollArr[idx++].fd = int(it->sockfd()); |
249 | } |
250 | } |
251 | |
252 | Poco::Timespan remainingTime(timeout); |
253 | int rc; |
254 | do |
255 | { |
256 | Poco::Timestamp start; |
257 | rc = ::poll(pPollArr.get(), nfd, remainingTime.totalMilliseconds()); |
258 | if (rc < 0 && SocketImpl::lastError() == POCO_EINTR) |
259 | { |
260 | Poco::Timestamp end; |
261 | Poco::Timespan waited = end - start; |
262 | if (waited < remainingTime) remainingTime -= waited; |
263 | else remainingTime = 0; |
264 | } |
265 | } |
266 | while (rc < 0 && SocketImpl::lastError() == POCO_EINTR); |
267 | if (rc < 0) SocketImpl::error(); |
268 | |
269 | SocketList readyReadList; |
270 | SocketList readyWriteList; |
271 | SocketList readyExceptList; |
272 | |
273 | SocketList::iterator begE = exceptList.begin(); |
274 | SocketList::iterator endE = exceptList.end(); |
275 | for (int idx = 0; idx < nfd; ++idx) |
276 | { |
277 | SocketList::iterator slIt = std::find_if(begR, endR, Socket::FDCompare(pPollArr[idx].fd)); |
278 | if (POLLIN & pPollArr[idx].revents && slIt != endR) readyReadList.push_back(*slIt); |
279 | slIt = std::find_if(begW, endW, Socket::FDCompare(pPollArr[idx].fd)); |
280 | if (POLLOUT & pPollArr[idx].revents && slIt != endW) readyWriteList.push_back(*slIt); |
281 | slIt = std::find_if(begE, endE, Socket::FDCompare(pPollArr[idx].fd)); |
282 | if (POLLERR & pPollArr[idx].revents && slIt != endE) readyExceptList.push_back(*slIt); |
283 | } |
284 | std::swap(readList, readyReadList); |
285 | std::swap(writeList, readyWriteList); |
286 | std::swap(exceptList, readyExceptList); |
287 | return readList.size() + writeList.size() + exceptList.size(); |
288 | |
289 | #else |
290 | |
291 | fd_set fdRead; |
292 | fd_set fdWrite; |
293 | fd_set fdExcept; |
294 | int nfd = 0; |
295 | FD_ZERO(&fdRead); |
296 | for (SocketList::const_iterator it = readList.begin(); it != readList.end(); ++it) |
297 | { |
298 | poco_socket_t fd = it->sockfd(); |
299 | if (fd != POCO_INVALID_SOCKET) |
300 | { |
301 | if (int(fd) > nfd) |
302 | nfd = int(fd); |
303 | FD_SET(fd, &fdRead); |
304 | } |
305 | } |
306 | FD_ZERO(&fdWrite); |
307 | for (SocketList::const_iterator it = writeList.begin(); it != writeList.end(); ++it) |
308 | { |
309 | poco_socket_t fd = it->sockfd(); |
310 | if (fd != POCO_INVALID_SOCKET) |
311 | { |
312 | if (int(fd) > nfd) |
313 | nfd = int(fd); |
314 | FD_SET(fd, &fdWrite); |
315 | } |
316 | } |
317 | FD_ZERO(&fdExcept); |
318 | for (SocketList::const_iterator it = exceptList.begin(); it != exceptList.end(); ++it) |
319 | { |
320 | poco_socket_t fd = it->sockfd(); |
321 | if (fd != POCO_INVALID_SOCKET) |
322 | { |
323 | if (int(fd) > nfd) |
324 | nfd = int(fd); |
325 | FD_SET(fd, &fdExcept); |
326 | } |
327 | } |
328 | if (nfd == 0) return 0; |
329 | Poco::Timespan remainingTime(timeout); |
330 | int rc; |
331 | do |
332 | { |
333 | struct timeval tv; |
334 | tv.tv_sec = (long) remainingTime.totalSeconds(); |
335 | tv.tv_usec = (long) remainingTime.useconds(); |
336 | Poco::Timestamp start; |
337 | rc = ::select(nfd + 1, &fdRead, &fdWrite, &fdExcept, &tv); |
338 | if (rc < 0 && SocketImpl::lastError() == POCO_EINTR) |
339 | { |
340 | Poco::Timestamp end; |
341 | Poco::Timespan waited = end - start; |
342 | if (waited < remainingTime) |
343 | remainingTime -= waited; |
344 | else |
345 | remainingTime = 0; |
346 | } |
347 | } |
348 | while (rc < 0 && SocketImpl::lastError() == POCO_EINTR); |
349 | if (rc < 0) SocketImpl::error(); |
350 | |
351 | SocketList readyReadList; |
352 | for (SocketList::const_iterator it = readList.begin(); it != readList.end(); ++it) |
353 | { |
354 | poco_socket_t fd = it->sockfd(); |
355 | if (fd != POCO_INVALID_SOCKET) |
356 | { |
357 | if (FD_ISSET(fd, &fdRead)) |
358 | readyReadList.push_back(*it); |
359 | } |
360 | } |
361 | std::swap(readList, readyReadList); |
362 | SocketList readyWriteList; |
363 | for (SocketList::const_iterator it = writeList.begin(); it != writeList.end(); ++it) |
364 | { |
365 | poco_socket_t fd = it->sockfd(); |
366 | if (fd != POCO_INVALID_SOCKET) |
367 | { |
368 | if (FD_ISSET(fd, &fdWrite)) |
369 | readyWriteList.push_back(*it); |
370 | } |
371 | } |
372 | std::swap(writeList, readyWriteList); |
373 | SocketList readyExceptList; |
374 | for (SocketList::const_iterator it = exceptList.begin(); it != exceptList.end(); ++it) |
375 | { |
376 | poco_socket_t fd = it->sockfd(); |
377 | if (fd != POCO_INVALID_SOCKET) |
378 | { |
379 | if (FD_ISSET(fd, &fdExcept)) |
380 | readyExceptList.push_back(*it); |
381 | } |
382 | } |
383 | std::swap(exceptList, readyExceptList); |
384 | return rc; |
385 | |
386 | #endif // POCO_HAVE_FD_EPOLL |
387 | } |
388 | |
389 | |
390 | SocketBufVec Socket::makeBufVec(std::size_t size, std::size_t bufLen) |
391 | { |
392 | SocketBufVec buf(size); |
393 | SocketBufVec::iterator it = buf.begin(); |
394 | SocketBufVec::iterator end = buf.end(); |
395 | for (; it != end; ++it) |
396 | { |
397 | // TODO: use memory pool |
398 | *it = makeBuffer(malloc(bufLen), bufLen); |
399 | } |
400 | return buf; |
401 | } |
402 | |
403 | |
404 | void Socket::destroyBufVec(SocketBufVec& buf) |
405 | { |
406 | SocketBufVec::iterator it = buf.begin(); |
407 | SocketBufVec::iterator end = buf.end(); |
408 | for (; it != end; ++it) |
409 | { |
410 | #if defined(POCO_OS_FAMILY_WINDOWS) |
411 | free(it->buf); |
412 | #elif defined(POCO_OS_FAMILY_UNIX) |
413 | free(it->iov_base); |
414 | #endif |
415 | } |
416 | SocketBufVec().swap(buf); |
417 | } |
418 | |
419 | |
420 | SocketBuf Socket::makeBuffer(void* buffer, std::size_t length) |
421 | { |
422 | SocketBuf ret; |
423 | #if defined(POCO_OS_FAMILY_WINDOWS) |
424 | ret.buf = reinterpret_cast<char*>(buffer); |
425 | ret.len = static_cast<ULONG>(length); |
426 | #elif defined(POCO_OS_FAMILY_UNIX) |
427 | ret.iov_base = buffer; |
428 | ret.iov_len = length; |
429 | #else |
430 | throw NotImplementedException("Socket::makeBuffer(void*, size_t)" ); |
431 | #endif |
432 | return ret; |
433 | } |
434 | |
435 | |
436 | SocketBufVec Socket::makeBufVec(const std::vector<char*>& vec) |
437 | { |
438 | SocketBufVec buf(vec.size()); |
439 | SocketBufVec::iterator it = buf.begin(); |
440 | SocketBufVec::iterator end = buf.end(); |
441 | std::vector<char*>::const_iterator vIt = vec.begin(); |
442 | for (; it != end; ++it, ++vIt) |
443 | { |
444 | *it = makeBuffer(*vIt, strlen(*vIt)); |
445 | } |
446 | return buf; |
447 | } |
448 | |
449 | |
450 | SocketBufVec Socket::makeBufVec(const std::vector<std::string>& vec) |
451 | { |
452 | SocketBufVec buf(vec.size()); |
453 | SocketBufVec::iterator it = buf.begin(); |
454 | SocketBufVec::iterator end = buf.end(); |
455 | std::vector<std::string>::const_iterator vIt = vec.begin(); |
456 | for (; it != end; ++it, ++vIt) |
457 | { |
458 | char* c = const_cast<char*>(vIt->data()); |
459 | *it = makeBuffer(reinterpret_cast<void*>(c), vIt->size()); |
460 | } |
461 | return buf; |
462 | } |
463 | |
464 | |
465 | } } // namespace Poco::Net |
466 | |