cgv
Loading...
Searching...
No Matches
socket.cxx
1#include <errno.h>
2#ifdef WIN32
3#pragma warning(disable:4996)
4#include <WinSock2.h>
5#else
6#include <netdb.h>
7#include <sys/socket.h>
8#include <sys/time.h>
9#include <fcntl.h>
10#include <netinet/in.h>
11#include <unistd.h>
12#include <string.h>
13#endif
14
15#include <algorithm>
16#include <iostream>
17#include "socket.h"
18#include "mutex.h"
19
20#ifndef WIN32
21typedef int SOCKET;
22#define INVALID_SOCKET -1
23#define SOCKET_ERROR -1
24#define closesocket(s) ::close(s)
25#endif
26
27#ifdef WIN32
28 typedef int socklen_t;
29#endif
30
31
32using namespace std;
33
34namespace cgv {
35 namespace os {
36
37mutex& ref_show_mutex()
38{
39 static mutex sm;
40 return sm;
41}
42
43int socket::nr_of_sockets= 0;
44bool socket::show_debug_output = false;
45
48{
49 show_debug_output = enable;
50}
51
52
53bool socket::begin()
54{
55 if (!nr_of_sockets) {
56#ifdef WIN32
57 WSADATA info;
58 if (WSAStartup(MAKEWORD(2,0), &info)) {
59 if (show_debug_output) {
60 ref_show_mutex().lock();
61 std::cerr << "could not start up windows socket dll" << std::endl;
62 ref_show_mutex().unlock();
63 }
64 return false;
65 }
66 else
67#endif
68 if (show_debug_output) {
69 ref_show_mutex().lock();
70 std::cout << "successfully start up windows socket dll" << std::endl;
71 ref_show_mutex().unlock();
72 }
73 }
74 ++nr_of_sockets;
75 return true;
76}
77
78void socket::end()
79{
80 if (--nr_of_sockets == 0) {
81#ifdef WIN32
82 WSACleanup();
83#endif
84 if (show_debug_output) {
85 ref_show_mutex().lock();
86 std::cout << "cleaned up windows socket dll" << std::endl;
87 ref_show_mutex().unlock();
88 }
89 }
90
91}
92
93socket::socket() : user_data(0)
94{
95}
96
98socket::socket(size_t _id) : user_data(_id)
99{
100}
101
102bool socket::set_last_error(const char* location, const std::string& text) const
103{
104 if (text.empty()) {
105 char buffer[1024];
106 socklen_t len;
107 getsockopt(user_data, SOL_SOCKET, SO_ERROR, buffer, &len);
108 last_error = buffer;
109 }
110 else
111 last_error = text;
112 if (show_debug_output) {
113 ref_show_mutex().lock();
114 std::cerr << "socket error: ";
115 if (user_data)
116 std::cerr << '(' << user_data << ") ";
117 std::cerr << location << " - " << last_error.c_str();
118 ref_show_mutex().unlock();
119 }
120 return false;
121}
122
124{
125 if (user_data)
126 close();
127}
128
130std::string socket::get_last_error() const
131{
132 return last_error;
133}
134
137{
138 fd_set set;
139 FD_ZERO(&set);
140 FD_SET(user_data, &set);
141 timeval t;
142 timerclear(&t);
143 return select(0, &set, 0, 0, &t) == 1;
144}
145
148{
149 if (!user_data) {
150 set_last_error("get_nr_of_arrived_bytes", "socket not connected");
151 return -1;
152 }
153 unsigned long arg;
154#ifdef WIN32
155 if (ioctlsocket(user_data, FIONREAD, &arg) != 0) {
156 set_last_error("get_nr_of_arrived_bytes");
157 return -1;
158 }
159 last_error.clear();
160#else
161 arg = -1;
162 std::cerr << "get_nr_of_arrived_bytes not implemented!!!" << std::endl;
163#endif
164 return arg;
165}
166
167std::string socket::receive_data(unsigned int nr_of_bytes)
168{
169 std::string ret;
170 char buf[1024];
171 last_error.clear();
172
173 if (nr_of_bytes == 0) {
174 while (is_data_pending()) {
175 int received_nr_of_bytes = recv (user_data, buf, 1024, 0);
176 if (received_nr_of_bytes <= 0) {
177 set_last_error("receive_data", received_nr_of_bytes == SOCKET_ERROR ? "" : "connection closed");
178 break;
179 }
180 ret += std::string(buf,received_nr_of_bytes);
181 }
182 }
183 else {
184 while (nr_of_bytes > 0) {
185 int received_nr_of_bytes = recv(user_data, buf, min(1024, (int)nr_of_bytes), 0);
186 if (received_nr_of_bytes <= 0) {
187 set_last_error("receive_data", received_nr_of_bytes == SOCKET_ERROR ? "" : "connection closed");
188 break;
189 }
190 ret += std::string(buf,received_nr_of_bytes);
191 nr_of_bytes -= received_nr_of_bytes;
192 }
193
194 }
195 return ret;
196}
197
199{
200 std::string ret;
201 while (true) {
202 char r;
203 int result = recv(user_data, &r, 1, 0);
204 if (result <= 0) {
205 set_last_error("receive_line", result == SOCKET_ERROR ? "" : "connection closed");
206 return "";
207 }
208 ret += r;
209 if (r == '\n') {
210 last_error.clear();
211 if (show_debug_output) {
212 ref_show_mutex().lock();
213 std::cout << "received line: " << ret.c_str();
214 std::cout.flush();
215 ref_show_mutex().unlock();
216 }
217 return ret;
218 }
219 }
220}
221
222bool socket::send_line(const std::string& s)
223{
224 return send_data(s+'\n');
225}
226
227bool socket::send_data(const std::string& s)
228{
229 int nr_bytes = (int)s.length();
230 const char* buf = s.c_str();
231 do {
232 int nr_bytes_sent = send(user_data, buf, nr_bytes, 0);
233 if (nr_bytes_sent <= 0)
234 return set_last_error("send_data/line", nr_bytes_sent == SOCKET_ERROR ? "" : "connection closed");
235 nr_bytes -= nr_bytes_sent;
236 buf += nr_bytes_sent;
237 } while (nr_bytes > 0);
238 last_error.clear();
239 return true;
240}
241
243{
244#ifdef WIN32
245 shutdown(user_data, SD_BOTH);
246 int result = closesocket(user_data);
247#else
248 shutdown(user_data, SHUT_RDWR);
249 int result = closesocket(user_data);
250#endif
251 if (result == 0) {
252 if (show_debug_output) {
253 ref_show_mutex().lock();
254 std::cout << "socket(" << user_data << ") closed successfully" << std::endl;
255 ref_show_mutex().unlock();
256 }
257 last_error.clear();
258 }
259 else
260 set_last_error("close");
261
262 user_data = 0;
263 end();
264 return result == 0;
265}
266
270
271bool socket_client::connect(const std::string& host, int port)
272{
273 if (!begin())
274 return set_last_error("connect", "could not initialize os specific socket shared library");
275 user_data = ::socket(AF_INET,SOCK_STREAM,0);
276 if (user_data == INVALID_SOCKET) {
277 user_data = 0;
278 return set_last_error("connect");
279 }
280 std::string error;
281 hostent *he;
282 if ((he = gethostbyname(host.c_str())) == 0)
283 return set_last_error("connect");
284 sockaddr_in addr;
285 addr.sin_family = AF_INET;
286 addr.sin_port = htons(port);
287 addr.sin_addr = *((in_addr *)he->h_addr);
288 memset(&(addr.sin_zero), 0, 8);
289 if (::connect(user_data, (sockaddr *) &addr, sizeof(sockaddr)))
290 return set_last_error("connect");
291 if (show_debug_output) {
292 ref_show_mutex().lock();
293 std::cout << "successfully connected socket_client(" << user_data << ")" << std::endl;
294 ref_show_mutex().unlock();
295 }
296 return true;
297}
298
299socket_client_ptr create_socket_client()
300{
301 return socket_client_ptr(new socket_client());
302}
303
307
308bool socket_server::bind_and_listen(int port, int max_nr_connections)
309{
310 if (!begin())
311 return set_last_error("bind_and_listen", "could not initialize os specific socket shared library");
312 sockaddr_in sa;
313 memset(&sa, 0, sizeof(sa));
314 sa.sin_family = PF_INET;
315 sa.sin_port = htons(port);
316 user_data = ::socket(AF_INET, SOCK_STREAM, 0);
317 if (user_data == INVALID_SOCKET) {
318 user_data = 0;
319 return set_last_error("bind_and_listen", "could not create socket");
320 }
321 /* bind the socket to the internet address */
322 if (bind(user_data, (sockaddr *)&sa, sizeof(sockaddr_in)) == SOCKET_ERROR) {
323 set_last_error("bind_and_listen");
324 closesocket((SOCKET)user_data);
325 user_data = 0;
326 return false;
327 }
328 if (listen((SOCKET)user_data, max_nr_connections) != 0)
329 return set_last_error("bind_and_listen");
330 last_error.clear();
331 if (show_debug_output) {
332 ref_show_mutex().lock();
333 std::cout << "successfully bound socket_server(" << user_data << ") to port " << port << std::endl;
334 ref_show_mutex().unlock();
335 }
336 return true;
337}
338
341{
342 if (!user_data) {
343 set_last_error("wait_for_connection", "attempt to wait for connection of socket server that does not listen to port");
344 return socket_ptr();
345 }
346#ifdef WIN32
347 u_long arg = 0;
348 ioctlsocket(user_data, FIONBIO, &arg);
349#else
350 int flags = fcntl(user_data, F_GETFD) & (~O_NONBLOCK);
351 fcntl(user_data, F_SETFD, flags);
352#endif
353 SOCKET new_sock = ::accept(user_data, 0, 0);
354 if (new_sock == INVALID_SOCKET) {
355 set_last_error("wait_for_connection");
356 return socket_ptr();
357 }
358 last_error.clear();
359 if (show_debug_output) {
360 ref_show_mutex().lock();
361 std::cout << "received new connection on socket_server(" << user_data << "): " << new_sock << std::endl;
362 ref_show_mutex().unlock();
363 }
364 begin();
365 return socket_ptr(new socket(new_sock));
366}
367
370{
371 if (!user_data) {
372 set_last_error("check_for_connection", "attempt to check for connection of socket server that does not listen to port");
373 return socket_ptr();
374 }
375#ifdef WIN32
376 u_long arg = 1;
377 ioctlsocket(user_data, FIONBIO, &arg);
378#else
379 fcntl(user_data, F_SETFD, O_NONBLOCK);
380#endif
381 SOCKET new_sock = ::accept(user_data, 0, 0);
382 if (new_sock == INVALID_SOCKET) {
383#ifdef WIN32
384 if (WSAGetLastError() == WSAEWOULDBLOCK) {
385#else
386 if (errno == EAGAIN || errno == EWOULDBLOCK) {
387#endif
388 last_error.clear();
389 return socket_ptr();
390 }
391 set_last_error("check_for_connection");
392 return socket_ptr();
393 }
394 last_error.clear();
395 if (show_debug_output) {
396 ref_show_mutex().lock();
397 std::cout << "received new connection on socket_server(" << user_data << "): " << new_sock << std::endl;
398 ref_show_mutex().unlock();
399 }
400 begin();
401 return socket_ptr(new socket(new_sock));
402}
403
409
410
411 }
412}
413
reference counted pointer, which can work together with types that are derived from ref_counted,...
Definition ref_ptr.h:160
client socket
Definition socket.h:64
socket_client()
hide from direct use
Definition socket.cxx:267
bool connect(const std::string &host, int port)
connect to given port of given host, if the socket is already connected, close this connection first
Definition socket.cxx:271
socket server allows to be connected to
Definition socket.h:85
bool bind_and_listen(int port, int max_nr_pending_connections)
bind and listen to given port with a queue for pending connections of the given size
Definition socket.cxx:308
socket_server()
hide from direct use
Definition socket.cxx:304
socket_ptr wait_for_connection()
if no connection is pending, block thread and wait for next incoming connection
Definition socket.cxx:340
socket_ptr check_for_connection()
check if a new connection is in the connection queue and return this or an empty connection pointer
Definition socket.cxx:369
friend CGV_API socket_server_ptr create_socket_server()
this is the only way to create a socket_server as a reference counted pointer
Definition socket.cxx:405
static void enable_debug_output(bool enable=true)
enables or disables (default) debug output for all socket commands
Definition socket.cxx:47
bool send_line(const std::string &content)
extends line by newline and send as data
Definition socket.cxx:222
virtual ~socket()
virtual destructor
Definition socket.cxx:123
std::string receive_data(unsigned int nr_of_bytes=0)
receive all pending data or if nr_of_bytes is larger than 0, exactly nr_of_bytes
Definition socket.cxx:167
socket()
hides constructor from user
Definition socket.cxx:93
bool set_last_error(const char *location, const std::string &text="") const
convenience function to set last error and print debug info. The method always returns false.
Definition socket.cxx:102
bool send_data(const std::string &)
send the data in the string
Definition socket.cxx:227
std::string receive_line()
receive data up to the next newline excluding the newline char
Definition socket.cxx:198
bool is_data_pending() const
return whether data has arrived
Definition socket.cxx:136
size_t user_data
store platform dependent reference to socket
Definition socket.h:26
std::string get_last_error() const
returns the last error
Definition socket.cxx:130
std::string last_error
store the last error
Definition socket.h:28
int get_nr_of_arrived_bytes() const
return the number of data bytes that have been arrived at the socket or -1 if socket is not connected
Definition socket.cxx:147
bool close()
close the socket
Definition socket.cxx:242
the cgv namespace
Definition print.h:11
void unlock()
unlock the mutex
void lock()
lock the mutex (if the mutex is already locked, the caller is blocked until the mutex becomes availab...