TDME2  1.9.200
UDPSocket.cpp
Go to the documentation of this file.
1 #include <errno.h>
2 #include <string.h>
3 #if defined(_WIN32)
4  #include <winsock2.h>
5  #include <ws2tcpip.h>
6  #define socklen_t int
7  #define BUF_CAST(buf) ((char*)buf)
8 #else
9  #include <sys/socket.h>
10  #include <netinet/in.h>
11  #include <arpa/inet.h>
12  #define BUF_CAST(buf) ((void*)buf)
13 #endif
14 
15 #include <memory>
16 #include <string>
17 
18 #include <tdme/tdme.h>
20 
21 using std::make_unique;
22 using std::string;
23 using std::to_string;
24 using std::unique_ptr;
25 
27 
28 // determine which SO_REUSE option to use
29 #if defined(_WIN32)
30  #define SO_REUSEOPTION SO_REUSEADDR
31 #elif defined(__linux__)
32  // on linux < 3.9 we need to to use "addr". it behaves like "port" on BSD for UDP sockets
33  #define SO_REUSEOPTION SO_REUSEADDR
34 #else
35  // standard is "port" as it exactly does what we want
36  #define SO_REUSEOPTION SO_REUSEPORT
37 #endif
38 
39 UDPSocket::UDPSocket(IpVersion ipVersion) {
40  this->ipVersion = ipVersion;
41 }
42 
44 }
45 
47  auto socket = make_unique<UDPSocket>(ipVersion);
48  socket->descriptor = ::socket(ipVersion == IPV6?AF_INET6:AF_INET, SOCK_DGRAM, IPPROTO_UDP);
49  if (socket->descriptor == -1) {
50  throw NetworkSocketException("Could not create socket: " + string(strerror(errno)));
51  }
52  #ifdef __APPLE__
53  int flag = 1;
54  if (setsockopt(socket->descriptor, SOL_SOCKET, SO_NOSIGPIPE, (void*)&flag, sizeof(flag)) == -1) {
55  throw NetworkSocketException("Could not set no sig pipe on socket: " + string(strerror(errno)));
56  }
57  #endif
58  //
59  return socket.release();
60 }
61 
62 UDPSocket* UDPSocket::createServerSocket(const string& ip, const unsigned int port) {
63  // create socket
64  auto socket = unique_ptr<UDPSocket>(UDPSocket::create(determineIpVersion(ip)));
65  //
66  try {
67  // set non blocked
68  socket->setNonBlocked();
69 
70  // enable socket reuse port
71  int flag = 1;
72  if (setsockopt(socket->descriptor, SOL_SOCKET, SO_REUSEOPTION, BUF_CAST(&flag), sizeof(flag)) == -1) {
73  throw NetworkSocketException("Could not set reuse port on socket: " + string(strerror(errno)));
74  }
75 
76  // bind socket to host
77  socket->bind(ip, port);
78  } catch (NetworkSocketException &exception) {
79  socket->close();
80  throw exception;
81  }
82  //
83  return socket.release();
84 }
85 
87  // create socket
88  auto socket = unique_ptr<UDPSocket>(UDPSocket::create(ipVersion));
89 
90  try {
91  // set non blocked
92  socket->setNonBlocked();
93  } catch (NetworkSocketException &exception) {
94  socket->close();
95  throw exception;
96  }
97  //
98  return socket.release();
99 }
100 
101 ssize_t UDPSocket::read(string& from, unsigned int& port, void* buf, const size_t bytes) {
102  // socket address in setup
103  socklen_t sinLen = 0;
104  void* sin;
105  sockaddr_in sinIPV4;
106  sockaddr_in6 sinIPV6;
107  switch(ipVersion) {
108  case IPV4:
109  {
110  sin = &sinIPV4;
111  sinLen = sizeof(sinIPV4);
112  }
113  break;
114  case IPV6:
115  {
116 
117  sin = &sinIPV6;
118  sinLen = sizeof(sinIPV6);
119  }
120  }
121 
122  ssize_t bytesRead = ::recvfrom(descriptor, BUF_CAST(buf), bytes, 0, (struct sockaddr *)sin, &sinLen);
123  if (bytesRead == -1) {
124  #if defined(_WIN32)
125  auto wsaError = WSAGetLastError();
126  if (wsaError == WSAEWOULDBLOCK ||
127  wsaError == WSAECONNRESET) {
128  return -1;
129  } else {
130  throw NetworkIOException("Error while reading from socket: " + to_string(wsaError));
131  }
132  #else
133  // nope throw an exception
134  if (errno == EAGAIN) {
135  return -1;
136  } else {
137  throw NetworkIOException("Error while reading from socket: " + string(strerror(errno)));
138  }
139  #endif
140  }
141 
142  // set up senders ip + port
143  switch(ipVersion) {
144  case IPV4:
145  {
146  char ipv4AddressString[INET_ADDRSTRLEN];
147  from = inet_ntop(AF_INET, &sinIPV4.sin_addr, ipv4AddressString, INET_ADDRSTRLEN) == NULL?"127.0.0.1":ipv4AddressString;
148  port = ntohs(sinIPV4.sin_port);
149  }
150  break;
151  case IPV6:
152  {
153  char ipv6AddressString[INET6_ADDRSTRLEN];
154  from = inet_ntop(AF_INET6, &sinIPV6.sin6_addr, ipv6AddressString, INET6_ADDRSTRLEN) == NULL?"::1":ipv6AddressString;
155  port = ntohs(sinIPV6.sin6_port);
156  }
157  }
158 
159  // return bytes read
160  return bytesRead;
161 }
162 
163 ssize_t UDPSocket::write(const string& to, const unsigned int port, void* buf, const size_t bytes) {
164  // receiver address in setup
165  socklen_t sinLen = 0;
166  void* sin;
167  sockaddr_in sinIPV4;
168  sockaddr_in6 sinIPV6;
169  switch(ipVersion) {
170  case IPV4:
171  {
172  sinLen = sizeof(sinIPV4);
173  memset(&sinIPV4, 0, sinLen);
174  sinIPV4.sin_family = AF_INET;
175  sinIPV4.sin_port = htons(port);
176  sinIPV4.sin_addr.s_addr = inet_addr(to.c_str());
177  sin = &sinIPV4;
178  }
179  break;
180  case IPV6:
181  {
182  sinLen = sizeof(sinIPV6);
183  memset(&sinIPV6, 0, sinLen);
184  sinIPV6.sin6_family = AF_INET6;
185  sinIPV6.sin6_port = htons(port);
186  inet_pton(AF_INET6, to.c_str(), &sinIPV6.sin6_addr);
187  sin = &sinIPV6;
188  }
189  break;
190  }
191 
192  // go
193  #if defined(__APPLE__) || defined(_WIN32)
194  ssize_t bytesWritten = ::sendto(descriptor, BUF_CAST(buf), bytes, 0, (const struct sockaddr*)sin, sinLen);
195  #else
196  ssize_t bytesWritten = ::sendto(descriptor, BUF_CAST(buf), bytes, MSG_NOSIGNAL, (const struct sockaddr*)sin, sinLen);
197  #endif
198 
199  // send successful?
200  if (bytesWritten == -1) {
201  #if defined(_WIN32)
202  auto wsaError = WSAGetLastError();
203  if (wsaError == WSAEWOULDBLOCK) {
204  return -1;
205  } else {
206  throw NetworkIOException("Error while writing to socket: " + to_string(wsaError));
207  }
208  #else
209  // nope throw an exception
210  if (errno == EAGAIN) {
211  return -1;
212  } else {
213  throw NetworkIOException("Error while writing to socket: " + string(strerror(errno)));
214  }
215  #endif
216  }
217 
218  // return bytes written
219  return bytesWritten;
220 }
#define SO_REUSEOPTION
Definition: UDPSocket.cpp:36
#define BUF_CAST(buf)
Definition: UDPSocket.cpp:12
Base exception class for network IO exceptions.
static IpVersion determineIpVersion(const string &ip)
Determine IP version.
Class representing a UDP socket.
Definition: UDPSocket.h:27
ssize_t read(string &from, unsigned int &port, void *buf, const size_t bytes)
Reads a datagram from socket.
Definition: UDPSocket.cpp:101
static UDPSocket * create(IpVersion ipVersion)
Creates a UDP socket.
Definition: UDPSocket.cpp:46
ssize_t write(const string &to, const unsigned int port, void *buf, const size_t bytes)
Writes a datagram to socket.
Definition: UDPSocket.cpp:163
virtual ~UDPSocket()
Destructor.
Definition: UDPSocket.cpp:43
static UDPSocket * createClientSocket(IpVersion ipVersion)
Creates a UDP client socket.
Definition: UDPSocket.cpp:86
static UDPSocket * createServerSocket(const string &ip, const unsigned int port)
Creates a UDP server socket.
Definition: UDPSocket.cpp:62