NetCpp  v0.2
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Macros Pages
posix_socket.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2013 Evidence Srl - www.evidence.eu.com
3  *
4  * Boost Software License - Version 1.0 - August 17th, 2003
5  *
6  * Permission is hereby granted, free of charge, to any person or organization
7  * obtaining a copy of the software and accompanying documentation covered by
8  * this license (the "Software") to use, reproduce, display, distribute,
9  * execute, and transmit the Software, and to prepare derivative works of the
10  * Software, and to permit third-parties to whom the Software is furnished to
11  * do so, all subject to the following:
12  *
13  * The copyright notices in the Software and this entire statement, including
14  * the above license grant, this restriction and the following disclaimer,
15  * must be included in all copies of the Software, in whole or in part, and
16  * all derivative works of the Software, unless such copies or derivative
17  * works are solely in the form of machine-executable object code generated by
18  * a source language processor.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
23  * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
24  * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
25  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26  * DEALINGS IN THE SOFTWARE.
27  */
28 
29 #include <unistd.h>
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 #include <sys/un.h>
33 #include <stdexcept>
34 #include <strings.h>
35 #include <cstring>
36 #include <netinet/in.h>
37 #include <netinet/ip.h>
38 #include <arpa/inet.h>
39 
40 #include "posix_socket.hpp"
41 #include "protocol.hpp"
42 #include "logger.hpp"
43 #include "tcp_ip4.hpp"
44 
45 namespace net {
46 
47 /**
48  * @brief Constructor
49  *
50  * The concrete class constructed depends on the specific protocol.
51  * @param prot protocol used by the socket
52  * @exception runtime_error in case of unknown protocol
53  */
56 {
57  if (prot == protocol(STREAM, LOCAL))
58  fd_ = socket(AF_LOCAL, SOCK_STREAM, 0);
59  else if (prot == protocol(DGRAM, LOCAL))
60  fd_ = socket(AF_LOCAL, SOCK_DGRAM, 0);
61  else if (prot == protocol(STREAM, IPv4))
62  fd_ = socket(AF_INET, SOCK_STREAM, 0);
63  else if (prot == protocol(DGRAM, IPv4))
64  fd_ = socket(AF_INET, SOCK_DGRAM, 0);
65  else {
66  ERROR("Error: protocol unkown");
67  throw std::runtime_error ("Protocol unknown");
68  }
69  if (fd_ < 0) {
70  ERROR("Error when creating socket");
71  throw std::runtime_error ("Socket error");
72  }
73 }
74 
75 /**
76  * @brief Destructor.
77  *
78  * It just calls close()
79  */
81 {
82  close();
83 }
84 
85 
86 /**
87  * \brief Low-level read
88  *
89  * @param buffer Pointer to the buffer where read bytes must be stored
90  * @param size Number of bytes to be read
91  * @return The number of actually read bytes or -1 in case of error
92  */
93 int PosixSocket::read (void* buffer, size_t size)
94 {
95  return ::read(fd_, buffer , size);
96 }
97 
98 
99 
100 /**
101  * \brief Low-level write
102  *
103  * @param buffer Pointer to the buffer containing bytes to be written
104  * @param size Number of bytes to be written
105  * @return The number of actually written bytes or -1 in case of error
106  */
107 int PosixSocket::write (const void* buffer, size_t size)
108 {
109  return ::write(fd_, buffer, size);
110 }
111 
112 
113 /**
114  * @brief Method to close the socket
115  *
116  * @return true in case of success; false otherwise
117  */
119  return !(::close(fd_));
120 }
121 
122 /**
123  * @brief Method to accept() a connection on the socket.
124  *
125  * This method calls accept().
126  * This method is usually invoked on the server-side for stream communications.
127  * @param sock Socket on which the new connection must be accepted.
128  * @exception runtime_error in case of error in accept()
129  */
131 {
132  if ((sock->getProtocol() != getProtocol()) || (getProtocol().getType() != net::STREAM)) {
133  ERROR("Accept not available!");
134  throw std::runtime_error("Accept not available");
135  }
136 
137  fd_ = ::accept((dynamic_cast<PosixSocket*> (sock))->fd_, NULL, 0);
138  if (fd_ < 0) {
139  ERROR("Error in accept()!");
140  throw std::runtime_error("Accept error");
141  }
142 }
143 
144 /**
145  * @brief Listen operation
146  *
147  * This method calls listen() and allows to specify the number
148  * of maximum allowed pending connections.
149  * This method is usually invoked on the server-side.
150  * @param max_pending_connections Number of maximum allowed pending connections.
151  * @exception runtime_error in case of error in listen()
152  */
153 void PosixSocket::listen (int max_pending_connections)
154 {
155  if (getProtocol().getType() != net::STREAM) {
156  ERROR("Listen not available!");
157  throw std::runtime_error("Listen not available");
158  }
159  if (::listen(fd_, max_pending_connections) < 0) {
160  ::close(fd_);
161  ERROR("Error when listening");
162  throw std::runtime_error ("Listen error");
163  }
164 }
165 
166 /**
167  * @brief Method to bind() the socket to an address.
168  *
169  * This method calls bind().
170  * This method is usually invoked on the server-side.
171  * @param addr Address which the socket must be bound to
172  * @exception runtime_error in case of error in bind()
173  */
174 void PosixSocket::bind (const Address& addr)
175 {
176  if (getProtocol().getDomain() == net::LOCAL) {
177  DEBUG("Local protocol found");
178  struct sockaddr_un serv_addr;
179  bzero((char *) &serv_addr, sizeof(serv_addr));
180  serv_addr.sun_family = AF_LOCAL;
181  strncpy(serv_addr.sun_path, addr.getAddress().c_str(),
182  sizeof(serv_addr.sun_path) - 1);
183  if (::bind(fd_, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) == 0)
184  return;
185  } else if (getProtocol().getDomain() == net::IPv4) {
186  DEBUG("Network protocol found");
187  struct sockaddr_in serv_addr;
188  bzero((char *) &serv_addr, sizeof(serv_addr));
189  serv_addr.sin_family = AF_INET;
190  serv_addr.sin_port = htons((dynamic_cast<const ip4::tcp::address*>(&addr))->getPort());
191  serv_addr.sin_addr.s_addr = INADDR_ANY;
192  if (::bind(fd_, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) == 0)
193  return;
194  } else {
195  ERROR("Unknown protocol found");
196  }
197 
198 error:
199  ::close(fd_);
200  ERROR("Error when binding socket");
201  throw std::runtime_error ("Bind error");
202 }
203 
204 
205 /**
206  * @brief Method to connect() the socket to an address.
207  *
208  * This method calls connect().
209  * This method is usually invoked on the client-side.
210  * @param addr Address which the socket must be connected to
211  * @exception runtime_error in case of error in connect()
212  */
213 void PosixSocket::connect (const Address& addr)
214 {
215  if (getProtocol().getDomain() == net::LOCAL) {
216  struct sockaddr_un serv_addr;
217  bzero((char *) &serv_addr, sizeof(serv_addr));
218  serv_addr.sun_family = AF_LOCAL;
219  strncpy(serv_addr.sun_path, addr.getAddress().c_str(), sizeof(serv_addr.sun_path) - 1);
220  if (::connect(fd_, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) == 0)
221  return;
222  } else if (getProtocol().getDomain() == net::IPv4) {
223  struct sockaddr_in serv_addr;
224  bzero((char *) &serv_addr, sizeof(serv_addr));
225  serv_addr.sin_family = AF_INET;
226  serv_addr.sin_port = htons((dynamic_cast<const ip4::tcp::address*>(&addr))->getPort());
227 
228 
229  struct in_addr add;
230  inet_aton(addr.getAddress().c_str(), &add);
231  bcopy(&add, &serv_addr.sin_addr.s_addr, sizeof(add));
232 
233  if (::connect(fd_, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) == 0)
234  return;
235  }
236 
237 error:
238  ::close(fd_);
239  ERROR("Error when creating client socket");
240  throw std::runtime_error ("Client socket error");
241 }
242 
243 
244 } // net