SoDaRadio-5.0.3-master:8901fb5
UDSockets.cxx
Go to the documentation of this file.
1 /*
2  Copyright (c) 2012, Matthew H. Reilly (kb1vc)
3  All rights reserved.
4 
5  Redistribution and use in source and binary forms, with or without
6  modification, are permitted provided that the following conditions are
7  met:
8 
9  Redistributions of source code must retain the above copyright
10  notice, this list of conditions and the following disclaimer.
11  Redistributions in binary form must reproduce the above copyright
12  notice, this list of conditions and the following disclaimer in
13  the documentation and/or other materials provided with the
14  distribution.
15 
16  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28 
29 #include "UDSockets.hxx"
30 
31 #include <iostream>
32 #include <unistd.h>
33 #include <netdb.h>
34 #include <sys/select.h>
35 #include <sys/types.h>
36 #include <sys/time.h>
37 #include <unistd.h>
38 #include <sys/ioctl.h>
39 #include <fcntl.h>
40 #include <errno.h>
41 #include <string.h>
42 #include <boost/format.hpp>
43 
44 SoDa::UD::ServerSocket::ServerSocket(const std::string & path)
45 {
46  int stat;
47  // create the socket.
48  server_socket = socket(AF_UNIX, SOCK_STREAM, 0);
49 
50  if(server_socket < 0) {
51  std::cerr << "Failed to create server socket... I quit." << std::endl;
52  exit(-1);
53  }
54 
55  int x = fcntl(server_socket, F_GETFL, 0);
56  fcntl(server_socket, F_SETFL, x | O_NONBLOCK);
57 
58  // setup the server address
59  bzero((char*) &server_address, sizeof(server_address));
60  server_address.sun_family = AF_UNIX;
61  strncpy(server_address.sun_path, path.c_str(), sizeof(server_address.sun_path));
62  unlink(server_address.sun_path);
63  int len = strlen(server_address.sun_path) + sizeof(server_address.sun_family);
64 
65  mailbox_pathname = path;
66 
67  // now bind it
68  if (bind(server_socket, (struct sockaddr *) &server_address, len) < 0) {
69  std::cerr << "Couldn't bind Unix domain socket at path " << path << " I quit." << std::endl;
70  exit(-1);
71  }
72 
73  // now let the world know that we're ready for one and only one connection.
74  stat = listen(server_socket, 1);
75  if(stat < 0) {
76  std::cerr << "Couldn't listen on Unix socket " << path << " got " << errno << " I quit." << std::endl;
77  exit(-1);
78  }
79 
80  // mark the socket as "not ready" for input -- it needs to accept first.
81  ready = false;
82 }
83 
84 SoDa::UD::ClientSocket::ClientSocket(const std::string & path, int startup_timeout_count)
85 {
86  int retry_count;
87  conn_socket = socket(AF_UNIX, SOCK_STREAM, 0);
88  if(conn_socket < 0) {
89  std::cerr << boost::format("Failed to create client socket on [%s]... I quit.\n")
90  % path;
91  exit(-1);
92  }
93 
94  server_address.sun_family = AF_UNIX;
95  strncpy(server_address.sun_path, path.c_str(), sizeof(server_address.sun_path));
96  int len = strlen(server_address.sun_path) + sizeof(server_address.sun_family);
97 
98  int stat;
99  for(retry_count = 0; retry_count < startup_timeout_count; retry_count++) {
100  stat = connect(conn_socket, (struct sockaddr *) &server_address, len);
101  if(stat >= 0) break;
102  else {
103  // we should wait a little while before we give up.
104  sleep(2);
105  }
106  }
107 
108  if(stat < 0) {
109  std::cerr << "Client couldn't connect to UNIX socket [" << path << "]. I quit." << std::endl;
110  perror("oops.");
111  exit(-1);
112  }
113 
114  int x = fcntl(conn_socket, F_GETFL, 0);
115  fcntl(conn_socket, F_SETFL, x | O_NONBLOCK);
116 
117 }
118 
120 {
121  if(ready) return true;
122  else {
123  socklen_t ca_len = sizeof(client_address);
124  // note that we've set the server_socket to non-block, so if nobody is here,
125  // we should get an EAGAIN or EWOULDBLOCK.
126  int ns = accept(server_socket, (struct sockaddr *) & client_address, &ca_len);
127  if(ns < 0) {
128  ready = false;
129  }
130  else {
131  conn_socket = ns;
132  int x = fcntl(conn_socket, F_GETFL, 0);
133  fcntl(conn_socket, F_SETFL, x | O_NONBLOCK);
134  ready = true;
135  }
136  }
137  return ready;
138 }
139 
140 int SoDa::UD::NetSocket::loopWrite(int fd, const void * ptr, unsigned int nbytes)
141 {
142  char * bptr = (char*) ptr;
143  int left = nbytes;
144  int stat;
145  while(left > 0) {
146  stat = write(fd, bptr, left);
147  if(stat < 0) {
148  if((errno == EWOULDBLOCK) || (errno == EAGAIN)) {
149  continue;
150  }
151  else {
152  return stat;
153  }
154  }
155  else {
156  left -= stat;
157  bptr += stat;
158  }
159  }
160  return 0;
161 }
162 int SoDa::UD::NetSocket::put(const void * ptr, unsigned int size)
163 {
164  // we always put a buffer of bytes, preceded by a count of bytes to be sent.
165  int stat;
166 
167  stat = loopWrite(conn_socket, &size, sizeof(unsigned int));
168  if(stat < 0) return stat;
169 
170  stat = loopWrite(conn_socket, ptr, size);
171 
172  return stat;
173 }
174 
175 int SoDa::UD::NetSocket::get(void * ptr, unsigned int size)
176 {
177  int stat;
178  unsigned int rsize;
179 
180  stat = read(conn_socket, &rsize, sizeof(unsigned int));
181  if(stat <= 0) {
182  if((errno == EWOULDBLOCK) || (errno == EAGAIN)) {
183  // std::cerr << ">>>" << std::endl;
184  return 0;
185  }
186  else {
187  perror("Oops -- socket get -- ");
188  return stat;
189  }
190  }
191 
192  int left = rsize;
193  char * bptr = (char*) ptr;
194  while(left > 0) {
195  int ls = read(conn_socket, bptr, left);
196  if(ls < 0) {
197  if((errno == EWOULDBLOCK) || (errno == EAGAIN)) {
198  continue;
199  }
200  else {
201  perror("Ooops -- read buffer continued");
202  return ls;
203  }
204  }
205  else {
206  left -= ls;
207  bptr += ls;
208  }
209  }
210 
211  if(rsize > size) {
212  char dmy[100];
213  unsigned int left = rsize - size;
214  while(left != 0) {
215  int ls;
216  ls = read(conn_socket, dmy, (left > 100) ? 100 : left);
217  if(ls < 0) {
218  if((errno == EWOULDBLOCK) || (errno == EAGAIN)) {
219  continue;
220  }
221  else {
222  perror("Ooops -- read buffer continued");
223  return ls;
224  }
225  }
226  left -= ls;
227  }
228  }
229 
230  return size;
231 
232 }
std::string mailbox_pathname
Definition: UDSockets.hxx:88
int loopWrite(int fd, const void *ptr, unsigned int nbytes)
Definition: UDSockets.cxx:140
ClientSocket(const std::string &path, int startup_timeout_count=1)
Definition: UDSockets.cxx:84
ServerSocket(const std::string &path)
Definition: UDSockets.cxx:44
struct sockaddr_un server_address client_address
Definition: UDSockets.hxx:58
int put(const void *ptr, unsigned int size)
Definition: UDSockets.cxx:162
int get(void *ptr, unsigned int size)
Definition: UDSockets.cxx:175