udpraw.cpp
Go to the documentation of this file.
00001 /* This file is part of Udpraw - A library for UDP transmission.
00002  *
00003  * Copyright (C) 2011 Trevor Dodds <@gmail.com trev.dodds>
00004  *
00005  * Udpraw is free software: you can redistribute it and/or modify it under the
00006  * terms of the GNU General Public License as published by the Free Software
00007  * Foundation, either version 3 of the License, or (at your option) any later
00008  * version.
00009  * 
00010  * Udpraw is distributed in the hope that it will be useful, but WITHOUT ANY
00011  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00012  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
00013  * details.
00014  * 
00015  * You should have received a copy of the GNU General Public License along with
00016  * Udpraw.  If not, see <http://www.gnu.org/licenses/>.
00017  */
00018 
00019 #include "udpraw.h"
00020 #include <algorithm> // for max()
00021 #include <ctime>
00022 #include <iostream>
00023 #include <string>
00024 #ifndef _MSC_VER
00025   #include <sys/time.h>
00026   #include <stdio.h>
00027   #include <sys/errno.h>
00028   #include <arpa/inet.h>
00029   //#include <netdb.h> // temporary for testing socket properties
00030   //#include <sys/socket.h> // temporary for testing
00031 #endif
00032 
00033 #ifdef max
00034 #undef max // get rid of annoying windows definitions that override std::max()
00035 #endif
00036 #ifdef min
00037 #undef min
00038 #endif
00039 
00040 Udpraw::Udpraw()
00041 {
00042   createdRecv = false;
00043   createdSend = false;
00044   FD_ZERO(&readSocks);
00045   FD_ZERO(&writeSocks);
00046   FD_ZERO(&exceptionSocks);
00047   recvSocket = 0; // for max() in doSelect()
00048   sendSocket = 0;
00049   //recvPort = 0;
00050 
00051   statSent = 0; // generate some stats
00052   statRecvd = 0;
00053   lastError = UDPRAW_NO_ERROR;
00054   errorPrepend = "Udpraw error: ";
00055 
00056   simulateLoss.send = 0.0;
00057   simulateLoss.recv = 0.0;
00058   srand(time(NULL));
00059 
00060 #ifdef _MSC_VER
00061   initWSA = false;
00062 #endif
00063 }
00064 
00065 Udpraw::~Udpraw()
00066 {
00067   closeAndCleanup();
00068 }
00069 
00070 int Udpraw::getError()
00071   // Get and clear last error number.
00072 {
00073   int temp = lastError;
00074   lastError = UDPRAW_NO_ERROR;
00075   return temp;
00076 }
00077 
00078 std::string Udpraw::getErrorStr()
00079   // Get last error message and clear last error.
00080 {
00081   return getErrorStr(getError());
00082 }
00083 
00084 std::string Udpraw::getErrorStr(int error)
00085   // Get error message.
00086 {
00087   std::string str;
00088 
00089   switch (error)
00090   {
00091     case UDPRAW_NO_ERROR:
00092       str = "No error.";
00093       break;
00094     case UDPRAW_BIND:
00095       str = "Error binding.";
00096       break;
00097     case UDPRAW_MSGSIZE:
00098       str = "Buffer not big enough for message, some data lost.";
00099       break;
00100     case UDPRAW_NOINIT:
00101       str = "Socket not initialised.";
00102       break;
00103     case UDPRAW_NOIP:
00104       str = "No ip address specified.";
00105       break;
00106     case UDPRAW_NOPORT:
00107       str = "No port specified.";
00108       break;
00109     case UDPRAW_RECV:
00110       str = "Error receiving (not message size).";
00111       break;
00112     case UDPRAW_SELECT:
00113       str = "Error with select.";
00114       break;
00115     case UDPRAW_SEND:
00116       str = "Error sending.";
00117       break;
00118     case UDPRAW_SOCKEXCEPTION:
00119       str = "Select detected a socket exception.";
00120       break;
00121     case UDPRAW_UNMATCHED:
00122       str = "> 1 IPs, > 1 ports, but the number of IPs does not match the number of ports.";
00123       break;
00124     default:
00125       str = "Error not found";
00126       break;
00127   }
00128 
00129   return str;
00130 }
00131 
00132 void Udpraw::writeError()
00133   // Write and clear last error.
00134 {
00135   std::cerr << errorPrepend;
00136   writeError(getError());
00137 }
00138 
00139 void Udpraw::writeError(std::string prependMsg)
00140 {
00141   if (prependMsg != "") std::cerr << prependMsg;
00142   writeError(getError());
00143 }
00144 
00145 void Udpraw::writeError(int error)
00146   // Write error message for number 'error'
00147 {
00148   std::cerr << getErrorStr(error) << std::endl;
00149 }
00150 
00151 void Udpraw::init()
00152 {
00153 #ifdef _MSC_VER
00154   // Initialize Winsock
00155   WSAStartup(MAKEWORD(2,2), &wsaData); // version 2.2
00156   initWSA = true;
00157 #endif
00158 }
00159 
00160 int Udpraw::initReceiver(int port)
00161 // returns 0 success, -1 error binding
00162 {
00163 #ifdef _MSC_VER
00164   if (!initWSA) init();
00165 #endif
00166 
00167   //recvPort = port;
00168 
00169   // Create a receiver socket to receive datagrams
00170   recvSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
00171   createdRecv = true;
00172 
00173   // Bind the socket to address (or INADDR_ANY) and the specified recvPort.
00174   recvAddr.sin_family = AF_INET;
00175   recvAddr.sin_port = htons(port);
00176   recvAddr.sin_addr.s_addr = htonl(INADDR_ANY);
00177 
00178 #ifdef _MSC_VER
00179   if (bind(recvSocket, (SOCKADDR *) &recvAddr, sizeof(recvAddr)) != 0) {
00180 #else
00181   if (bind(recvSocket, (struct sockaddr *) &recvAddr, sizeof(recvAddr)) != 0) {
00182 #endif
00183     //std::cerr << "Error binding" << std::endl;
00184     lastError = UDPRAW_BIND;
00185     return -1;
00186   }
00187 
00188   return 0;
00189 
00190   //
00191   //
00192   // check socket properties (temporary for testing)
00193   //
00194   // send and recv buffers were 65535 at socket level (cygwin).
00195 
00196   //protoent *pent = getprotobyname("udp");
00197   //int protnum = pent->p_proto;
00198   //cout << "Protocol number: " << protnum << std::endl;
00199   //long int optval = 0;
00200   //int optvallen = sizeof(optval);
00201   //cout << "optvallen: " << optvallen << std::endl;
00202   //int getsockoptret = getsockopt(recvSocket, SOL_SOCKET, SO_SNDBUF, &optval, &optvallen);
00203   //if (getsockoptret < 0) {
00204   //  perror("perror getsockopt");
00205   //}
00206   //if (optvallen > 0) cout << "RCVBUF2: " << optval << std::endl;
00207   //cout << "getsockoptret: " << getsockoptret << std::endl;
00208 
00209 }
00210 
00211 void Udpraw::initSender(int port)
00212 {
00213 #ifdef _MSC_VER
00214   if (!initWSA) init();
00215 #endif
00216 
00217   //sendPort = port;
00218 
00219   // Create a socket for sending data
00220   sendSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
00221 
00222   sendAddr.sin_family = AF_INET;
00223   //sendAddr.sin_port = htons(port);
00224 
00225   if (port > 0) setSendPort(port);
00226 
00227   createdSend = true;
00228 }
00229 
00230 void Udpraw::initSender(const char *ip, int port)
00231 {
00232   setSendIp(ip);
00233   initSender(port);
00234 }
00235 
00236 void Udpraw::initSender(std::vector <std::string> ip_list, int port)
00237 {
00238   setSendIp(ip_list);
00239   initSender(port);
00240 }
00241 
00242 void Udpraw::initSender(std::vector <std::string> ip_list, std::vector <int> port_list)
00243 {
00244   setSendIp(ip_list);
00245   initSender(0);
00246   setSendPort(port_list);
00247 }
00248 
00249 void Udpraw::setSendIp(const char* ip)
00250 {
00251   sendIp.clear();
00252   sendIp.push_back(ip);
00253 }
00254 
00255 void Udpraw::setSendIp(std::vector <std::string> ip_list)
00256 {
00257   sendIp = ip_list;
00258 }
00259 
00260 void Udpraw::setSendPort(int port)
00261 {
00262   sendPort.clear();
00263   sendPort.push_back(port);
00264 }
00265 
00266 void Udpraw::setSendPort(std::vector <int> port_list)
00267 {
00268   sendPort = port_list;
00269 }
00270 
00271 int Udpraw::doSelect()
00272   // Select, check for errors
00273   // Returns 0 on success, -1 error, -2 if a socket exception was detected.
00274   // NOTE: doSelect() is called by the client, so it can be done once for send and recv
00275 {
00276   struct timeval timeout;             // timeout for using select()
00277   timeout.tv_sec = 0; // don't halt
00278   timeout.tv_usec = 0;
00279 
00280   FD_ZERO(&readSocks);
00281   FD_ZERO(&writeSocks);
00282   FD_ZERO(&exceptionSocks);
00283 
00284   if (createdRecv) {
00285     FD_SET(recvSocket, &readSocks); // check for sockfd being readable
00286     FD_SET(recvSocket, &exceptionSocks);
00287   }
00288   if (createdSend) {
00289     FD_SET(sendSocket, &writeSocks); // check for sockfd being writable
00290     FD_SET(sendSocket, &exceptionSocks);
00291   }
00292 
00293   int sockfdMax = std::max( (int) recvSocket, (int) sendSocket ) + 1;
00294 
00295   // check if socket is readable/writable
00296   if ((numSocksReadable = select(sockfdMax, &readSocks, &writeSocks, &exceptionSocks, &timeout)) == -1) {
00297     lastError = UDPRAW_SELECT;
00298     return -1;
00299   }
00300 
00301   if (FD_ISSET(recvSocket, &exceptionSocks) || FD_ISSET(sendSocket, &exceptionSocks)) {
00302     lastError = UDPRAW_SOCKEXCEPTION;
00303     return -2;
00304   }
00305 
00306   return 0;
00307 }
00308 
00309 int Udpraw::recvRaw(char* buf, int len, bool select)
00310   // return number of bytes received and output pointer to data
00311   // 0 if socket will block
00312   // -1 if data didn't fit, -2 for any other error
00313 {
00314   // error check
00315   if (!createdRecv) {
00316     lastError = UDPRAW_NOINIT;
00317     return -2;
00318   }
00319 
00320   if (!select || FD_ISSET(recvSocket, &readSocks)) {
00321 #ifdef _MSC_VER
00322     int fromAddrSize = sizeof(fromAddr); // this should get filled in with the size of the fromAddr, but recvfrom seems to like it to already be defined
00323 #else
00324     socklen_t fromAddrSize = sizeof(fromAddr);
00325 #endif
00326 
00327     // here, data is extracted from the first enqueued message.
00328     // note if the datagram is larger than recvBufLen, the excess will be discarded!
00329     // see http://msdn.microsoft.com/en-us/library/ms740120%28VS.85%29.aspx
00330     //cout << "Receiving from port: " << recvAddr.sin_port << ", length: " << len << std::endl;
00331 
00332     // TODO may need to use MSG_WAITALL, see man recvfrom, to make sure
00333     // we get all the data we're looking for
00334     // TODO see also man 2 getsockopt, and find out the minimum send/recv
00335     // buffer sizes
00336 #ifdef _MSC_VER
00337     int bytesRecvd = recvfrom(recvSocket, buf, len, 0, (SOCKADDR *)&fromAddr, &fromAddrSize);
00338 #else
00339     int bytesRecvd = recvfrom(recvSocket, buf, len, 0, (struct sockaddr *)&fromAddr, &fromAddrSize);
00340 #endif
00341 
00342     if (bytesRecvd > 0) statRecvd += bytesRecvd;
00343 
00344 #ifdef _MSC_VER
00345     if (bytesRecvd == 0 || bytesRecvd == SOCKET_ERROR)
00346 #else
00347     if (bytesRecvd < 1)
00348 #endif
00349     {
00350       //cout << "Error occurred with recvfrom, possibly buffer to small, or socket closed" << std::endl;
00351 #ifdef _MSC_VER
00352       int error = WSAGetLastError();
00353       if (error == WSAEMSGSIZE) {
00354         //cout << "Detected error: the message size was longer than the receive buffer." << std::endl;
00355         //cout << "Set last byte to null before attempting to output!" << std::endl;
00356         lastError = UDPRAW_MSGSIZE;
00357         return -1;
00358       }else{
00359         lastError = UDPRAW_RECV;
00360         return -2;
00361       }
00362 #else
00363       // use cmsg:
00364 
00365       // TODO receive 'truncated' message here:
00366       /*struct msghdr msgh;
00367       struct cmsghdr *cmsg;
00368       int *ttlptr;
00369       int received_ttl;
00370 
00371       // Receive auxiliary data in msgh
00372       for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL;
00373            cmsg = CMSG_NXTHDR(&msgh,cmsg)) {
00374 
00375         if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TTL) {
00376                    ttlptr = (int *) CMSG_DATA(cmsg);
00377                    received_ttl = *ttlptr;
00378                    break;
00379         }
00380       }
00381       if (cmsg == NULL) {
00382                //
00383                // Error: IP_TTL not enabled or small buffer
00384                // or I/O error.
00385                //
00386       }*/
00387 
00388       //perror("perror recvfrom");
00389       //cout << "Error number: " << errno << std::endl;
00390       lastError = UDPRAW_RECV;
00391       return -2;
00392 #endif
00393     }
00394 
00395     // simulate packet loss
00396     if (simulateLoss.recv > 0.0 && simulatePacketLoss(simulateLoss.recv)) return 0;
00397 
00398     return bytesRecvd;
00399   }else{
00400     return 0;
00401   }
00402 }
00403 
00404 int Udpraw::sendRaw(const char* buf, int len, bool select)
00405 // returns the minimum bytes sent to each destination, 0 if dropped (simulated)
00406 // or -1 on error or when socket would block
00407 {
00408   int minbytes = -1, bytes = 0;
00409 
00410   std::vector<std::string>::iterator ip_iterator = sendIp.begin();
00411   std::vector<int>::iterator port_iterator = sendPort.begin();
00412 
00413   if (!createdSend) {
00414     lastError = UDPRAW_NOINIT;
00415     return -1;
00416   }
00417 
00418   // error check, either 1 ip and > 0 ports, 1 port and > 0 ips,
00419   // or > 1 ip and equal number of ports
00420   if (sendPort.size() == 0) {
00421     //std::cerr << "UDP sendRaw error: no port." << std::endl;
00422     lastError = UDPRAW_NOPORT;
00423     return -1;
00424   }
00425   if (sendIp.size() == 0) {
00426     //std::cerr << "UDP sendRaw error: no IP." << std::endl;
00427     lastError = UDPRAW_NOIP;
00428     return -1;
00429   }
00430   if (sendPort.size() > 1 && sendIp.size() > 1) {
00431     // in this case need a list of corresponding ports for each IP.
00432     if (sendPort.size() != sendIp.size()) {
00433       //std::cerr << "UDP sendRaw error: Number of send ports does not match number of IPs." << std::endl;
00434       lastError = UDPRAW_UNMATCHED;
00435       return -1;
00436     }
00437   }
00438   
00439 
00440   // simulate packet loss:
00441   if (simulateLoss.send > 0.0 && simulatePacketLoss(simulateLoss.send)) return 0;
00442 
00443 
00444   bool first = true;
00445   while (ip_iterator != sendIp.end() && port_iterator != sendPort.end())
00446   {
00447     if (first) first = false;
00448     else{
00449       if (select) doSelect(); // only repeat this if sending to more than one IP, otherwise let app do it
00450       // this is so they can do select once for send and recv
00451     }
00452     if (!select || FD_ISSET(sendSocket, &writeSocks)) {
00453       sendAddr.sin_port = htons(*port_iterator);
00454       sendAddr.sin_addr.s_addr = inet_addr(ip_iterator->c_str());
00455       //cout << "sending: " << buf << " (size: " << len << ") to ip: " << ip_iterator->c_str() << ", and port: " << *port_iterator << std::endl;
00456 #ifdef _MSC_VER
00457       bytes = sendto(sendSocket, buf, len, 0, (SOCKADDR *) &sendAddr, sizeof(sendAddr));
00458       if (bytes == SOCKET_ERROR) bytes = -1; // compatible with below
00459 #else
00460       bytes = sendto(sendSocket, buf, len, 0, (struct sockaddr *) &sendAddr, sizeof(sendAddr));
00461       // linux returns -1 on error
00462 #endif
00463       if (bytes == -1) lastError = UDPRAW_SEND;
00464       if (bytes > 0) statSent += bytes;
00465       if (minbytes < 0 || bytes < minbytes) minbytes = bytes; // could be -1, for error
00466     }else break; // may mean packets don't go to all addresses, but we know that anyway since we're using UDP!
00467 
00468     // either increase one, or the other, or both, or break
00469     if (sendIp.size() > 1) ++ip_iterator;
00470     if (sendPort.size() > 1) ++port_iterator;
00471     if (sendIp.size() == 1 && sendPort.size() == 1) break;
00472   }
00473 
00474   return minbytes; // it's possible that sendto could send less than we asked it to
00475 }
00476 
00477 int Udpraw::getStatSent()
00478 {
00479   return statSent;
00480 }
00481 
00482 int Udpraw::getStatRecvd()
00483 {
00484   return statRecvd;
00485 }
00486 
00487 void Udpraw::setStatSent(int bytes)
00488 {
00489   statSent = bytes;
00490 }
00491 
00492 void Udpraw::setStatRecvd(int bytes)
00493 {
00494   statRecvd = bytes;
00495 }
00496 
00497 void Udpraw::setSimulateLossSend(float n)
00498 {
00499   simulateLoss.send = n;
00500 }
00501 
00502 void Udpraw::setSimulateLossRecv(float n)
00503 {
00504   simulateLoss.recv = n;
00505 }
00506 
00507 float Udpraw::getSimulateLossSend()
00508 {
00509   return simulateLoss.send;
00510 }
00511 
00512 float Udpraw::getSimulateLossRecv()
00513 {
00514   return simulateLoss.recv;
00515 }
00516 
00517 bool Udpraw::simulatePacketLoss(float prob)
00518 // takes a probability from 0-1, 0 = never lose, 1 = always lose.
00519 {
00520   if (rand() % 1000 < prob * 1000) return true; // loss
00521   return false; // no loss
00522 }
00523 
00524 int Udpraw::closeAndCleanup()
00525 {
00526   int retval = 0;
00527 #ifdef _MSC_VER
00528   if (createdRecv) { createdRecv = false; if (closesocket(recvSocket) != 0) retval = 1; }
00529   if (createdSend) { createdSend = false; if (closesocket(sendSocket) != 0) retval = 1; }
00530   if (initWSA) { initWSA = false; WSACleanup(); }
00531 #else
00532   if (createdRecv) { createdRecv = false; if (close(recvSocket) != 0) retval = 1; }
00533   if (createdSend) { createdSend = false; if (close(sendSocket) != 0) retval = 1; }
00534 #endif
00535 
00536   return retval;
00537 }
00538 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines


tk_vrqc2011
Author(s): Martin Riedel
autogenerated on Wed Apr 24 2013 11:26:17