2 * Copyright (C) 2011 Felix Salfelder
3 * Authors: Felix Salfelder,
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)
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
22 *------------------------------------------------------------------
23 * TCP socket stream stuff.
24 * don't know how to do this right.
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <netinet/in.h>
38 #include <arpa/inet.h>
45 //#include "general.h"
46 //#include "xstring.h"
49 // using namespace std;
52 #define MAX_LISTEN_QUEUE 256
59 #define DEFINE_EXCEPTION(CLASS,PARENT) \
60 class CLASS : public PARENT { \
62 CLASS(const std::string& msg) : PARENT(msg) { } /*, #CLASS) { } */\
66 DEFINE_EXCEPTION(SocketException
, Exception
);
69 * @brief the SocketStream handles the actual data
70 * writing and reading from the socket.
72 class SocketStream
: public iostream
{
76 mutable unsigned _bufsize
;
78 // how to get these from iostream?
79 char* _rbuf
; // receive buffer
80 char* _tbuf
; // transmit buffer
83 mutable bool copied
; // this is just an expriment. if you dont like it, dont use
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));
95 SocketStream(int fd_w
, int fd_r
, unsigned bs
=BUFSIZE
) :
102 trace3("SocketStream(...)",fd_w
,fd_r
,_bufsize
);
104 _rbuf
= (char*)malloc((_bufsize
)*sizeof(char));
105 _tbuf
= (char*)malloc((_bufsize
)*sizeof(char));
110 void operator=(const SocketStream
& obj
)
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
;
122 SocketStream(const SocketStream
& obj
) :
126 _bufsize(obj
._bufsize
),
132 assert(_bufsize
==obj
._bufsize
);
133 //FIXME: check bufsize
135 virtual ~SocketStream();
140 bool at_end(){return _rcur
==chunksize
;}
141 unsigned bufsize()const {return _bufsize
;}
144 const std::string
get(unsigned len
);
146 SocketStream
& operator>>(std::string
&);
147 SocketStream
& operator>>(char&);
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; }
161 SocketStream
& operator<<(const T data
);
162 SocketStream
& pad(const unsigned i
);
166 unsigned rcur()const {return _rcur
;}
167 unsigned tcur()const {return _tcur
;}
171 * @brief the Socket, is an abstract OO approach
172 * to handle (actually wrap the C functions) sockets
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 );
192 short unsigned port_tries
;
195 virtual operator SocketStream() = 0;
198 SocketStream
& operator<<(const T data
);
202 SocketStream
& Socket::operator<<(const Socket::EOL
& ){
204 return *_stream
<< SocketStream::eol
;
208 SocketStream
& Socket::operator<<(const T data
){
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
{
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
{
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
){
258 SocketStream::~SocketStream() {
260 trace1("SocketStream::~SocketStream closing", _fd_w
);
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
);
274 throw SocketException("Could not write to socket");
279 inline SocketStream
& SocketStream::operator<<(const T data
) {
280 const unsigned len
= unsigned((sizeof(T
)/sizeof(char)));
287 // how to do this efficiently?
288 for(unsigned i
=0; i
<len
; i
++){
289 *this << convert
.c
[i
];
294 inline SocketStream
& SocketStream::operator<<(const char data
) {
295 if(_tcur
==_bufsize
) flush();
296 _tbuf
[_tcur
++] = data
;
300 inline SocketStream
& SocketStream::operator<<(const char* data
) {
301 size_t len
=strlen(data
);
303 for(unsigned i
=0; i
<len
; i
++){
306 *this << SocketStream::eol
;
310 void SocketStream::flush() {
311 ssize_t n
= ::write(_fd_w
, _tbuf
, _tcur
);
313 trace5("flush failed", n
,_tcur
,_tbuf
,_fd_w
,errno
);
314 throw SocketException("flush: Could not write to socket ");
319 inline void SocketStream::read()
321 assert(chunksize
== _rcur
);
325 ssize_t n
= ::read(_fd_r
, _rbuf
, _bufsize
);
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
);
336 throw SocketException("Could not read from socket");
340 inline SocketStream
&SocketStream::operator>>(unsigned len
) {
342 for(unsigned i
=0; i
<len
; ++i
){
349 inline SocketStream
&SocketStream::operator>>(T
& d
) {
350 const uint_t len
=sizeof(T
);
355 for(unsigned i
=0; i
<len
; ++i
){
356 *this >> convert
.c
[i
];
362 // this is inefficient, but so what?
363 inline SocketStream
&SocketStream::operator>>(char& c
) {
364 if( _rcur
== chunksize
){
371 inline SocketStream
&SocketStream::operator>>(std::string
& s
) {
372 if( _rcur
== chunksize
) {
376 while (char c
= _rbuf
[_rcur
++]) {
378 if( _rcur
== chunksize
) {
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
));
390 fd
= socket(AF_INET
, SOCK_STREAM
, 0);
392 fd
= socket(AF_INET
, SOCK_DGRAM
, 0);
393 else if(type
== UNIX
)
394 fd
= socket(AF_UNIX
, SOCK_DGRAM
, 0);
397 throw SocketException("Could not create socket");
400 inline Socket::~Socket() {
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
);
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
));
423 trace2("cannot bind to", p
, b
);
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() {
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
*)
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
));
462 hints
.ai_protocol
= 0; /* Any protocol */
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
){
474 trace3("looking up...", hints
.ai_family
, hints
.ai_socktype
, hints
.ai_protocol
);
477 s
= getaddrinfo(host
.c_str(),port
.c_str(),&hints
,&result
);
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);
493 if (connect(fd
, rp
->ai_addr
, rp
->ai_addrlen
) != -1) {
494 trace1("ClientSocket::ClientSocket connected to " + host
, fd
);
500 freeaddrinfo(result
);
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() { }