bump to -rc12
[gnucap-felix.git] / src / u_sock.h
blob6766a4fb4064eafc38c984d4ef010f3f8255b79c
1 /*
2 * Copyright (C) 2011 Felix Salfelder
3 * Authors: Felix Salfelder,
4 * Markus Meissner
6 * This file is part of "Gnucap", the Gnu Circuit Analysis Package
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3, or (at your option)
11 * any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 * 02110-1301, USA.
22 *------------------------------------------------------------------
23 * TCP socket stream stuff.
24 * don't know how to do this right.
27 #ifndef XSOCKET_H
28 #define XSOCKET_H
30 #include <stdio.h>
31 #include <strings.h>
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <netinet/in.h>
35 #include <netdb.h>
36 #include <ctype.h>
37 #include <sys/time.h>
38 #include <arpa/inet.h>
40 #include <iostream>
41 #include <vector>
42 #include <string>
43 #include <unistd.h>
45 //#include "general.h"
46 //#include "xstring.h"
49 // using namespace std;
50 using std::iostream;
51 using std::ios;
52 #define MAX_LISTEN_QUEUE 256
53 #define BUFSIZE 32
56 //namespace TOOLS {
57 //namespace NET {
59 #define DEFINE_EXCEPTION(CLASS,PARENT) \
60 class CLASS : public PARENT { \
61 public: \
62 CLASS(const std::string& msg) : PARENT(msg) { } /*, #CLASS) { } */\
66 DEFINE_EXCEPTION(SocketException, Exception);
68 /**
69 * @brief the SocketStream handles the actual data
70 * writing and reading from the socket.
72 class SocketStream : public iostream {
74 int _fd_w;
75 int _fd_r;
76 mutable unsigned _bufsize;
77 unsigned chunksize;
78 // how to get these from iostream?
79 char* _rbuf; // receive buffer
80 char* _tbuf; // transmit buffer
81 unsigned _rcur;
82 unsigned _tcur;
83 mutable bool copied; // this is just an expriment. if you dont like it, dont use
84 // copy and operator=
85 public:
86 enum EOL {eol};
87 SocketStream() : iostream(), _fd_w(0), _fd_r(0), _bufsize(BUFSIZE), copied(false) {
88 trace3("SocketStream()",_fd_w,_fd_r,_bufsize);
89 _rbuf = (char*)malloc((_bufsize)*sizeof(char));
90 _tbuf = (char*)malloc((_bufsize)*sizeof(char));
91 _rcur=0;
92 _tcur=0;
95 SocketStream(int fd_w, int fd_r, unsigned bs=BUFSIZE) :
96 iostream(),
97 _fd_w(fd_w),
98 _fd_r(fd_r),
99 _bufsize(bs),
100 copied(false)
102 trace3("SocketStream(...)",fd_w,fd_r,_bufsize);
103 assert(_bufsize);
104 _rbuf = (char*)malloc((_bufsize)*sizeof(char));
105 _tbuf = (char*)malloc((_bufsize)*sizeof(char));
106 _rcur=0;
107 _tcur=0;
110 void operator=(const SocketStream& obj)
112 _fd_w = obj._fd_w;
113 _fd_r = obj._fd_r;
114 obj.copied = true;
115 // assert(bufsize==obj.bufsize); // could happen due to simple constructor
116 trace4("SocketStream operator=",_fd_w,_fd_r,_bufsize,obj._bufsize);
117 _bufsize=obj._bufsize;
118 _rbuf = obj._rbuf;
119 _tbuf = obj._tbuf;
122 SocketStream(const SocketStream& obj) :
123 ios(), iostream(),
124 _fd_w(obj._fd_w),
125 _fd_r(obj._fd_r),
126 _bufsize(obj._bufsize),
127 _rbuf(obj._rbuf),
128 _tbuf(obj._tbuf)
130 assert(_rbuf);
131 obj.copied=true;
132 assert(_bufsize==obj._bufsize);
133 //FIXME: check bufsize
135 virtual ~SocketStream();
136 private:
137 void read();
139 public:
140 bool at_end(){return _rcur==chunksize;}
141 unsigned bufsize()const {return _bufsize;}
142 void flush();
144 const std::string get(unsigned len);
146 SocketStream& operator>>(std::string&);
147 SocketStream& operator>>(char&);
148 template<class T>
149 SocketStream& operator>>(T&);
150 SocketStream& operator>>(unsigned); // skip chars.
153 void send(const string& data);
154 void send(const double& data);
155 void send(const string& data, int len);
156 SocketStream& operator<<(const std::string& data);
157 SocketStream& operator<<(const char* data); // good idea?
158 SocketStream& operator<<(const char data);
159 SocketStream& operator<<(const EOL){ flush(); return *this; }
160 template<class T>
161 SocketStream& operator<<(const T data);
162 SocketStream& pad(const unsigned i);
164 #ifndef NDEBUG
165 public:
166 unsigned rcur()const {return _rcur;}
167 unsigned tcur()const {return _tcur;}
168 #endif
171 * @brief the Socket, is an abstract OO approach
172 * to handle (actually wrap the C functions) sockets
174 class Socket {
175 public:
176 enum SOCKET_TYPE {
177 UNIX,
178 TCP,
181 protected:
182 int fd;
183 uint16_t port;
184 struct sockaddr_in addr;
185 SOCKET_TYPE type; // ??
186 SocketStream* _stream;
188 Socket(): fd(0), port(0), _stream(0), port_tries(1){}
190 Socket(SOCKET_TYPE type, short unsigned port=0 );
191 protected:
192 short unsigned port_tries;
193 public:
194 virtual ~Socket();
195 virtual operator SocketStream() = 0;
196 enum EOL {eol};
197 template<class T>
198 SocketStream& operator<<(const T data);
201 template<>
202 SocketStream& Socket::operator<<(const Socket::EOL& ){
203 assert(_stream);
204 return *_stream << SocketStream::eol;
207 template<class T>
208 SocketStream& Socket::operator<<(const T data){
209 assert(_stream);
210 return *_stream << data;
213 * @brief ServerSocket sets up a server
214 * socket, which then can be used to listen to and recive connections
216 class ServerSocket : public Socket {
217 public:
218 ServerSocket(SOCKET_TYPE, string /*port*/, short unsigned /*tries*/){ assert(false);}
219 ServerSocket(SOCKET_TYPE type, short unsigned port, short unsigned tries, unsigned bufsize);
220 virtual ~ServerSocket();
222 SocketStream listen();
223 operator SocketStream(){ assert(false);
224 return SocketStream();
225 } // not implemented.
228 * @brief ClientSocket does the obvious, it can connect to any socket
230 class ClientSocket : public Socket {
231 public:
232 ClientSocket(SOCKET_TYPE type, string port, const std::string& target,unsigned bufsize);
233 virtual ~ClientSocket();
235 operator SocketStream(){ return*_stream;}
238 * @brief StreamSelecter wants an arbitrary number of (Socket)Streams as
239 * input - it wraps the select() mechanism.
245 //using namespace TOOLS::NET;
247 SocketStream& SocketStream::pad(const unsigned i){
248 unsigned j=i;
249 const char x='\0';
250 while(j-->0) {
251 *this<<x;
253 return *this;
258 SocketStream::~SocketStream() {
259 if (!copied){
260 trace1("SocketStream::~SocketStream closing", _fd_w);
261 if(_fd_w != _fd_r)
262 close(_fd_w);
263 close(_fd_r);
264 free(_rbuf);
265 free(_tbuf);
269 inline SocketStream& SocketStream::operator<<(const std::string& data) {
270 assert(false); // not implemented
271 size_t len = data.length();
272 ssize_t n = ::write(_fd_w, data.c_str(), len);
273 if(n < 0)
274 throw SocketException("Could not write to socket");
275 return *this;
278 template<class T>
279 inline SocketStream& SocketStream::operator<<(const T data) {
280 const unsigned len = unsigned((sizeof(T)/sizeof(char)));
281 union {
282 char c[len];
283 T d;
284 } convert;
285 convert.d = data;
287 // how to do this efficiently?
288 for(unsigned i=0; i<len; i++){
289 *this << convert.c[i];
291 return *this;
294 inline SocketStream& SocketStream::operator<<(const char data) {
295 if(_tcur==_bufsize) flush();
296 _tbuf[_tcur++] = data;
297 return *this;
300 inline SocketStream& SocketStream::operator<<(const char* data) {
301 size_t len=strlen(data);
303 for(unsigned i=0; i<len; i++){
304 *this << data[i];
306 *this << SocketStream::eol;
307 return *this;
310 void SocketStream::flush() {
311 ssize_t n = ::write(_fd_w, _tbuf, _tcur);
312 if(n < 0) {
313 trace5("flush failed", n,_tcur,_tbuf,_fd_w,errno);
314 throw SocketException("flush: Could not write to socket ");
316 _tcur = 0;
319 inline void SocketStream::read()
321 assert(chunksize == _rcur);
322 assert(_rbuf);
323 assert(_tbuf);
324 _rcur = 0;
325 ssize_t n = ::read(_fd_r, _rbuf, _bufsize);
326 if(n < 0)
327 throw SocketException("SocketStream: Could not read from socket");
328 chunksize = static_cast<unsigned>(n);
331 inline const string SocketStream::get(unsigned len)
333 trace1("::get", len);
334 ssize_t n = ::read(_fd_r, _rbuf, len);
335 if(n < 0)
336 throw SocketException("Could not read from socket");
337 return _rbuf;
340 inline SocketStream &SocketStream::operator>>(unsigned len) {
341 char x;
342 for(unsigned i=0; i<len; ++i){
343 *this >> x;
345 return *this;
348 template<class T>
349 inline SocketStream &SocketStream::operator>>(T& d) {
350 const uint_t len=sizeof(T);
351 union {
352 char c[len];
353 T d;
354 } convert;
355 for(unsigned i=0; i<len; ++i){
356 *this >> convert.c[i];
358 d = convert.d;
359 return *this;
362 // this is inefficient, but so what?
363 inline SocketStream &SocketStream::operator>>(char& c) {
364 if( _rcur == chunksize ){
365 read();
367 c = _rbuf[_rcur++];
368 return *this;
371 inline SocketStream &SocketStream::operator>>(std::string& s) {
372 if( _rcur == chunksize ) {
373 read();
375 s = "";
376 while (char c = _rbuf[_rcur++]) {
377 s+= string(&c);
378 if( _rcur == chunksize ) {
379 read();
382 return *this;
385 inline Socket::Socket(SOCKET_TYPE type, short unsigned port ) : fd(0),
386 port(port), type(type), _stream(0) {
387 bzero((char*) &addr, sizeof(addr));
389 if(type == TCP)
390 fd = socket(AF_INET, SOCK_STREAM, 0);
391 else if(type == UDP)
392 fd = socket(AF_INET, SOCK_DGRAM, 0);
393 else if(type == UNIX)
394 fd = socket(AF_UNIX, SOCK_DGRAM, 0);
396 if(fd < 0)
397 throw SocketException("Could not create socket");
400 inline Socket::~Socket() {
401 delete _stream;
402 // hmmm no fd because SocketStream does the job
406 // glib bug in htons!
407 #pragma GCC diagnostic ignored "-Wconversion"
408 inline ServerSocket::ServerSocket(SOCKET_TYPE type, uint16_t port, short
409 unsigned port_tries=1, unsigned int bs=BUFSIZE) : Socket(type, port ) {
410 if (port_tries == 0) return;
411 addr.sin_family = AF_INET;
412 addr.sin_addr.s_addr = INADDR_ANY;
414 addr.sin_port = htons(port);
416 int b = -1;
417 for( short unsigned p = port; p<port+port_tries; p++ ){
418 addr.sin_port = htons(p);
419 b = bind(fd, (struct sockaddr*) &addr, sizeof(addr));
420 if (b >= 0) {
421 break;
422 } else {
423 trace2("cannot bind to", p, b);
426 if (b<0)
427 throw SocketException("Could not bind to address/port");
429 _stream = new SocketStream(fd,fd,bs);
431 #pragma GCC diagnostic warning "-Wconversion"
433 inline ServerSocket::~ServerSocket() {
434 if(_stream != NULL)
435 delete _stream;
438 inline SocketStream ServerSocket::listen() {
439 struct sockaddr_in client_addr;
440 size_t client_addr_len = sizeof(client_addr);
442 bzero((char*) &client_addr, client_addr_len);
443 ::listen(fd, MAX_LISTEN_QUEUE);
445 int client_fd = accept(fd, (struct sockaddr*) &client_addr, (socklen_t*)
446 &client_addr_len);
447 if(client_fd < 0)
448 throw SocketException("Error during accaptance of remote client");
449 return SocketStream(client_fd, client_fd);
452 #pragma GCC diagnostic ignored "-Wconversion"
453 inline ClientSocket::ClientSocket(SOCKET_TYPE type, string port, const
454 std::string& host, unsigned bufsize) : Socket(type) {
455 struct addrinfo *result, *rp;
456 struct addrinfo hints;
458 trace0("ClientSocket::ClientSocket " + host + " " + port );
460 memset(&hints, 0, sizeof(struct addrinfo));
461 hints.ai_flags = 0;
462 hints.ai_protocol = 0; /* Any protocol */
464 if(type == TCP) {
465 hints.ai_family = AF_INET;
466 hints.ai_socktype = SOCK_STREAM;
467 } else if(type == UDP) {
468 assert(false); // cleanup;
469 } else if(type == UNIX){
470 assert(false);
471 } else {
472 assert(false);
474 trace3("looking up...", hints.ai_family, hints.ai_socktype, hints.ai_protocol );
476 int s;
477 s = getaddrinfo(host.c_str(),port.c_str(),&hints,&result);
478 if (s)
479 throw SocketException("Could not resolve "+host );
482 if(fd)close(fd); // d'oh
484 for (rp = result; rp != NULL; rp = rp->ai_next) {
485 // trace3("connecting...", rp->ai_family, rp->ai_socktype, rp->ai_protocol );
486 rp->ai_family=hints.ai_family;
488 fd = socket(rp->ai_family, rp->ai_socktype,0); // rp->ai_protocol);
490 if (fd == -1)
491 continue;
493 if (connect(fd, rp->ai_addr, rp->ai_addrlen) != -1) {
494 trace1("ClientSocket::ClientSocket connected to " + host, fd );
495 break;
498 close(fd);
500 freeaddrinfo(result);
503 if(!rp){
504 perror("connect:");
505 throw SocketException("Could not connect to " + host + ":" + to_string(port) );
508 _stream = new SocketStream(fd,fd,bufsize);
510 #pragma GCC diagnostic warning "-Wconversion"
512 ClientSocket::~ClientSocket() { }
514 #endif
515 // vim:ts=8:sw=2:et: