2 * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 * @file generic_socket.h
18 * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com)
20 * @brief This file is the header file of generic socket
22 #ifndef DPL_GENERIC_SOCKET_H
23 #define DPL_GENERIC_SOCKET_H
25 #include <dpl/socket/abstract_socket.h>
26 #include <dpl/waitable_handle_watch_support.h>
27 #include <dpl/binary_queue.h>
28 #include <dpl/thread.h>
30 #include <dpl/log/wrt_log.h>
31 #include <dpl/free_deleter.h>
33 #include <sys/types.h>
34 #include <sys/socket.h>
37 #include <dpl/assert.h>
42 // Generic abstract socket implementation
43 // Execution context is inherited
45 template<typename SocketType>
47 public AbstractSocket,
48 private WaitableHandleWatchSupport::WaitableHandleListener
52 * Translate generic Address to specific socket kernel structure
54 * @warning Must be implemented in derived class
56 virtual std::pair<sockaddr *, socklen_t> TranslateAddressGenericToSpecific(
57 const Address &address) const = 0;
60 * Translate specific socket kernel structure to generic Address
62 * @warning Must be implemented in derived class
64 virtual Address TranslateAddressSpecificToGeneric(sockaddr *,
68 * Get specific socket kernel structure size
70 * @warning Must be implemented in derived class
72 virtual socklen_t GetSpecificAddressSize() const = 0;
75 * Alloc specific implementation of socket from descriptor
77 * @warning Must be implemented in derived class
79 virtual SocketType *AllocAcceptedSpecificSocket() const = 0;
82 * Alloc specific implementation of socket descriptor
84 * @warning Must be implemented in derived class
86 virtual int AllocSpecificDescriptor() const = 0;
90 static const size_t DEFAULT_READ_BUFFER_SIZE = 4096;
93 int m_socket; // FIXME: Consider generalization to WaitableHandle upon
94 // leaving nix platform
99 InternalState_None, ///< Not connected and not listening state
100 InternalState_Prepare, ///< Descriptor allocated, but not connected
101 InternalState_Listening, ///< Listening state
102 InternalState_Connecting, ///< Connecting state
103 InternalState_Connected ///< Connected state
106 InternalState m_internalState;
108 void SetNonBlocking()
110 // Set non-blocking mode
111 if (fcntl(m_socket, F_SETFL, O_NONBLOCK |
112 fcntl(m_socket, F_GETFL)) == -1)
114 Throw(AbstractSocket::Exception::SetNonBlockingFailed);
118 // WaitableHandleWatchSupport::WaitableHandleListener
119 virtual void OnWaitableHandleEvent(WaitableHandle waitableHandle,
122 (void)waitableHandle;
123 Assert(waitableHandle == m_socket);
125 switch (m_internalState) {
126 case InternalState_None:
129 case InternalState_Prepare:
130 Assert(0 && "Invalid internal generic socket state!");
133 case InternalState_Listening:
134 Assert(mode == WaitMode::Read);
136 // New client waiting for accept
137 DPL::Event::EventSupport<AbstractSocketEvents::AcceptEvent>::
138 EmitEvent(AbstractSocketEvents::AcceptEvent(
139 EventSender(this)), DPL::Event::EmitMode::Queued);
144 case InternalState_Connecting:
145 Assert(mode == WaitMode::Write);
147 // Connected to server
148 RemoveConnectWatch();
149 m_internalState = InternalState_Connected;
155 DPL::Event::EventSupport<AbstractSocketEvents::ConnectedEvent>::
156 EmitEvent(AbstractSocketEvents::ConnectedEvent(
157 EventSender(this)), DPL::Event::EmitMode::Queued);
162 case InternalState_Connected:
163 if (mode == WaitMode::Read) {
165 DPL::Event::EventSupport<AbstractSocketEvents::ReadEvent>::
166 EmitEvent(AbstractSocketEvents::ReadEvent(
168 this)), DPL::Event::EmitMode::Queued);
169 } else if (mode == WaitMode::Write) {
171 DPL::Event::EventSupport<AbstractSocketEvents::WriteEvent>::
172 EmitEvent(AbstractSocketEvents::WriteEvent(
174 this)), DPL::Event::EmitMode::Queued);
187 void AddAcceptWatch()
189 WaitableHandleWatchSupport * inheritedContext =
190 WaitableHandleWatchSupport::InheritedContext();
193 inheritedContext->AddWaitableHandleWatch(
199 void RemoveAcceptWatch()
201 WaitableHandleWatchSupport * inheritedContext =
202 WaitableHandleWatchSupport::InheritedContext();
205 inheritedContext->RemoveWaitableHandleWatch(this, m_socket, WaitMode::Read);
208 void AddConnectWatch()
210 WaitableHandleWatchSupport * inheritedContext =
211 WaitableHandleWatchSupport::InheritedContext();
214 inheritedContext->AddWaitableHandleWatch(
220 void RemoveConnectWatch()
222 WaitableHandleWatchSupport * inheritedContext =
223 WaitableHandleWatchSupport::InheritedContext();
226 inheritedContext->RemoveWaitableHandleWatch(this, m_socket, WaitMode::Write);
231 WaitableHandleWatchSupport * inheritedContext =
232 WaitableHandleWatchSupport::InheritedContext();
235 inheritedContext->AddWaitableHandleWatch(
241 void RemoveReadWatch()
243 WaitableHandleWatchSupport * inheritedContext =
244 WaitableHandleWatchSupport::InheritedContext();
247 inheritedContext->RemoveWaitableHandleWatch(this, m_socket, WaitMode::Read);
252 WaitableHandleWatchSupport * inheritedContext =
253 WaitableHandleWatchSupport::InheritedContext();
256 inheritedContext->AddWaitableHandleWatch(
262 void RemoveWriteWatch()
264 WaitableHandleWatchSupport * inheritedContext =
265 WaitableHandleWatchSupport::InheritedContext();
268 inheritedContext->RemoveWaitableHandleWatch(this, m_socket, WaitMode::Write);
274 m_internalState(InternalState_None)
277 virtual ~GenericSocket()
279 // Always ensure to close socket
284 Catch(Exception::CloseFailed)
286 WrtLogD("Close failed and ignored");
290 Assert(m_socket == -1);
295 if (m_internalState != InternalState_None) {
296 ThrowMsg(AbstractSocket::Exception::OpenFailed,
297 "Invalid socket state, should be 'None'");
300 WrtLogD("Opening socket...");
303 Assert(m_socket == -1);
305 // Allocate specific socket descriptor
306 m_socket = AllocSpecificDescriptor();
308 // Set socket non-blocking
312 m_internalState = InternalState_Prepare;
314 WrtLogD("Socket opened");
317 virtual void Connect(const Address &address)
319 if (m_internalState != InternalState_Prepare) {
320 ThrowMsg(AbstractSocket::Exception::ConnectFailed,
321 "Invalid socket state, should be 'Prepare'");
324 WrtLogD("Connecting to: %s", address.ToString().c_str());
326 // Try to convert address
327 std::pair<sockaddr *, socklen_t> socketAddress;
331 // Translate address to specific socket address struct
332 socketAddress = TranslateAddressGenericToSpecific(address);
334 Catch(Address::Exception::InvalidAddress)
336 // This address is invalid. Cannot connect.
337 ReThrowMsg(AbstractSocket::Exception::ConnectFailed,
343 TEMP_FAILURE_RETRY(connect(m_socket, socketAddress.first,
344 socketAddress.second));
348 WrtLogD("Immediate connected to: %s", address.ToString().c_str());
352 m_internalState = InternalState_Connected;
354 // Emit connected event
355 DPL::Event::EventSupport<AbstractSocketEvents::ConnectedEvent>::
356 EmitEvent(AbstractSocketEvents::ConnectedEvent(
357 EventSender(this)), DPL::Event::EmitMode::Queued);
359 if (errno == EINTR || errno == EINPROGRESS) {
361 "Asynchronous connect in progress: %s", address.ToString().c_str());
363 // Connecting in progress
365 m_internalState = InternalState_Connecting;
367 // Free translation structure
368 free(socketAddress.first);
371 ThrowMsg(AbstractSocket::Exception::ConnectFailed,
376 // Free translation structure
377 free(socketAddress.first);
382 if (m_internalState == InternalState_None) {
386 Assert(m_socket != -1);
388 if (m_internalState == InternalState_Listening) {
389 // Remove watch in listening state
390 WrtLogD("Removing accept watch");
392 m_internalState = InternalState_None;
393 } else if (m_internalState == InternalState_Connecting) {
394 // Remove watch in connecting state
395 WrtLogD("Removing connect watch");
396 RemoveConnectWatch();
397 m_internalState = InternalState_None;
398 } else if (m_internalState == InternalState_Connected) {
399 // Remove watch in connected state
400 WrtLogD("Removing read watch");
402 m_internalState = InternalState_None;
404 // State must be just prepared only
405 Assert(m_internalState == InternalState_Prepare);
408 if (TEMP_FAILURE_RETRY(close(m_socket)) == -1) {
409 Throw(Exception::CloseFailed);
415 // Reset socket state
416 m_internalState = InternalState_None;
418 WrtLogD("Socket closed");
421 virtual void Bind(const Address &address)
423 if (m_internalState != InternalState_Prepare) {
424 ThrowMsg(AbstractSocket::Exception::BindFailed,
425 "Invalid socket state, should be 'Prepare'");
428 WrtLogD("Binding to: %s : %i",
429 address.GetAddress().c_str(), address.GetPort());
431 // Translate address to specific socket address struct
432 std::pair<sockaddr *,
433 socklen_t> socketAddress = TranslateAddressGenericToSpecific(
437 if (::bind(m_socket, socketAddress.first,
438 socketAddress.second) == -1)
440 ThrowMsg(AbstractSocket::Exception::BindFailed, address.ToString());
443 // Free translation structure
444 free(socketAddress.first);
447 WrtLogD("Bound to address: %s : %i",
448 address.GetAddress().c_str(), address.GetPort());
451 virtual void Listen(int backlog)
453 if (m_internalState != InternalState_Prepare) {
454 ThrowMsg(AbstractSocket::Exception::ListenFailed,
455 "Invalid socket state, should be 'None'");
458 WrtLogD("Starting to listen...");
461 if (listen(m_socket, backlog) != 0) {
462 Throw(AbstractSocket::Exception::ListenFailed);
467 m_internalState = InternalState_Listening;
469 WrtLogD("Listen started");
472 virtual AbstractSocket *Accept()
474 if (m_internalState != InternalState_Listening) {
475 ThrowMsg(AbstractSocket::Exception::AcceptFailed,
476 "Invalid socket state, should be 'Listening'");
479 WrtLogD("Accepting...");
482 socklen_t length = 0;
483 int client = TEMP_FAILURE_RETRY(accept(m_socket, NULL, &length));
485 WrtLogD("Socket accept returned %i", client);
487 // Check if there is any client waiting
488 if (errno == EWOULDBLOCK || errno == EAGAIN) {
492 if (errno == ENOENT) {
495 WrtLogD("throwing error. errrno %i", err);
497 Throw(AbstractSocket::Exception::AcceptFailed);
500 WrtLogD("Accepted client. Seting up...");
502 // Create derived class type
503 GenericSocket *acceptedSocket = AllocAcceptedSpecificSocket();
505 // Save client socket specific descriptor
506 acceptedSocket->m_socket = client;
508 // Enter proper states and add read watch
509 acceptedSocket->AddReadWatch();
510 acceptedSocket->m_internalState = InternalState_Connected;
512 // Set non-blocking mode for new socket
513 acceptedSocket->SetNonBlocking();
516 WrtLogD("Accepted client set up");
518 // return new conneced client socket
519 return acceptedSocket;
522 virtual Address GetLocalAddress() const
524 // FIXME: Additional internal state check
526 socklen_t length = GetSpecificAddressSize();
527 std::unique_ptr<sockaddr,free_deleter> address(static_cast<sockaddr *>(calloc(
533 if (getsockname(m_socket, address.get(), &length) == -1) {
534 ThrowMsg(AbstractSocket::Exception::GetPeerNameFailed,
535 "Failed to get local address");
538 return TranslateAddressSpecificToGeneric(address.get(), length);
541 virtual Address GetRemoteAddress() const
543 // FIXME: Additional internal state check
545 socklen_t length = GetSpecificAddressSize();
546 std::unique_ptr<sockaddr,free_deleter> address(static_cast<sockaddr *>(calloc(
552 if (getpeername(m_socket, address.get(), &length) == -1) {
553 ThrowMsg(AbstractSocket::Exception::GetPeerNameFailed,
554 "Failed to get remote address");
557 return TranslateAddressSpecificToGeneric(address.get(), length);
560 virtual BinaryQueueAutoPtr Read(size_t size)
562 if (m_internalState != InternalState_Connected) {
563 ThrowMsg(AbstractSocket::Exception::AcceptFailed,
564 "Invalid socket state, should be 'Connected'");
569 // Adjust bytes to be read
570 size_t bytesToRead = size >
571 DEFAULT_READ_BUFFER_SIZE ? DEFAULT_READ_BUFFER_SIZE : size;
573 // Malloc default read buffer size
574 // It is unmanaged, so it can be then attached directly to binary
576 void *buffer = malloc(bytesToRead);
578 if (buffer == NULL) {
579 throw std::bad_alloc();
582 // Receive bytes from socket
584 TEMP_FAILURE_RETRY(recv(m_socket, buffer, bytesToRead, 0));
587 // Succedded to read socket data
588 BinaryQueueAutoPtr binaryQueue(new BinaryQueue());
590 // Append unmanaged memory
591 binaryQueue->AppendUnmanaged(buffer,
593 &BinaryQueue::BufferDeleterFree,
598 } else if (result == 0) {
599 // Socket was gracefuly closed
602 // Return empty buffer
603 return BinaryQueueAutoPtr(new BinaryQueue());
605 // Must first save errno value, because it may be altered
606 int lastErrno = errno;
611 // Interpret error result
613 case EAGAIN: // = EWOULDBLOCK
615 // * The socket's file descriptor is marked O_NONBLOCK and
616 // no data is waiting
617 // to be received; or MSG_OOB is set and no out-of-band
619 // and either the socket's file descriptor is marked
620 // O_NONBLOCK or the socket
621 // does not support blocking to await out-of-band data.
623 // return null data buffer to indicate no data waiting
625 return BinaryQueueAutoPtr();
629 // * The socket argument is not a valid file descriptor.
631 // This is internal error
633 ThrowMsg(CommonException::InternalError,
634 "Invalid socket descriptor");
638 // * A connection was forcibly closed by a peer.
640 // In result, we can interpret this error as a broken
643 Throw(AbstractSocket::Exception::ConnectionBroken);
647 // * The recv() function was interrupted by a signal that
648 // was caught, before any
649 // data was available.
651 // No interrupt here is expected, due to fact that we used
652 // TEMP_FAILURE_RETRY
654 ThrowMsg(CommonException::InternalError,
655 "Unexpected interrupt occurred");
659 // * The MSG_OOB flag is set and no out-of-band data is
662 // We did not specified OOB data. This is an error.
664 ThrowMsg(CommonException::InternalError,
665 "Unexpected OOB error occurred");
669 // * A receive is attempted on a connection-mode socket that
672 // FIXME: Is this proper exception here ?
674 ThrowMsg(CommonException::InternalError,
675 "Socket is not connected");
679 // * The socket argument does not refer to a socket.
681 ThrowMsg(CommonException::InternalError,
682 "Handle is not a socket");
686 // * The specified flags are not supported for this socket
689 ThrowMsg(CommonException::InternalError,
690 "Socket flags not supported");
694 // * The connection timed out during connection
695 // establishment, or due to a transmission timeout on active
698 // In result, we can interpret this error as a broken
701 Throw(AbstractSocket::Exception::ConnectionBroken);
705 // * An I/O error occurred while reading from or writing to
708 // In result, we can interpret this error as a broken
711 Throw(AbstractSocket::Exception::ConnectionBroken);
715 // * Insufficient resources were available in the system to
716 // perform the operation.
718 ThrowMsg(CommonException::InternalError,
719 "Insufficient system resources");
723 // * Insufficient memory was available to fulfill the
726 ThrowMsg(CommonException::InternalError,
727 "Insufficient system memory");
730 // Some kernel error occurred, should never happen
731 ThrowMsg(CommonException::InternalError,
732 "Unknown kernel read error returned");
737 Catch(CommonException::InternalError)
739 // If any internal error occurred, this is fatal for Write method
740 // interpret this as WriteError exception and rethrow
741 ReThrow(AbstractSocket::Exception::ReadFailed);
745 virtual size_t Write(const BinaryQueue &buffer, size_t bufferSize)
747 if (m_internalState != InternalState_Connected) {
748 ThrowMsg(AbstractSocket::Exception::AcceptFailed,
749 "Invalid socket state, should be 'Connected'");
755 if (bufferSize > buffer.Size()) {
756 bufferSize = buffer.Size();
759 // FIXME: User write visitor to write !
760 // WriteVisitor visitor
762 std::unique_ptr<void,free_deleter> flattened(malloc(bufferSize));
763 buffer.Flatten(flattened.get(), bufferSize);
765 // Linux: MSG_NOSIGNAL is supported, but it is not an ideal solution
766 // FIXME: Should we setup signal PIPE ignoring for whole process ?
767 // In BSD, there is: setsockopt(c, SOL_SOCKET, SO_NOSIGPIPE, (void
768 // *)&on, sizeof(on))
770 TEMP_FAILURE_RETRY(send(m_socket, flattened.get(), bufferSize,
774 // Successfuly written some bytes
775 return static_cast<size_t>(result);
776 } else if (result == 0) {
777 // This is abnormal result
778 ThrowMsg(CommonException::InternalError,
779 "Invalid socket write result, 0 bytes written");
780 } else if (result == -1) {
781 // Interpret error result
783 case EAGAIN: // = EWOULDBLOCK
785 // * The socket's file descriptor is marked O_NONBLOCK and
786 // the requested operation would block.
788 // We should wait for writability
794 // * The socket argument is not a valid file descriptor.
796 // This is internal error
798 ThrowMsg(CommonException::InternalError,
799 "Invalid socket descriptor");
803 // * A connection was forcibly closed by a peer.
805 // In result, we can interpret this error as a broken
808 Throw(AbstractSocket::Exception::ConnectionBroken);
812 // * The socket is not connection-mode and no peer address
815 // FIXME: Is this proper exception here ?
817 ThrowMsg(CommonException::InternalError,
818 "Socket is not connected");
822 // * A signal interrupted send() before any data was
825 // No interrupt here is expected, due to fact that we used
826 // TEMP_FAILURE_RETRY
828 ThrowMsg(CommonException::InternalError,
829 "Unexpected interrupt occurred");
833 // * The message is too large to be sent all at once, as the
836 // FIXME: Is this proper exception here ?
838 ThrowMsg(CommonException::InternalError,
839 "Socket message is too big");
843 // * The socket is not connected or otherwise has not had
844 // the peer pre-specified.
846 // FIXME: Is this proper exception here ?
848 ThrowMsg(CommonException::InternalError,
849 "Socket is not connected");
853 // * The socket argument does not refer to a socket.
855 ThrowMsg(CommonException::InternalError,
856 "Handle is not a socket");
860 // * The socket argument is associated with a socket that
861 // does not support one or more of the values set in flags.
863 ThrowMsg(CommonException::InternalError,
864 "Socket flags not supported");
868 // * The socket is shut down for writing, or the socket is
869 // connection-mode and
870 // is no longer connected. In the latter case, and if the
872 // SOCK_STREAM, the SIGPIPE signal is generated to the
875 // In result, we can interpret this error as a broken
878 Throw(AbstractSocket::Exception::ConnectionBroken);
882 // * The calling process does not have the appropriate
885 // Priviledges might have changed.
886 // In result, we can interpret this error as a broken
889 Throw(AbstractSocket::Exception::ConnectionBroken);
893 // * An I/O error occurred while reading from or writing to
896 // In result, we can interpret this error as a broken
899 Throw(AbstractSocket::Exception::ConnectionBroken);
903 // * The local network interface used to reach the
904 // destination is down.
906 // In result, we can interpret this error as a broken
909 Throw(AbstractSocket::Exception::ConnectionBroken);
913 // * No route to the network is present.
915 // In result, we can interpret this error as a broken
918 Throw(AbstractSocket::Exception::ConnectionBroken);
922 // * Insufficient resources were available in the system to
923 // perform the operation.
925 ThrowMsg(CommonException::InternalError,
926 "Insufficient system resources");
929 // Some kernel error occurred, should never happen
930 ThrowMsg(CommonException::InternalError,
931 "Unknown kernel write error returned");
936 Catch(CommonException::InternalError)
938 // If any internal error occurred, this is fatal for Write method
939 // interpret this as WriteError exception and rethrow
940 ReThrow(AbstractSocket::Exception::WriteFailed);
947 // AbstractWaitableInput
948 virtual WaitableHandle WaitableReadHandle() const
953 // AbstractWaitableOutput
954 virtual WaitableHandle WaitableWriteHandle() const
962 #endif // DPL_GENERIC_SOCKET_H