APM:Libraries
Socket.cpp
Go to the documentation of this file.
1 /*
2  This program is free software: you can redistribute it and/or modify
3  it under the terms of the GNU General Public License as published by
4  the Free Software Foundation, either version 3 of the License, or
5  (at your option) any later version.
6 
7  This program is distributed in the hope that it will be useful,
8  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  GNU General Public License for more details.
11 
12  You should have received a copy of the GNU General Public License
13  along with this program. If not, see <http://www.gnu.org/licenses/>.
14  */
15 /*
16  simple socket handling class for systems with BSD socket API
17  */
18 
19 #include <AP_HAL/AP_HAL.h>
20 #if HAL_OS_SOCKETS
21 
22 #include "Socket.h"
23 
24 /*
25  constructor
26  */
27 SocketAPM::SocketAPM(bool _datagram) :
28  SocketAPM(_datagram,
29  socket(AF_INET, _datagram?SOCK_DGRAM:SOCK_STREAM, 0))
30 {}
31 
32 SocketAPM::SocketAPM(bool _datagram, int _fd) :
33  datagram(_datagram),
34  fd(_fd)
35 {
36  fcntl(fd, F_SETFD, FD_CLOEXEC);
37  if (!datagram) {
38  int one = 1;
39  setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
40  }
41 }
42 
43 SocketAPM::~SocketAPM()
44 {
45  if (fd != -1) {
46  ::close(fd);
47  fd = -1;
48  }
49 }
50 
51 void SocketAPM::make_sockaddr(const char *address, uint16_t port, struct sockaddr_in &sockaddr)
52 {
53  memset(&sockaddr, 0, sizeof(sockaddr));
54 
55 #ifdef HAVE_SOCK_SIN_LEN
56  sockaddr.sin_len = sizeof(sockaddr);
57 #endif
58  sockaddr.sin_port = htons(port);
59  sockaddr.sin_family = AF_INET;
60  sockaddr.sin_addr.s_addr = inet_addr(address);
61 }
62 
63 /*
64  connect the socket
65  */
66 bool SocketAPM::connect(const char *address, uint16_t port)
67 {
68  struct sockaddr_in sockaddr;
69  make_sockaddr(address, port, sockaddr);
70 
71  if (::connect(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != 0) {
72  return false;
73  }
74  return true;
75 }
76 
77 /*
78  bind the socket
79  */
80 bool SocketAPM::bind(const char *address, uint16_t port)
81 {
82  struct sockaddr_in sockaddr;
83  make_sockaddr(address, port, sockaddr);
84 
85  if (::bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != 0) {
86  return false;
87  }
88  return true;
89 }
90 
91 
92 /*
93  set SO_REUSEADDR
94  */
95 void SocketAPM::reuseaddress(void)
96 {
97  int one = 1;
98  setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
99 }
100 
101 /*
102  set blocking state
103  */
104 void SocketAPM::set_blocking(bool blocking)
105 {
106  if (blocking) {
107  fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK);
108  } else {
109  fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
110  }
111 }
112 
113 /*
114  send some data
115  */
116 ssize_t SocketAPM::send(const void *buf, size_t size)
117 {
118  return ::send(fd, buf, size, 0);
119 }
120 
121 /*
122  send some data
123  */
124 ssize_t SocketAPM::sendto(const void *buf, size_t size, const char *address, uint16_t port)
125 {
126  struct sockaddr_in sockaddr;
127  make_sockaddr(address, port, sockaddr);
128  return ::sendto(fd, buf, size, 0, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
129 }
130 
131 /*
132  receive some data
133  */
134 ssize_t SocketAPM::recv(void *buf, size_t size, uint32_t timeout_ms)
135 {
136  if (!pollin(timeout_ms)) {
137  return -1;
138  }
139  socklen_t len = sizeof(in_addr);
140  return ::recvfrom(fd, buf, size, MSG_DONTWAIT, (sockaddr *)&in_addr, &len);
141 }
142 
143 /*
144  return the IP address and port of the last received packet
145  */
146 void SocketAPM::last_recv_address(const char *&ip_addr, uint16_t &port)
147 {
148  ip_addr = inet_ntoa(in_addr.sin_addr);
149  port = ntohs(in_addr.sin_port);
150 }
151 
152 void SocketAPM::set_broadcast(void)
153 {
154  int one = 1;
155  setsockopt(fd,SOL_SOCKET,SO_BROADCAST,(char *)&one,sizeof(one));
156 }
157 
158 /*
159  return true if there is pending data for input
160  */
161 bool SocketAPM::pollin(uint32_t timeout_ms)
162 {
163  fd_set fds;
164  struct timeval tv;
165 
166  FD_ZERO(&fds);
167  FD_SET(fd, &fds);
168 
169  tv.tv_sec = timeout_ms / 1000;
170  tv.tv_usec = (timeout_ms % 1000) * 1000UL;
171 
172  if (select(fd+1, &fds, nullptr, nullptr, &tv) != 1) {
173  return false;
174  }
175  return true;
176 }
177 
178 
179 /*
180  return true if there is room for output data
181  */
182 bool SocketAPM::pollout(uint32_t timeout_ms)
183 {
184  fd_set fds;
185  struct timeval tv;
186 
187  FD_ZERO(&fds);
188  FD_SET(fd, &fds);
189 
190  tv.tv_sec = timeout_ms / 1000;
191  tv.tv_usec = (timeout_ms % 1000) * 1000UL;
192 
193  if (select(fd+1, nullptr, &fds, nullptr, &tv) != 1) {
194  return false;
195  }
196  return true;
197 }
198 
199 /*
200  start listening for new tcp connections
201  */
202 bool SocketAPM::listen(uint16_t backlog)
203 {
204  return ::listen(fd, (int)backlog) == 0;
205 }
206 
207 /*
208  accept a new connection. Only valid for TCP connections after
209  listen has been used. A new socket is returned
210 */
211 SocketAPM *SocketAPM::accept(uint32_t timeout_ms)
212 {
213  if (!pollin(timeout_ms)) {
214  return nullptr;
215  }
216 
217  int newfd = ::accept(fd, nullptr, nullptr);
218  if (newfd == -1) {
219  return nullptr;
220  }
221  // turn off nagle for lower latency
222  int one = 1;
223  setsockopt(newfd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
224  return new SocketAPM(false, newfd);
225 }
226 
227 #endif // HAL_OS_SOCKETS
uint16_t size(void) const
Definition: RingBuffer.h:221
int close(int fileno)
POSIX Close a file with fileno handel.
Definition: posix.c:675