1 #ifndef POSIXX_SOCKET_BASIC_SOCKET_HPP_
2 #define POSIXX_SOCKET_BASIC_SOCKET_HPP_
4 #include "../error.hpp" // posixx::error
5 #include "../static_assert.hpp" // static_assert
7 #include <string> // std::string
8 #include <utility> // std::pair
9 #include <sys/socket.h> // socket, send, recv, etc.
13 /// POSIX thin wrapper
28 * Connectionless, unreliable messages of a fixed maximum
33 * Reliable datagram layer that does not guarantee ordering.
37 * Sequenced, reliable, two-way connection-based datagram
40 * Sequenced, reliable, two-way connection-based data
41 * transmission path for datagrams of fixed maximum length;
42 * a consumer is required to read an entire packet with each
45 SEQPACKET = SOCK_SEQPACKET,
47 * Sequenced, reliable, two-way, connection-based byte streams.
49 * An out-of-band data transmission mechanism may be supported.
53 * Raw network protocol access.
61 * @see socket::shutdown().
66 RD = SHUT_RD, ///< Further receptions will be disallowed.
67 WR = SHUT_WR, ///< Further transmissions will be disallowed.
68 RDWR = SHUT_RDWR ///< Both will be disallowed.
72 * Generic socket interface.
74 * This class is thread-safe as it just stores a file descriptor for the socket.
78 template < typename TSockTraits >
82 /// Traits used by this socket
83 typedef TSockTraits traits;
86 * Create an endpoint for communication.
88 * @param type Type of socket.
89 * @param protocol Protocol number.
91 * @see socket(2), socket(7)
93 basic_socket(type type, int protocol = 0) throw (error);
96 * Create a socket instance from a file descriptor.
98 * This is mainly used by the accept() method and pair() function and
99 * should be used with care because it takes "ownership" of the file
100 * descriptor (it's closed by the destructor).
104 * @param fd Socket file descriptor.
106 explicit basic_socket(int fd) throw ();
109 * Bind a name to the socket.
111 * @param addr Protocol-dependant name to bind on.
115 void bind(const typename TSockTraits::sockaddr& addr) throw (error);
118 * Initiate a connection on the socket.
120 * @param addr Protocol-dependant name to connect to.
124 void connect(const typename TSockTraits::sockaddr& addr) throw (error);
127 * Get the socket name.
129 * @return Protocol-dependant name binded.
131 * @see getsockname(2)
133 typename TSockTraits::sockaddr name() const throw (error);
136 * Get the name of connected peer socket.
138 * @return Protocol-dependant name of the peer.
140 * @see getpeername(2)
142 typename TSockTraits::sockaddr peer_name() const throw (error);
145 * Send a message on the socket.
147 * @param buf Message buffer.
148 * @param n Message length.
149 * @param flags Sending options.
151 * @return The number of characters sent.
153 * @see send(2), connect()
155 * @todo make an enum for the flags (are they protocol-dependant?)
157 ssize_t send(const void* buf, size_t n, int flags = 0) throw (error);
160 * Receive a message on the socket.
162 * @param buf Message buffer.
163 * @param n Maximum message length.
164 * @param flags Receiving options.
166 * @return The number of characters received.
168 * @see recv(2), connect()
170 * @todo make an enum for the flags (are they protocol-dependant?)
172 ssize_t recv(void* buf, size_t n, int flags = 0) throw (error);
175 * Send a message on the socket to a specific name.
177 * @param buf Message buffer.
178 * @param n Message length.
179 * @param to Name to send the message to.
180 * @param flags Sending options.
182 * @return The number of characters sent.
186 * @todo make an enum for the flags (are they protocol-dependant?)
188 ssize_t send(const void* buf, size_t n,
189 const typename TSockTraits::sockaddr& to,
190 int flags = 0) throw (error);
193 * Receive a message on the socket from a specific name.
195 * @param buf Message buffer.
196 * @param n Maximum message length.
197 * @param from Name to receive the message from.
198 * @param flags Receiving options.
200 * @return The number of characters received.
204 * @todo make an enum for the flags (are they protocol-dependant?)
206 ssize_t recv(void* buf, size_t n, typename TSockTraits::sockaddr& from,
207 int flags = 0) throw (error);
210 * Get options on the socket.
212 * @param level Level at which the option reside.
213 * @param optname Name of the option.
214 * @param opt Where to store the option value.
218 template< typename TSockOpt >
219 void getsockopt(int level, int optname, TSockOpt& opt) const
223 * Set options on the socket.
225 * @param level Level at which the option reside.
226 * @param optname Name of the option.
227 * @param opt New option value.
231 template< typename TSockOpt >
232 void setsockopt(int level, int optname, const TSockOpt& opt)
236 * Get option on the socket (type-safe).
238 * Note that template argument must be specified explicitly, and the
239 * template argument must be a valid socket option, which is a struct
240 * with 5 members: level, optname, read, write and type. level and
241 * optname must be the corresponding SOL_* and SO_* constants. read and
242 * write must be boolean constants indicating if the options can be
243 * read or written. type should be the type of the option's argument.
245 * See sockopt namespaces for available options.
247 * @return Option value
251 template< typename TSockOpt >
252 typename TSockOpt::type opt() const throw (error);
255 * Set option on the socket (type-safe).
257 * See getsockopt() for details on how options can be constructed.
259 * @param opt Option value
263 template< typename TSockOpt >
264 void opt(const typename TSockOpt::type& opt) throw (error);
267 * Listen for connections on the socket.
269 * @param backlog Maximum length the queue of pending
270 * connections may grow to.
272 * @see accept(), listen(2)
274 void listen(int backlog = 5) throw (error);
277 * Accept a connection on the socket.
279 * @see listen(), accept(2)
281 basic_socket* accept() throw (error);
284 * Accept a connection on the socket.
286 * @param addr Address of the peer socket, as known to the
287 * communications layer.
289 * @see listen(), accept(2)
291 basic_socket* accept(typename TSockTraits::sockaddr& addr)
295 * Shut down part of a full-duplex connection.
297 * @param how How the connection is shutted down.
301 void shutdown(shutdown_mode how = RDWR) throw (error);
304 * Close the socket file descriptor.
308 void close() throw (error);
315 * @see shutdown(), close()
317 ~basic_socket() throw ();
322 * Send a message on the socket.
324 * The message could be any struct and is sent as binary data without
325 * any concerns about the byte-order.
327 * This is a shortcut for the low-level send() method, where the
328 * message length is calculated as sizeof(TPacket).
330 * Of course, TPacket should be a POD type, without pointers or
333 * This method loops on the low-level send() until all the data is
334 * sent, or throws an exception if the send() can't be completed.
336 * @param packet Message struct to send.
337 * @param flags Sending options.
341 * @todo make an enum for the flags (are they protocol-dependant?)
343 template< typename TPacket >
344 void send_struct(const TPacket& packet, int flags = MSG_NOSIGNAL)
348 * Receive a message on the socket.
350 * The message could be any struct and is received as binary data
351 * without any concerns about the byte-order.
353 * This is a shortcut for the low-level recv() method, where the
354 * message length is calculated as sizeof(TPacket).
356 * Of course, TPacket should be a POD type, without pointers or
359 * This method loops on the low-level recv() until all the data is
360 * received, or throws an exception if the recv() can't be completed.
362 * @param packet Message struct to receive on.
363 * @param flags Sending options.
367 * @todo make an enum for the flags (are they protocol-dependant?)
369 template< typename TPacket >
370 void recv_struct(TPacket& packet,
371 int flags = MSG_NOSIGNAL | MSG_WAITALL) throw (error);
374 * Send a message on the socket to a specific name.
376 * This method behaves the same as send(const TPacket& packet, int
377 * flags) but sending to a specific name.
379 * @param packet Message struct to send.
380 * @param to Name to send the message to.
381 * @param flags Sending options.
385 * @todo make an enum for the flags (are they protocol-dependant?)
387 template< typename TPacket >
388 void send_struct(const TPacket& packet,
389 const typename TSockTraits::sockaddr& to,
390 int flags = MSG_NOSIGNAL) throw (error);
393 * Receive a message on the socket from a specific name.
395 * This method behaves the same as recv(TPacket& packet, int flags) but
396 * receiving from a specific name.
398 * @param packet Message struct to receive to.
399 * @param from Name to receive the message from.
400 * @param flags Receiving options.
404 * @todo make an enum for the flags (are they protocol-dependant?)
406 template< typename TPacket >
407 void recv_struct(TPacket& packet, typename TSockTraits::sockaddr& from,
408 int flags = MSG_NOSIGNAL | MSG_WAITALL) throw (error);
411 * Get the socket file descriptor.
413 * You should be careful when using the socket file descriptor, and you
414 * should not close it since it's closed by the destructor.
416 * @return Socket file descriptor.
418 int fd() const throw ();
421 * Convert the socket to a file descriptor.
423 * This is really a shortcut to fd(), useful to pass a socket to a low
426 * @return Socket file descriptor.
428 operator int () const throw ();
432 /// Hidden copy constructor (it has non-copiable behavior).
433 basic_socket(const basic_socket& s);
435 /// Hidden assign operator (it has non-assignable behavior).
436 basic_socket& operator=(const basic_socket& s);
438 /// Socket file descriptor.
444 * Create a pair of connected sockets.
446 * @param type Type of socket.
447 * @param protocol Protocol number.
449 * @return The new connected pair of sockets.
453 template < typename TSock >
455 std::pair< TSock*, TSock* > pair(type type, int protocol = 0) throw (error);
457 } } // namespace posixx::socket
462 template < typename TSock >
463 std::pair< TSock*, TSock* > posixx::socket::pair(type type, int protocol)
464 throw (posixx::error)
467 if (::socketpair(TSock::traits::PF, type, protocol, fds) == -1)
468 throw error("socketpair");
469 return std::make_pair(new TSock(fds[0]), new TSock(fds[1]));
473 template< typename TSockTraits >
475 posixx::socket::basic_socket< TSockTraits >::basic_socket(int fd) throw ():
480 template< typename TSockTraits >
482 posixx::socket::basic_socket< TSockTraits >::basic_socket(type type,
483 int protocol) throw (posixx::error)
485 _fd = ::socket(TSockTraits::PF, type, protocol);
487 throw error("socket");
490 template< typename TSockTraits >
492 void posixx::socket::basic_socket< TSockTraits >::bind(
493 const typename TSockTraits::sockaddr& addr)
494 throw (posixx::error)
496 if (::bind(_fd, reinterpret_cast< const ::sockaddr* >(&addr),
497 addr.length()) == -1)
501 template< typename TSockTraits >
503 void posixx::socket::basic_socket< TSockTraits >::connect(
504 const typename TSockTraits::sockaddr& addr)
505 throw (posixx::error)
507 if (::connect(_fd, reinterpret_cast< const ::sockaddr* >(&addr),
508 addr.length()) == -1)
509 throw error("connect");
512 template< typename TSockTraits >
514 typename TSockTraits::sockaddr posixx::socket::basic_socket< TSockTraits >::name()
515 const throw (posixx::error)
517 typename TSockTraits::sockaddr addr;
518 socklen_t len = sizeof(typename TSockTraits::sockaddr);
519 // TODO assert len == sizeof(typename TSockTraits::sockaddr)
520 if (::getsockname(_fd, reinterpret_cast< ::sockaddr* >(&addr), &len) == -1)
521 throw error("getsockname");
525 template< typename TSockTraits >
527 typename TSockTraits::sockaddr
528 posixx::socket::basic_socket< TSockTraits >::peer_name() const throw (posixx::error)
530 typename TSockTraits::sockaddr addr;
531 socklen_t len = sizeof(typename TSockTraits::sockaddr);
532 // TODO assert len == sizeof(typename TSockTraits::sockaddr)
533 if (::getpeername(_fd, reinterpret_cast< ::sockaddr* >(&addr), &len) == -1)
534 throw error("getpeername");
538 template< typename TSockTraits >
540 ssize_t posixx::socket::basic_socket< TSockTraits >::send(const void* buf,
541 size_t n, int flags) throw (posixx::error)
543 ssize_t s = ::send(_fd, buf, n, flags);
547 throw error("send connection shutdown"); // XXX
551 template< typename TSockTraits >
553 ssize_t posixx::socket::basic_socket< TSockTraits >::recv(void* buf, size_t n, int flags)
554 throw (posixx::error)
556 ssize_t s = ::recv(_fd, buf, n, flags);
560 throw error("recv connection shutdown"); // XXX
564 template< typename TSockTraits >
566 ssize_t posixx::socket::basic_socket< TSockTraits >::send(const void* buf,
567 size_t n, const typename TSockTraits::sockaddr& to, int flags)
568 throw (posixx::error)
570 ssize_t s = ::sendto(_fd, buf, n, flags,
571 reinterpret_cast< const ::sockaddr* >(&to),
574 throw error("sendto");
576 error e("sendto client shutdown"); // XXX
583 template< typename TSockTraits >
585 ssize_t posixx::socket::basic_socket< TSockTraits >::recv(void* buf, size_t n,
586 typename TSockTraits::sockaddr& from, int flags)
587 throw (posixx::error)
589 socklen_t len = sizeof(typename TSockTraits::sockaddr);
590 ssize_t s = ::recvfrom(_fd, buf, n, flags,
591 reinterpret_cast< ::sockaddr* >(&from), &len);
593 throw error("recvfrom");
595 error e("recvfrom client shutdown"); // XXX
603 template< typename TSockTraits >
605 ssize_t posixx::socket::basic_socket< TSockTraits >::send(const msghdr* msg, int flags)
607 ssize_t s = ::sendmsg(_fd, msg, flags);
609 throw error("sendmsg");
615 template< typename TSockTraits >
617 ssize_t posixx::socket::basic_socket< TSockTraits >::recv(struct msghdr* msg, int flags)
619 ssize_t s = ::recvmsg(_fd, msg, flags);
621 throw error("recvmsg");
628 template< typename TSockTraits >
629 template< typename TSockOpt >
631 void posixx::socket::basic_socket< TSockTraits >::getsockopt(int level, int optname,
632 TSockOpt& opt) const throw (posixx::error)
634 socklen_t len = sizeof(TSockOpt);
635 if (::getsockopt(_fd, level, optname, &opt, &len) == -1) // TODO assert len == sizeof(TSockOpt)
636 throw error("getsockopt");
639 template< typename TSockTraits >
640 template< typename TSockOpt >
642 void posixx::socket::basic_socket< TSockTraits >::setsockopt(int level, int optname,
643 const TSockOpt& opt) throw (posixx::error)
645 if (::setsockopt(_fd, level, optname, &opt, sizeof(TSockOpt)) == -1)
646 throw error("setsockopt");
649 template< typename TSockTraits >
650 template< typename TSockOpt >
652 typename TSockOpt::type posixx::socket::basic_socket< TSockTraits >::opt() const
653 throw (posixx::error)
655 static_assert(TSockOpt::read, "Option is not readable");
656 typename TSockOpt::type opt;
657 socklen_t len = sizeof(opt);
658 // TODO assert len == sizeof(TSockOpt)
659 if (::getsockopt(_fd, TSockOpt::level, TSockOpt::optname, &opt, &len) == -1)
660 throw error("getsockopt");
664 template< typename TSockTraits >
665 template< typename TSockOpt >
667 void posixx::socket::basic_socket< TSockTraits >::opt(
668 const typename TSockOpt::type& opt) throw (posixx::error)
670 static_assert(TSockOpt::write, "Option is not writable");
671 if (::setsockopt(_fd, TSockOpt::level, TSockOpt::optname, &opt,
672 sizeof(typename TSockOpt::type)) == -1)
673 throw error("setsockopt");
676 template< typename TSockTraits >
678 void posixx::socket::basic_socket< TSockTraits >::listen(int backlog) throw (posixx::error)
680 if (::listen(_fd, backlog) == -1)
681 throw error("listen");
684 template< typename TSockTraits >
686 posixx::socket::basic_socket< TSockTraits >* posixx::socket::basic_socket< TSockTraits >::accept()
687 throw (posixx::error)
689 int fd = ::accept(_fd, 0, 0);
691 throw error("accept");
692 return new basic_socket(fd);
695 template< typename TSockTraits >
697 posixx::socket::basic_socket< TSockTraits >*
698 posixx::socket::basic_socket< TSockTraits >::accept(typename TSockTraits::sockaddr& addr)
699 throw (posixx::error)
701 socklen_t len = sizeof(typename TSockTraits::sockaddr);
702 // TODO assert len = sizeof(typename TSockTraits::sockaddr)
703 int fd = ::accept(_fd, reinterpret_cast< ::sockaddr* >(&addr), &len);
705 throw error("accept");
706 return new basic_socket(fd);
709 template< typename TSockTraits >
711 void posixx::socket::basic_socket< TSockTraits >::shutdown(shutdown_mode how)
712 throw (posixx::error)
714 if (::shutdown(_fd, how) == -1)
715 throw error("shutdown");
718 template< typename TSockTraits >
720 void posixx::socket::basic_socket< TSockTraits >::close() throw (posixx::error)
722 if (::close(_fd) == -1)
723 throw error("close");
727 template< typename TSockTraits >
729 posixx::socket::basic_socket< TSockTraits >::~basic_socket() throw ()
735 template< typename TSockTraits >
736 template< typename TPacket >
737 void posixx::socket::basic_socket< TSockTraits >::send_struct(
738 const TPacket& packet, int flags) throw (posixx::error)
741 while (static_cast< unsigned >(s) < sizeof(TPacket))
745 s += send(&packet + s, sizeof(TPacket) - s, flags);
747 catch (const error& e)
749 if (e.no != EINTR && e.no != EAGAIN)
753 if (static_cast< unsigned >(s) != sizeof(TPacket))
754 throw error("send size not match");
757 template< typename TSockTraits >
758 template< typename TPacket >
760 void posixx::socket::basic_socket< TSockTraits >::recv_struct(TPacket& packet, int flags)
761 throw (posixx::error)
764 while (static_cast< unsigned >(s) < sizeof(TPacket))
768 s += recv(&packet + s, sizeof(TPacket) - s, flags);
770 catch (const error& e)
772 if (e.no != EINTR && e.no != EAGAIN && e.no != EWOULDBLOCK)
776 if (static_cast< unsigned >(s) != sizeof(TPacket))
777 throw error("recv size not match");
780 template< typename TSockTraits >
781 template< typename TPacket >
783 void posixx::socket::basic_socket< TSockTraits >::send_struct(
784 const TPacket& packet,
785 const typename TSockTraits::sockaddr& to,
786 int flags) throw (posixx::error)
789 while (static_cast< unsigned >(s) < sizeof(TPacket))
793 s += send(&packet + s, sizeof(TPacket) - s, to, flags);
795 catch (const error& e)
797 if (e.no != EINTR && e.no != EAGAIN)
801 if (static_cast< unsigned >(s) != sizeof(TPacket))
802 throw error("send size not match");
805 template< typename TSockTraits >
806 template< typename TPacket >
808 void posixx::socket::basic_socket< TSockTraits >::recv_struct(TPacket& packet,
809 typename TSockTraits::sockaddr& from, int flags)
810 throw (posixx::error)
813 while (static_cast< unsigned >(s) < sizeof(TPacket))
817 s += recv(&packet + s, sizeof(TPacket) - s, from, flags);
819 catch (const error& e)
821 if (e.no != EINTR && e.no != EAGAIN && e.no != EWOULDBLOCK)
825 if (static_cast< unsigned >(s) != sizeof(TPacket))
826 throw error("recv size not match");
829 template< typename TSockTraits >
831 int posixx::socket::basic_socket< TSockTraits >::fd() const throw ()
836 template< typename TSockTraits >
838 posixx::socket::basic_socket< TSockTraits >::operator int () const throw ()
845 template< typename TSockTraits >
846 template< typename TPacket >
848 void send(const TPacket& packet) // TODO specialize with std::string
852 template< typename TSockTraits >
853 template< typename TPacket >
855 void recv(TPacket& packet) // TODO specialize with std::string
859 template< typename TSockTraits >
861 void recv(std::string& buf, size_t n)
866 #endif // _POSIXX_SOCKET_BASIC_SOCKET_HPP_