• Main Page
  • Related Pages
  • Namespaces
  • Data Structures
  • Files
  • File List
  • Globals

LogSock.cc

Go to the documentation of this file.
00001 /** @file
00002 
00003   A brief file description
00004 
00005   @section license License
00006 
00007   Licensed to the Apache Software Foundation (ASF) under one
00008   or more contributor license agreements.  See the NOTICE file
00009   distributed with this work for additional information
00010   regarding copyright ownership.  The ASF licenses this file
00011   to you under the Apache License, Version 2.0 (the
00012   "License"); you may not use this file except in compliance
00013   with the License.  You may obtain a copy of the License at
00014 
00015       http://www.apache.org/licenses/LICENSE-2.0
00016 
00017   Unless required by applicable law or agreed to in writing, software
00018   distributed under the License is distributed on an "AS IS" BASIS,
00019   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00020   See the License for the specific language governing permissions and
00021   limitations under the License.
00022  */
00023 
00024 #include "P_EventSystem.h"
00025 #include "Error.h"
00026 
00027 #include "LogSock.h"
00028 #include "LogUtils.h"
00029 
00030 static const int LS_SOCKTYPE = SOCK_STREAM;
00031 static const int LS_PROTOCOL = 0;
00032 
00033 /**
00034   LogSock
00035 
00036   The constructor establishes the connection table (ct) and initializes the
00037   first entry of the table (index 0) to be the port on which new
00038   connections are accepted.
00039 */ 
00040 LogSock::LogSock(int max_connects)
00041   :
00042 ct((ConnectTable *) NULL),
00043 m_accept_connections(false),
00044 m_max_connections(max_connects + 1)
00045 {
00046   ink_assert(m_max_connections > 0);
00047 
00048   //
00049   // allocate space for the connection table.
00050   //
00051   ct = new ConnectTable[m_max_connections];
00052   ink_assert(ct != NULL);
00053   for (int i = 0; i < m_max_connections; ++i) {
00054     init_cid(i, NULL, 0, -1, LogSock::LS_STATE_UNUSED);
00055   }
00056 
00057   Debug("log-sock", "LogSocket established");
00058 }
00059 
00060 /**
00061   LogSock::~LogSock
00062 
00063   Shut down all connections and delete memory for the tables.
00064 */ 
00065 LogSock::~LogSock()
00066 {
00067   Debug("log-sock", "shutting down LogSocket on [%s:%d]", ct[0].host, ct[0].port);
00068 
00069   this->close();                // close all connections
00070   this->close(0);               // close accept socket
00071   delete[]ct;                   // delete the connection table
00072 }
00073 
00074 /**
00075   LogSock::listen
00076 
00077   This routine sets up the LogSock to begin accepting connections on the
00078   given @param accept port.  A maximum number of connections is also specified,
00079   which is used to establish the size of the listen queue.
00080 
00081   @Return zero if all goes well, -1 otherwise.
00082 */
00083 int
00084 LogSock::listen(int accept_port, int family)
00085 {
00086   IpEndpoint bind_addr;
00087   int size = sizeof(bind_addr);
00088   char this_host[MAXDNAME];
00089   int ret;
00090   ats_scoped_fd accept_sd;
00091 
00092   Debug("log-sock", "Listening ...");
00093 
00094   // Set up local address for bind.
00095   bind_addr.setToAnyAddr(family);
00096   if (!bind_addr.isValid()) {
00097     Warning("Could not set up socket - invalid address family %d", family);
00098     return -1;
00099   }
00100   bind_addr.port() = htons(accept_port);
00101   size = ats_ip_size(&bind_addr.sa);
00102 
00103   //
00104   // create the socket for accepting new connections
00105   //
00106   accept_sd =::socket(family, LS_SOCKTYPE, LS_PROTOCOL);
00107   if (accept_sd < 0) {
00108     Warning("Could not create a socket for family %d: %s", family, strerror(errno));
00109     return -1;
00110   }
00111   //
00112   // Set socket options (NO_LINGER, TCP_NODELAY, SO_REUSEADDR)
00113   //
00114   // CLOSE ON EXEC
00115   if ((ret = safe_fcntl(accept_sd, F_SETFD, 1)) < 0) {
00116     Warning("Could not set option CLOSE ON EXEC on socket (%d): %s", ret, strerror(errno));
00117     return -1;
00118   }
00119   // NO_LINGER
00120   struct linger l;
00121   l.l_onoff = 0;
00122   l.l_linger = 0;
00123   if ((ret = safe_setsockopt(accept_sd, SOL_SOCKET, SO_LINGER, (char *) &l, sizeof(l))) < 0) {
00124     Warning("Could not set option NO_LINGER on socket (%d): %s", ret, strerror(errno));
00125     return -1;
00126   }
00127   // REUSEADDR
00128   if ((ret = safe_setsockopt(accept_sd, SOL_SOCKET, SO_REUSEADDR, SOCKOPT_ON, sizeof(int))) < 0) {
00129     Warning("Could not set option REUSEADDR on socket (%d): %s", ret, strerror(errno));
00130     return -1;
00131   }
00132 
00133   // Bind to local address.
00134   if ((ret = safe_bind(accept_sd, &bind_addr.sa, size)) < 0) {
00135     Warning("Could not bind port: %s", strerror(errno));
00136     return -1;
00137   }
00138 
00139   if ((ret = safe_setsockopt(accept_sd, IPPROTO_TCP, TCP_NODELAY, SOCKOPT_ON, sizeof(int))) < 0) {
00140     Warning("Could not set option TCP_NODELAY on socket (%d): %s", ret, strerror(errno));
00141     return -1;
00142   }
00143 
00144   if ((ret = safe_setsockopt(accept_sd, SOL_SOCKET, SO_KEEPALIVE, SOCKOPT_ON, sizeof(int))) < 0) {
00145     Warning("Could not set option SO_KEEPALIVE on socket (%d): %s", ret, strerror(errno));
00146     return -1;
00147   }
00148 
00149   //
00150   // if the accept_port argument was zero, then the system just picked
00151   // one for us, so we need to find out what it was and record it in the
00152   // connection table correctly.
00153   //
00154   if (accept_port == 0) {
00155     ret = safe_getsockname(accept_sd, &bind_addr.sa, &size);
00156     if (ret == 0) {
00157       accept_port = ntohs(bind_addr.port());
00158     }
00159   }
00160   //
00161   // establish the listen queue for incomming connections
00162   //
00163   if ((ret = safe_listen(accept_sd, m_max_connections)) < 0) {
00164     Warning("Could not establish listen queue: %s", strerror(errno));
00165     return -1;
00166   }
00167   //
00168   // initialize the first entry of the table for accepting incoming
00169   // connection requests.
00170   //
00171   if (gethostname(&this_host[0], MAXDNAME) != 0) {
00172     snprintf(this_host, sizeof(this_host), "unknown-host");
00173   }
00174   init_cid(0, this_host, accept_port, accept_sd, LogSock::LS_STATE_INCOMING);
00175 
00176   m_accept_connections = true;
00177   Debug("log-sock", "LogSocket established on [%s:%d]", this_host, accept_port);
00178 
00179   accept_sd.release();
00180   return 0;
00181 }
00182 
00183 /**
00184   LogSock::accept
00185 
00186   Accept a new connection.  This is a blocking operation, so you may want
00187   to use one of the non-blocking pending_XXX calls to see if there is a
00188   connection first.
00189   @return This returns the table index for the new connection.
00190 */
00191 int
00192 LogSock::accept()
00193 {
00194   int cid, connect_sd;
00195   IpEndpoint connect_addr;
00196   socklen_t size = sizeof(connect_addr);
00197   in_port_t connect_port;
00198 
00199   if (!m_accept_connections || ct[0].sd < 0) {
00200     return LogSock::LS_ERROR_NO_CONNECTION;
00201   }
00202 
00203   cid = new_cid();
00204   if (cid < 0) {
00205     return LogSock::LS_ERROR_CONNECT_TABLE_FULL;
00206   }
00207 
00208   Debug("log-sock", "waiting to accept a new connection");
00209 
00210   connect_sd =::accept(ct[0].sd, &connect_addr.sa, &size);
00211   if (connect_sd < 0) {
00212     return LogSock::LS_ERROR_ACCEPT;
00213   }
00214   connect_port = ntohs(connect_addr.port());
00215 
00216   init_cid(cid, NULL, connect_port, connect_sd, LogSock::LS_STATE_INCOMING);
00217 
00218   Debug("log-sock", "new connection accepted, cid = %d, port = %d", cid, connect_port);
00219 
00220   return cid;
00221 }
00222 
00223 /**
00224   LogSock::connect
00225 
00226   Establish a new connection to another machine [host:port], and place this
00227   information into the connection and poll tables.
00228 */  
00229 int
00230 LogSock::connect(sockaddr const* ip)
00231 {
00232   int cid, ret;
00233   ats_scoped_fd connect_sd;
00234   uint16_t port;
00235 
00236   if (!ats_is_ip(ip)) {
00237     Note("Invalid host IP or port number for connection");
00238     return LogSock::LS_ERROR_NO_SUCH_HOST;
00239   }
00240   port = ntohs(ats_ip_port_cast(ip));
00241 
00242   ip_port_text_buffer ipstr;
00243   Debug("log-sock", "connecting to [%s:%d]", ats_ip_nptop(ip, ipstr, sizeof(ipstr)), port);
00244 
00245   // get an index into the connection table
00246   cid = new_cid();
00247   if (cid < 0) {
00248     Note("No more connections allowed for this socket");
00249     return LogSock::LS_ERROR_CONNECT_TABLE_FULL;
00250   }
00251   // initialize a new socket descriptor
00252   connect_sd =::socket(ip->sa_family, LS_SOCKTYPE, LS_PROTOCOL);
00253   if (connect_sd < 0) {
00254     Note("Error initializing socket for connection: %d", static_cast<int>(connect_sd));
00255     return LogSock::LS_ERROR_SOCKET;
00256   }
00257 
00258   if ((ret = safe_setsockopt(connect_sd, IPPROTO_TCP, TCP_NODELAY, SOCKOPT_ON, sizeof(int))) < 0) {
00259     Note("Could not set option TCP_NODELAY on socket (%d): %s", ret, strerror(errno));
00260     return -1;
00261   }
00262 
00263   if ((ret = safe_setsockopt(connect_sd, SOL_SOCKET, SO_KEEPALIVE, SOCKOPT_ON, sizeof(int))) < 0) {
00264     Note("Could not set option SO_KEEPALIVE on socket (%d): %s", ret, strerror(errno));
00265     return -1;
00266   }
00267 
00268   // attempt to connect
00269   if (::connect(connect_sd, ip, ats_ip_size(ip)) != 0) {
00270     Note("Failure to connect");
00271     return LogSock::LS_ERROR_CONNECT;
00272   }
00273 
00274   init_cid(cid, ipstr, port, connect_sd, LogSock::LS_STATE_OUTGOING);
00275 
00276   Debug("log-sock", "outgoing connection to [%s:%d] established, fd  = %d", ipstr, port, cid);
00277 
00278   connect_sd.release();
00279   return cid;
00280 }
00281 
00282 /**
00283   LogSock::pending_data
00284 
00285   This private routine checks for incoming data on some of the socket
00286   descriptors.
00287   @return Returns true if there is something incoming, with *cid
00288   set to the index corresponding to the incoming socket.
00289 */ 
00290 bool LogSock::pending_data(int *cid, int timeout_msec, bool include_connects)
00291 {
00292   int
00293     start_index,
00294     ret,
00295     n_poll_fds,
00296     i;
00297   static struct pollfd
00298     fds[LS_CONST_CLUSTER_MAX_MACHINES];
00299   int
00300     fd_to_cid[LS_CONST_CLUSTER_MAX_MACHINES];
00301 
00302   ink_assert(m_max_connections <= (LS_CONST_CLUSTER_MAX_MACHINES + 1));
00303   ink_assert(cid != NULL);
00304   ink_assert(timeout_msec >= 0);
00305 
00306   //
00307   // we'll use the poll() routine, which replaces the select routine
00308   // to support a larger number of socket descriptors.  to use poll,
00309   // we need to set-up a pollfd array for the socket descriptors
00310   // that will be polled.
00311   //
00312 
00313   if (*cid >= 0) {              // look for data on this specific socket
00314 
00315     ink_assert(*cid < m_max_connections);
00316     fds[0].fd = ct[*cid].sd;
00317     fds[0].events = POLLIN;
00318     fds[0].revents = 0;
00319     fd_to_cid[0] = *cid;
00320     n_poll_fds = 1;
00321 
00322   } else {                      // look for data on any INCOMING socket
00323 
00324     if (include_connects) {
00325       start_index = 0;
00326     } else {
00327       start_index = 1;
00328     }
00329     n_poll_fds = 0;
00330     for (i = start_index; i < m_max_connections; i++) {
00331       if (ct[i].state == LogSock::LS_STATE_INCOMING) {
00332         fds[n_poll_fds].fd = ct[i].sd;
00333         fds[n_poll_fds].events = POLLIN;
00334         fds[n_poll_fds].revents = 0;
00335         fd_to_cid[n_poll_fds] = i;
00336         n_poll_fds++;
00337       }
00338     }
00339   }
00340 
00341   if (n_poll_fds == 0) {
00342     return false;
00343   }
00344 
00345   ret =::poll(fds, n_poll_fds, timeout_msec);
00346 
00347   if (ret == 0) {
00348     return false;               // timeout
00349   } else if (ret < 0) {
00350     Debug("log-sock", "error on poll");
00351     return false;               // error
00352   }
00353   //
00354   // a positive return value indicates how many descriptors had something
00355   // waiting on them.  We only care about finding one of them, so we'll
00356   // look for the first one with an revents flag set to POLLIN.
00357   //
00358 
00359   for (i = 0; i < n_poll_fds; i++) {
00360     if (fds[i].revents & POLLIN) {
00361       *cid = fd_to_cid[i];
00362       Debug("log-sock", "poll successful on index %d", *cid);
00363       return true;
00364     }
00365   }
00366 
00367   Debug("log-sock", "invalid revents in the poll table");
00368   return false;
00369 }
00370 
00371 /**
00372   LogSock::pending_any
00373 
00374   Check for incomming data on any of the INCOMING sockets.
00375 */
00376 bool LogSock::pending_any(int *cid, int timeout_msec)
00377 {
00378   ink_assert(cid != NULL);
00379   *cid = -1;
00380   if (m_accept_connections) {
00381     return pending_data(cid, timeout_msec, true);
00382   } else {
00383     return pending_data(cid, timeout_msec, false);
00384   }
00385 }
00386 
00387 /*-------------------------------------------------------------------------
00388   LogSock::pending_message_any
00389 
00390   Check for an incomming message on any of the INCOMING sockets, aside from
00391   the socket reserved for accepting new connections.
00392   -------------------------------------------------------------------------*/
00393 
00394 bool LogSock::pending_message_any(int *cid, int timeout_msec)
00395 {
00396   ink_assert(cid != NULL);
00397   *cid = -1;
00398   return pending_data(cid, timeout_msec, false);
00399 }
00400 
00401 /**
00402   LogSock::pending_message_on
00403 
00404   Check for incomming data on the specified socket.
00405 */
00406 bool LogSock::pending_message_on(int cid, int timeout_msec)
00407 {
00408   return pending_data(&cid, timeout_msec, false);
00409 }
00410 
00411 /**
00412   LogSock::pending_connect
00413 
00414   Check for an incoming connection request on the socket reserved for that
00415   (cid = 0).
00416 */
00417 bool LogSock::pending_connect(int timeout_msec)
00418 {
00419   int
00420     cid = 0;
00421   if (m_accept_connections) {
00422     return pending_data(&cid, timeout_msec, true);
00423   } else {
00424     return false;
00425   }
00426 }
00427 
00428 /**
00429   LogSock::close
00430 
00431   Close one (cid specified) or all (no argument) sockets, except for the
00432   incomming connection socket.
00433 */
00434 void
00435 LogSock::close(int cid)
00436 {
00437   ink_assert(cid >= 0 && cid < m_max_connections);
00438 
00439   Debug("log-sock", "closing connection for cid %d", cid);
00440 
00441   if (ct[cid].state != LogSock::LS_STATE_UNUSED) {
00442     ::close(ct[cid].sd);
00443     delete ct[cid].host;
00444     ct[cid].state = LogSock::LS_STATE_UNUSED;
00445   }
00446 }
00447 
00448 void
00449 LogSock::close()
00450 {
00451   for (int i = 1; i < m_max_connections; i++) {
00452     this->close(i);
00453   }
00454 }
00455 
00456 /**
00457   LogSock::write
00458 
00459   Write data onto the socket corresponding to the given cid.  Return the
00460   number of bytes actually written.
00461 */
00462 int
00463 LogSock::write(int cid, void *buf, int bytes)
00464 {
00465   LogSock::MsgHeader header = {
00466   0};
00467   header.msg_bytes = 0;
00468   int ret;
00469 
00470   ink_assert(cid >= 0 && cid < m_max_connections);
00471 
00472   if (buf == NULL || bytes == 0) {
00473     return 0;
00474   }
00475 
00476   if (ct[cid].state != LogSock::LS_STATE_OUTGOING) {
00477     return LogSock::LS_ERROR_STATE;
00478   }
00479 
00480   Debug("log-sock", "Sending %d bytes to cid %d", bytes, cid);
00481 
00482   //
00483   // send the message header
00484   //
00485   Debug("log-sock", "   sending header (%zu bytes)", sizeof(LogSock::MsgHeader));
00486   header.msg_bytes = bytes;
00487   ret =::send(ct[cid].sd, (char *) &header, sizeof(LogSock::MsgHeader), 0);
00488   if (ret != sizeof(LogSock::MsgHeader)) {
00489     return LogSock::LS_ERROR_WRITE;
00490   }
00491   //
00492   // send the actual data
00493   //
00494   Debug("log-sock", "   sending data (%d bytes)", bytes);
00495   return::send(ct[cid].sd, (char *) buf, bytes, 0);
00496 }
00497 
00498 /**
00499   LogSock::read
00500 
00501   Read data from the specified connection.  This is a blocking call, so you
00502   may want to use one of the pending_XXX calls to see if there is anything
00503   to read first.  Returns number of bytes read.
00504 */
00505 int
00506 LogSock::read(int cid, void *buf, unsigned maxsize)
00507 {
00508   LogSock::MsgHeader header;
00509   unsigned size;
00510 
00511   ink_assert(cid >= 0 && cid < m_max_connections);
00512   ink_assert(buf != NULL);
00513 
00514   if (ct[cid].state != LogSock::LS_STATE_INCOMING) {
00515     return LogSock::LS_ERROR_STATE;
00516   }
00517 
00518   Debug("log-sock", "reading data from cid %d", cid);
00519 
00520   if (read_header(ct[cid].sd, &header) < 0) {
00521     return LogSock::LS_ERROR_READ;
00522   }
00523 
00524   size = ((unsigned) header.msg_bytes < maxsize) ? (unsigned) header.msg_bytes : maxsize;
00525   return read_body(ct[cid].sd, buf, size);
00526 }
00527 
00528 /**
00529   LogSock::read_alloc
00530 
00531   This routine reads data from the specified connection, and returns a
00532   pointer to newly allocated space (allocated with new) containing the
00533   data.  The number of bytes read is set in the argument size, which is
00534   expected to be a pointer to an int.
00535 */
00536 void *
00537 LogSock::read_alloc(int cid, int *size)
00538 {
00539   LogSock::MsgHeader header;
00540   char *data;
00541 
00542   ink_assert(cid >= 0 && cid < m_max_connections);
00543 
00544   if (ct[cid].state != LogSock::LS_STATE_INCOMING) {
00545     return NULL;
00546   }
00547 
00548   Debug("log-sock", "reading data from cid %d", cid);
00549 
00550   if (read_header(ct[cid].sd, &header) < 0) {
00551     return NULL;
00552   }
00553 
00554   data = new char[header.msg_bytes];
00555   ink_assert(data != NULL);
00556 
00557   if ((*size = read_body(ct[cid].sd, data, header.msg_bytes)) < 0) {
00558     delete[] data;
00559     data = NULL;
00560   }
00561 
00562   return data;
00563 }
00564 
00565 /**
00566 */
00567 bool LogSock::is_connected(int cid, bool ping) const
00568 {
00569   int
00570     i,
00571     j,
00572     flags;
00573 
00574   ink_assert(cid >= 0 && cid < m_max_connections);
00575 
00576   if (ct[cid].state == LogSock::LS_STATE_UNUSED) {
00577     return false;
00578   }
00579 
00580   if (ping) {
00581     flags = fcntl(ct[cid].sd, F_GETFL);
00582     ::fcntl(ct[cid].sd, F_SETFL, O_NONBLOCK);
00583     j =::recv(ct[cid].sd, (char *) &i, sizeof(int), MSG_PEEK);
00584     ::fcntl(ct[cid].sd, F_SETFL, flags);
00585     if (j != 0) {
00586       return true;
00587     } else {
00588       return false;
00589     }
00590   } else {
00591     return (ct[cid].sd >= 0);
00592   }
00593 }
00594 
00595 /**
00596 */
00597 void
00598 LogSock::check_connections()
00599 {
00600   for (int i = 1; i < m_max_connections; i++) {
00601     if (ct[i].state == LogSock::LS_STATE_INCOMING) {
00602       if (!is_connected(i, true)) {
00603         Debug("log-sock", "Connection %d is no longer connected", i);
00604         close(i);
00605       }
00606     }
00607   }
00608 }
00609 
00610 /**
00611   This routine will check to ensure that the client connecting is
00612   authorized to use the log collation port.  To authorize, the client is
00613   expected to send the logging secret string.
00614 */
00615 bool LogSock::authorized_client(int cid, char *key)
00616 {
00617   //
00618   // Wait for up to 5 seconds for the client to authenticate
00619   //
00620   if (!pending_message_on(cid, 5000)) {
00621     return false;
00622   }
00623   //
00624   // Ok, the client has a pending message, so check to see if it matches
00625   // the given key.
00626   //
00627   char
00628     buf[1024];
00629   int
00630     size = this->read(cid, buf, 1024);
00631   ink_assert(size >= 0 && size <= 1024);
00632 
00633   if (strncmp(buf, key, size) == 0) {
00634     return true;
00635   }
00636 
00637   return false;
00638 }
00639 
00640 /**
00641 */
00642 char *
00643 LogSock::connected_host(int cid)
00644 {
00645   ink_assert(cid >= 0 && cid < m_max_connections);
00646   return ct[cid].host;
00647 }
00648 
00649 /**
00650 */
00651 int
00652 LogSock::connected_port(int cid)
00653 {
00654   ink_assert(cid >= 0 && cid < m_max_connections);
00655   return ct[cid].port;
00656 }
00657 
00658 /*-------------------------------------------------------------------------
00659   LOCAL ROUTINES
00660   -------------------------------------------------------------------------*/
00661 
00662 /**
00663   LogSock::new_cid
00664 */
00665 int
00666 LogSock::new_cid()
00667 {
00668   int cid = -1;
00669 
00670   for (int i = 1; i < m_max_connections; i++) {
00671     if (ct[i].state == LogSock::LS_STATE_UNUSED) {
00672       cid = i;
00673       break;
00674     }
00675   }
00676 
00677   return cid;
00678 }
00679 
00680 /**
00681   LogSock::init_cid
00682 */
00683 void
00684 LogSock::init_cid(int cid, char *host, int port, int sd, LogSock::State state)
00685 {
00686   ink_assert(cid >= 0 && cid < m_max_connections);
00687   // host can be NULL if it's not known
00688   ink_assert(port >= 0);
00689   // sd can be -1 to indicate no connection yet
00690   ink_assert(state >= 0 && state < LogSock::LS_N_STATES);
00691 
00692   if (host != NULL) {
00693     const size_t host_size = strlen(host) + 1;
00694     ct[cid].host = new char[host_size];
00695     ink_strlcpy(ct[cid].host, host, host_size);
00696   } else {
00697     ct[cid].host = NULL;
00698   }
00699 
00700   ct[cid].port = port;
00701   ct[cid].sd = sd;
00702   ct[cid].state = state;
00703 }
00704 
00705 /**
00706 */
00707 int
00708 LogSock::read_header(int sd, LogSock::MsgHeader * header)
00709 {
00710   ink_assert(sd >= 0);
00711   ink_assert(header != NULL);
00712 
00713   int bytes =::recv(sd, (char *) header, sizeof(LogSock::MsgHeader), 0);
00714   if (bytes != sizeof(LogSock::MsgHeader)) {
00715     return -1;
00716   }
00717 
00718   return bytes;
00719 }
00720 
00721 /**
00722 */
00723 int
00724 LogSock::read_body(int sd, void *buf, int bytes)
00725 {
00726   ink_assert(sd >= 0);
00727   ink_assert(buf != NULL);
00728   ink_assert(bytes >= 0);
00729 
00730   if (bytes == 0) {
00731     return 0;
00732   }
00733 
00734   unsigned bytes_left = bytes;
00735   unsigned bytes_read;
00736   char *to = (char *) buf;
00737 
00738   while (bytes_left) {
00739     bytes_read =::recv(sd, to, bytes_left, 0);
00740     to += bytes_read;
00741     bytes_left -= bytes_read;
00742   }
00743 
00744   return bytes;
00745 }

Generated by  doxygen 1.7.1