ivl 679
ivl/sock.hpp
00001 /* This file is part of the ivl C++ library <http://image.ntua.gr/ivl>.
00002    A C++ template library extending syntax towards mathematical notation.
00003 
00004    Copyright (C) 2012 Yannis Avrithis <iavr@image.ntua.gr>
00005    Copyright (C) 2012 Kimon Kontosis <kimonas@image.ntua.gr>
00006 
00007    ivl is free software; you can redistribute it and/or modify
00008    it under the terms of the GNU Lesser General Public License 
00009    version 3 as published by the Free Software Foundation.
00010 
00011    Alternatively, you can redistribute it and/or modify it under the terms 
00012    of the GNU General Public License version 2 as published by the Free 
00013    Software Foundation.
00014 
00015    ivl is distributed in the hope that it will be useful,
00016    but WITHOUT ANY WARRANTY; without even the implied warranty of
00017    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
00018    See the GNU General Public License for more details.
00019 
00020    You should have received a copy of the GNU General Public License 
00021    and a copy of the GNU Lesser General Public License along 
00022    with ivl. If not, see <http://www.gnu.org/licenses/>. */
00023 
00025 /* Note: This is not a complete socket API implementation. */
00026 /* It only gives an interface to the basic operations */
00027 /* of sending and receiving data of given sizes */
00028 /* through socket connection. */
00029 
00030 /* Note#2: KLUDGE: This file is temporary and not official.
00031  * It should be in an ivl module not in the main library,
00032  * plus it has implementation which needs seperate compilation
00033  * plus it has no include guards.
00034  * */
00035 
00036 // standard C headers
00037 #include <string.h>
00038 #include <stdio.h>
00039 
00040 // include headers based on OS
00041 #if defined WIN32
00042 #include <winsock.h>  // WinSock subsystem
00043 #elif defined __linux__
00044 #include <unistd.h>
00045 #include <netdb.h>
00046 #include <sys/types.h>
00047 #include <sys/socket.h>
00048 #include <arpa/inet.h>
00049 #endif
00050 
00051 namespace ivl {
00052 
00053 namespace net {
00054 
00055 // redefine some types and constants based on OS
00056 #if defined WIN32
00057 typedef int socklen_t;  // Unix socket length
00058 #elif defined __linux__
00059 typedef int SOCKET;
00060 enum { INVALID_SOCKET = -1 }; // WinSock invalid socket
00061 enum { SOCKET_ERROR = -1 };   // basic WinSock error
00062 // Unix uses file descriptors, WinSock doesn't...
00063 template <class S> inline void closesocket(S& s) { close(s); }
00064 #endif
00065 
00066 struct socklib
00067 {
00068         static void initialize(bool no_auto_destroy = false);
00069         static void no_initialize(bool no_auto_destroy = false);
00070         static void destroy();
00071         static bool status;
00072         static inline void ensure() { if(!status) initialize(); }
00073 };
00074 
00075 class sock
00076 {
00077 private:
00078     struct sockaddr_in addr;  // local address variable
00079     SOCKET   s;               // local socket
00080     int      r;               // will hold return values
00081     hostent* h;               // server host entry (holds IPs, etc)
00082     bool status;
00083     bool tcpcon_status;
00084         sockaddr_in client_addr;
00085         SOCKET   connected;
00086 
00087 public:
00088 
00089         // TCP connection to host using port
00090         sock(const std::string& host, int port, bool listen = false, bool udp = false, int rcvbuf = 0, int sndbuf = 0);
00091         
00092         // send the data that is in the array and return true for success
00093         bool send(const array<char>& data);
00094 
00095         // receive data that fits the array and return true for success
00096         bool receive(array<char>& data);
00097 
00098         // receive data once into the array and return the size, else zero
00099         ptrdiff_t receive_once(array<char>& data);
00100 
00101         // receive data once into the array & resize the array downwards if necessary.
00102         void receive_resize(array<char>& data);
00103 
00104         ~sock();
00105         
00106         sock& operator <<(const array<char>& data)
00107                 { if(!send(data)) throw ecomp(); return *this; }
00108 
00109         sock& operator >>(array<char>& data)
00110                 { if(!receive(data)) throw ecomp(); return *this; }
00111                 
00112 };
00113 
00114 // ----------------------------------------------------
00115 // implementations
00116 
00117 void socklib::initialize(bool no_auto_destroy)
00118 {
00119         if(status) return;
00120         status = true;
00121         #if defined WIN32
00122                 WSADATA wsa_data;
00123                 WSAStartup(MAKEWORD(1,1), &wsa_data);
00124                 // networking on zombie threads is not automatically supported.
00125                 // unless no_auto_destroy is specified, in which case
00126                 // destruction is manual
00127                 if(!no_auto_destroy)
00128                         atexit(socklib::destroy);
00129         #endif
00130 }
00131 
00132 void socklib::no_initialize(bool no_auto_destroy)
00133 {
00134         if(status) return;
00135         status = true;
00136         #if defined WIN32
00137                 if(!no_auto_destroy)
00138                 // networking on zombie threads is not automatically supported.
00139                 // unless no_auto_destroy is specified, in which case
00140                 // destruction is manual
00141                         atexit(socklib::destroy);
00142         #endif  
00143 }
00144 
00145 void socklib::destroy()
00146 {
00147         if(!status) return;
00148         status = false;
00149     // cleanup WinSock in Windows
00150         #if defined WIN32
00151                 WSACleanup();
00152         #endif
00153 }
00154 
00155 bool socklib::status = false;
00156 
00157 
00158 // ----------------------------------------------------
00159 
00160 sock::~sock()
00161 {
00162         if(this->status)
00163                 closesocket(s);
00164         if(this->tcpcon_status)
00165                 closesocket(connected);
00166 }
00167 
00168 sock::sock(const std::string& host, int port, bool listen, bool udp, int rcvbuf, int sndbuf)
00169 {
00170         socklib::ensure();
00171         this->status = false;
00172         this->tcpcon_status = false;
00173         
00174         if(host != std::string(""))
00175         {
00176                 // get the server host entry
00177                 memset((void*)&addr, 0, sizeof(addr));
00178                 addr.sin_addr.s_addr = inet_addr(host.c_str());
00179                 if(INADDR_NONE == addr.sin_addr.s_addr) {
00180                         h = gethostbyname(host.c_str());
00181                         if(NULL == h) {
00182                                 throw ecomp(); // something better here
00183                                 // perror("Could not get host by name");
00184                         }
00185                 } else {
00186                         h = gethostbyaddr((const char*)&addr.sin_addr, sizeof(struct sockaddr_in), AF_INET);
00187                         if(NULL == h) {
00188                                 throw ecomp(); // something better here
00189                                 // perror("Could not get host by address");
00190                         }
00191                 }
00192             addr.sin_addr   = *((in_addr*)*h->h_addr_list);
00193         }
00194         else
00195         {
00196                 addr.sin_addr.s_addr = htonl(INADDR_ANY);
00197         }
00198 
00199     // create the local socket
00200         if(udp)
00201                 s = socket(AF_INET, SOCK_DGRAM, 0);
00202         else
00203                 s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
00204     if(INVALID_SOCKET == s) {
00205                 throw ecomp(); // something better here
00206         // perror("Could not create socket");
00207     }
00208 
00209     // setup the rest of our local address
00210     addr.sin_family = AF_INET;
00211     addr.sin_port   = htons(port);
00212     
00213         if(!listen)
00214                 r = connect(s, (sockaddr*)&addr, sizeof(struct sockaddr));
00215         else
00216                 r = bind(s, (sockaddr*)&addr, sizeof(struct sockaddr));
00217 
00218         if(listen && !udp)
00219         {
00220                 if (::listen(s, 1) == -1) {
00221             //perror("Listen");
00222             throw ecomp(); // something better here
00223         }
00224                 int sin_size;
00225                 sin_size = sizeof(struct sockaddr_in);
00226                 connected = accept(s, (struct sockaddr *)&client_addr,&sin_size);
00227                 tcpcon_status = true;
00228                 SOCKET tmp;
00229                 tmp = connected; connected = s; s = tmp;
00230         }
00231 
00232     if(SOCKET_ERROR == r) {
00233         closesocket(s);
00234                 throw ecomp(); // ...
00235         // perror("Cannot connect to server");
00236     }
00237 
00238         // set socket buffers to desired sizes
00239         for(int cnt = 0; cnt < 2; cnt++)
00240         {
00241                 int newsize = !cnt ? rcvbuf : sndbuf;
00242                 int opt = !cnt ? SO_RCVBUF : SO_SNDBUF;
00243                 if(newsize)
00244                 {
00245                         int status = setsockopt(s, SOL_SOCKET, opt, (const char*)&rcvbuf, sizeof(int));
00246                         if (status < 0)
00247                         {
00248                                 closesocket(s);
00249                                 throw ecomp(); // ...
00250                         }
00251                         int resize=0;
00252                         int getsize = sizeof(int);
00253                         status = getsockopt(s, SOL_SOCKET, opt, (char*)&resize, &getsize);
00254                         if (status < 0 || resize != rcvbuf)
00255                         {
00256                                 closesocket(s);
00257                                 throw ecomp(); // ...
00258                         }
00259                 }
00260         }
00261 
00262         this->status = true;
00263 }
00264 
00265 // send the data that is in the array and return true for success
00266 bool sock::send(const array<char>& data)
00267 {
00268         // break to pieces and send
00269         size_t max_chunk = 1024*1024*32;
00270         size_t len = data.length();
00271         size_t off = 0;
00272         ptrdiff_t err;
00273         while(len > off) {
00274                 size_t unit = std::min(len - off, max_chunk);
00275                 err = ::send(s, data.c_ptr() + off, unit, 0);
00276                 if(err <= 0)
00277                         return false;
00278                 off += err;
00279         }
00280         return true;
00281 }
00282 
00283 // receive data of size equal to array size and return true for success
00284 bool sock::receive(array<char>& data)
00285 {
00286         size_t max_chunk = 1024*1024*32;
00287         size_t len = data.length();
00288         size_t off = 0;
00289         ptrdiff_t err;
00290 
00291         while(len > off) {
00292                 size_t unit = std::min(len - off, max_chunk);
00293                 err = ::recv(s, data.c_ptr() + off, unit, 0);
00294                 if(err <= 0)
00295                         return false;
00296                 off += err;
00297         }
00298         return true;
00299 }
00300 
00301 // receive data once into the array and return the size, else zero
00302 ptrdiff_t sock::receive_once(array<char>& data)
00303 {
00304         ptrdiff_t err = recv(s, data.c_ptr(), data.length(), 0);
00305         if(err < 0) 
00306                 err = 0;
00307         return err;
00308 }
00309 
00310 void sock::receive_resize(array<char>& data)
00311 {
00312         ptrdiff_t err = this->receive_once(data);
00313         if(err < 0)
00314                 throw ecomp();
00315         else
00316                 data.resize(err);
00317 }
00318 
00319 }; // namespace net
00320 }; // namespace ivl
 All Classes Namespaces Files Functions Variables Typedefs Enumerations