ivl 679
|
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