MemCacheClient

Socket.cpp

Go to the documentation of this file.
00001 
00005 //#include "stdafx.h"
00006 
00007 #include <errno.h>
00008 
00009 #ifdef _WIN32
00010 // C4127: conditional expression is constant
00011 # pragma warning(disable: 4127) 
00012 # pragma warning(push)
00013 // C4702: unreachable code
00014 // C4706: assignment within conditional expression
00015 # pragma warning(disable: 4702 4706) 
00016 # include <winsock2.h>
00017 # include <ws2tcpip.h>
00018 # pragma warning(pop)
00019 // MSVC 2003-2008 never defined the EWOULDBLOCK etc errors,
00020 // but MSVC 2010 does, however they are different values to
00021 // the WSA errors that we need to check. This means that we 
00022 // need to define our own error symbols to handle the MS FUBAR.
00023 # define ERROR_WOULDBLOCK       WSAEWOULDBLOCK
00024 # define ERROR_INPROGRESS       WSAEINPROGRESS
00025 # define ERROR_TIMEDOUT         WSAETIMEDOUT
00026 # define MSG_NOSIGNAL           0
00027 # define GetLastSocketErrno     WSAGetLastError
00028 // we don't need to handle EINTR on Windows, so just use false for this case
00029 // which will be optimized out by the compiler
00030 # define handleEINTR(rc)        false
00031 typedef unsigned long           in_addr_t;
00032 #else
00033 # include <sys/types.h>
00034 # include <sys/socket.h>
00035 # include <sys/ioctl.h>
00036 # include <arpa/inet.h>
00037 # include <netinet/tcp.h>
00038 # include <netdb.h>
00039 # include <fcntl.h>
00040 // MSVC 2003-2008 never defined the EWOULDBLOCK etc errors,
00041 // but MSVC 2010 does, however they are different values to
00042 // the WSA errors that we need to check. This means that we 
00043 // need to define our own error symbols to handle the MS FUBAR.
00044 # define ERROR_WOULDBLOCK       EWOULDBLOCK
00045 # define ERROR_INPROGRESS       EINPROGRESS
00046 # define ERROR_TIMEDOUT         ETIMEDOUT
00047 # define closesocket            close
00048 # define ioctlsocket            ioctl
00049 # define GetLastSocketErrno()   errno
00050 # define handleEINTR(retval)    (retval < 0 && errno == EINTR)
00051 # define INVALID_SOCKET         -1
00052 #endif
00053 
00054 #include "Socket.h"
00055 #include <string>
00056 
00057 #ifdef CROSSBASE_API
00058 # include <xplatform/timer.h>
00059 # include <Core/clpaths.h>
00060 START_CL_NAMESPACE
00061 #endif
00062 
00063 // static function
00064 bool
00065 Socket::Resolve(
00066     const char *                aHost,
00067     std::vector<std::string> &  aAddress
00068     )
00069 {
00070     aAddress.clear();
00071 
00072     struct addrinfo hints;
00073     memset(&hints, 0, sizeof(hints));
00074     hints.ai_family   = AF_INET;        // force IPv4
00075     hints.ai_socktype = SOCK_STREAM;    // force TCP
00076     hints.ai_protocol = IPPROTO_TCP;    // force IP
00077 
00078     // resolve the name
00079     struct addrinfo * results = NULL;
00080     int rc = getaddrinfo(aHost, "80", &hints, &results);
00081     if (rc != 0) return false;
00082 
00083     // loop over all results
00084     bool loopback = false;
00085     static const in_addr_t localhost = inet_addr("127.0.0.1");
00086     for (struct addrinfo * p = results; p != NULL; p = p->ai_next) {   
00087         if (p->ai_family != AF_INET) continue; // does this happen?
00088         
00089         const struct sockaddr_in * addr = (struct sockaddr_in *) p->ai_addr;
00090         if (addr->sin_addr.s_addr == INADDR_NONE) continue;
00091         if (addr->sin_addr.s_addr == INADDR_LOOPBACK || addr->sin_addr.s_addr == localhost) {
00092             loopback = true;
00093             continue;
00094         }
00095 
00096         aAddress.push_back(inet_ntoa(addr->sin_addr));
00097     }   
00098  
00099     freeaddrinfo(results);
00100 
00101     if (aAddress.empty() && loopback) {
00102         aAddress.push_back("127.0.0.1");
00103     }
00104     return true;
00105 }
00106 
00107 Socket::Socket(const ClTrace & aTrace) 
00108     : mTrace(aTrace)
00109     , mSocket(INVALID_SOCKET)
00110     , mIdx(0)
00111     , mBufLen(0) 
00112     , mConnectTimeout(2000)
00113     , mSendTimeout(1000)
00114     , mRecvTimeout(1000)
00115     , mBufferSize(0)
00116 { }
00117 
00118 Socket::~Socket() 
00119 {
00120     Disconnect();
00121 }
00122 
00123 bool Socket::IsConnected() const 
00124 { 
00125     return mSocket != INVALID_SOCKET; 
00126 }
00127 
00128 // Fuck you very much Microsoft for not following the specs for socket timeouts.
00129 // This took me far too long to nail down the cause for the error.
00130 //
00131 // World:   http://pubs.opengroup.org/onlinepubs/009604599/functions/setsockopt.html
00132 //  SO_RCVTIMEO     ...It accepts a timeval structure with the number of seconds and microseconds ...
00133 // Windows: http://msdn.microsoft.com/en-us/library/windows/desktop/ms740476%28v=vs.85%29.aspx
00134 //  SO_RCVTIMEO     DWORD or int depending on documentation     timeout in milliseconds
00135 //
00136 int Socket::setSocketTimeout(SOCKET aSocket, int aType, int aTimeout)
00137 {
00138 #ifdef _WIN32
00139     DWORD timeout = (DWORD) aTimeout;
00140 #else
00141     struct timeval timeout;
00142     timeout.tv_sec  =  aTimeout / 1000;
00143     timeout.tv_usec = (aTimeout % 1000) * 1000;
00144 #endif
00145     return setsockopt(aSocket, SOL_SOCKET, aType, (const char*) &timeout, sizeof(timeout));
00146 }
00147 
00148 void 
00149 Socket::Disconnect()
00150 {
00151     if (mSocket == INVALID_SOCKET) {
00152         return;
00153     }
00154     
00155     mTrace.Trace(CLINFO, "disconnect socket");
00156 
00157     // shutdown SD_SEND
00158     mTrace.Trace(CLDEBUG, "shutdown socket send");
00159     shutdown(mSocket, 1);
00160 
00161     // read all pending data
00162     mTrace.Trace(CLDEBUG, "drain the socket");
00163     setSocketTimeout(mSocket, SO_RCVTIMEO, 500);
00164     int rc = 1;
00165     while (rc > 0 || handleEINTR(rc)) {
00166         rc = recv(mSocket, mBuf, MAXBUF, 0);
00167         mTrace.Trace(CLDEBUG, "recv returned %d", rc);
00168     }
00169 
00170     // done
00171     mTrace.Trace(CLDEBUG, "closing socket");
00172     closesocket(mSocket);
00173     mSocket = INVALID_SOCKET;
00174 
00175     // clear the buffer
00176     mIdx = mBufLen = 0; 
00177 }
00178 
00179 void Socket::setSocketBlocking(SOCKET aSocket, bool aBlocking)
00180 {
00181     const char * socktype = aBlocking ? "blocking" : "non-blocking";
00182     mTrace.Trace(CLDEBUG, "setting socket to %s", socktype);
00183 
00184 #ifdef _WIN32
00185     u_long value = aBlocking ? 0 : 1;
00186     int rc = ioctlsocket(aSocket, FIONBIO, &value);
00187 #else // !_WIN32
00188     int flags = fcntl(aSocket, F_GETFL, 0);
00189     if (aBlocking) {
00190         flags &= ~O_NONBLOCK;
00191     }
00192     else {
00193         flags |= O_NONBLOCK;
00194     }
00195     int rc = fcntl(aSocket, F_SETFL, flags);
00196 #endif // _WIN32
00197     if (rc != 0) {
00198         const int errcode = GetLastSocketErrno();
00199         throw Exception(ERR_CONNECT, errcode, socktype);
00200     }
00201 }
00202 
00203 void Socket::waitForConnect(SOCKET aSocket)
00204 {
00205     int rc;
00206 
00207 #ifdef _WIN32
00208     try {
00209         // Windows doesn't implement fd_set as a bitmap but as an array of
00210         // socket handles. Because we are only adding a single socket here, 
00211         // we will never exceed the limits of FD_SET_SIZE.
00212         fd_set wr; FD_ZERO(&wr); FD_SET(aSocket, &wr); 
00213         fd_set ex; FD_ZERO(&ex); FD_SET(aSocket, &ex); 
00214 
00215         // non-blocking connect so that we can use a timeout. Windows 
00216         // doesn't return EINTR and so we don't need to loop
00217         struct timeval timeout;
00218         timeout.tv_sec  =  mConnectTimeout / 1000;
00219         timeout.tv_usec = (mConnectTimeout % 1000) * 1000;
00220 
00221         // clear the socket error status
00222         int err, len = sizeof(rc);
00223         getsockopt(aSocket, SOL_SOCKET, SO_ERROR, (char*)&err, &len);
00224 
00225         mTrace.Trace(CLDEBUG, "connect: waiting for completion, timeout = %lums", mConnectTimeout);
00226         const int nfds = 0; // ignored by winsock
00227         rc = select(nfds, NULL, &wr, &ex, &timeout);
00228 
00229         const int errcode = GetLastSocketErrno();
00230         mTrace.Trace(CLDEBUG, "connect: select returned %d, errno %d", rc, errcode);
00231 
00232         if (rc != 1 || FD_ISSET(aSocket, &ex)) {
00233             // socket failed to connect, get the socket error status in case we 
00234             // can get more details from it
00235             len = sizeof(err);
00236             int rco = getsockopt(aSocket, SOL_SOCKET, SO_ERROR, (char*)&err, &len);
00237             if (rco == 0) {
00238                 mTrace.Trace(CLDEBUG, "connect: getsockopt(SO_ERROR) = %d", err);
00239             }
00240             else {
00241                 mTrace.Trace(CLDEBUG, "connect: getsockopt(SO_ERROR) failed, returned %d, errno %d", 
00242                     rco, GetLastSocketErrno());
00243                 err = 0;
00244             }
00245 
00246             if (rc < 0) {
00247                 throw Exception(ERR_CONNECT, errcode, "select");
00248             }
00249             if (rc == 0) {
00250                 throw Exception(ERR_CONNECT, ERROR_TIMEDOUT, "connection timeout");
00251             }
00252 
00253             // exception state is set
00254             throw Exception(ERR_CONNECT, err, "connection failed");
00255         }
00256 
00257         mTrace.Trace(CLDEBUG, "connect: success");
00258     }
00259     catch (const Exception & e) {
00260         mTrace.Trace(CLINFO, "connect exception: %s, %d", e.mDetail, e.mCode);
00261         throw;
00262     }
00263 #else // !_WIN32
00264     fd_set *wr = NULL;
00265     try {
00266         // because the socket FD may be bigger than FD_SET_SIZE (e.g. if the client
00267         // has changed the max file desriptor limit using ulimit), we need to 
00268         // allocate the size for these sets first to avoid stack overflow.
00269         const unsigned long fd = (unsigned long) aSocket;
00270         const size_t fdsetBytes = sizeof(fd_set) * (fd + FD_SETSIZE - 1) / FD_SETSIZE;
00271         wr = (fd_set*) malloc(fdsetBytes);
00272         if (!wr) {
00273             throw Exception(ERR_CONNECT, ENOMEM, "malloc");
00274         }
00275 
00276         // non-blocking connect so that we can use a timeout
00277         struct timeval timeout;
00278         const unsigned long stop  = xplatform::GetCurrentTickCount() + mConnectTimeout;
00279         unsigned long delay = mConnectTimeout;
00280         int err;
00281         socklen_t len;
00282         rc = 0;
00283         do {
00284             if (rc) {
00285                 delay = stop - xplatform::GetCurrentTickCount();
00286                 if (delay == 0 || delay > (unsigned long) mConnectTimeout) break;
00287                 mTrace.Trace(CLDEBUG, "connect: waiting for completion, timeout = %lums (EINTR)", delay);
00288             }
00289             else {
00290                 mTrace.Trace(CLDEBUG, "connect: waiting for completion, timeout = %lums", delay);
00291             }
00292 
00293             // fdset gets modified by select, so reset it every time through loop
00294             memset(wr, 0, fdsetBytes);
00295             FD_SET(fd, wr); 
00296 
00297             // clear the socket error status
00298             len = sizeof(rc);
00299             getsockopt(aSocket, SOL_SOCKET, SO_ERROR, (char*)&err, &len);
00300 
00301             errno = 0;
00302             timeout.tv_sec  =  delay / 1000;
00303             timeout.tv_usec = (delay % 1000) * 1000;
00304             rc = select(fd+1, NULL, wr, NULL, &timeout);
00305         } 
00306         while (handleEINTR(rc));
00307 
00308         const int errcode = GetLastSocketErrno();
00309         mTrace.Trace(CLDEBUG, "connect: select returned %d, errno %d", rc, errcode);
00310 
00311         if (rc != 1) {
00312             // socket failed to connect, get the socket error status in case we 
00313             // can get more details from it
00314             len = sizeof(err);
00315             int rco = getsockopt(aSocket, SOL_SOCKET, SO_ERROR, (char*)&err, &len);
00316             if (rco == 0) {
00317                 mTrace.Trace(CLDEBUG, "connect: getsockopt(SO_ERROR) = %d", err);
00318             }
00319             else {
00320                 mTrace.Trace(CLDEBUG, "connect: getsockopt(SO_ERROR) failed, returned %d, errno %d", 
00321                     rco, GetLastSocketErrno());
00322             }
00323 
00324             if (rc < 0) {
00325                 throw Exception(ERR_CONNECT, errcode, "select");
00326             }
00327             if (rc == 0) {
00328                 throw Exception(ERR_CONNECT, ERROR_TIMEDOUT, "connection timeout");
00329             }
00330         }
00331 
00332         mTrace.Trace(CLDEBUG, "connect: success");
00333     }
00334     catch (const Exception & e) {
00335         mTrace.Trace(CLINFO, "connect exception: %s, %d", e.mDetail, e.mCode);
00336         if (wr) free(wr);
00337         throw;
00338     }
00339 #endif // _WIN32
00340 }
00341 
00342 void Socket::connectSocket(SOCKET aSocket, const sockaddr_in * aServer)
00343 {
00344     // socket must be in non-blocking mode already so that we can
00345     // connect and specify a maximum connection timeout
00346 
00347     int rc = connect(aSocket, (const struct sockaddr *) aServer, sizeof(*aServer));
00348     if (rc < 0) {
00349         const int errcode = GetLastSocketErrno();
00350         if (errcode != ERROR_WOULDBLOCK && errcode != ERROR_INPROGRESS) {
00351             throw Exception(ERR_CONNECT, errcode, "connect");
00352         }
00353 
00354         // wait for our socket to be connected
00355         waitForConnect(aSocket);
00356     }
00357 }
00358 
00359 void Socket::setSocketOptions(SOCKET aSocket)
00360 {
00361     int rc;
00362 
00363     // turn off the Nagle algorithm as this socket is used for small requests which are
00364     // subject to delays otherwise. On Windows this can introduce delays of up to 200ms 
00365     // for a remote server (local servers are on the same TCP stack have no delay).
00366     int nodelay = 1;
00367     rc = setsockopt(aSocket, IPPROTO_TCP, TCP_NODELAY, (const char*) &nodelay, sizeof(nodelay));
00368     if (rc != 0) {
00369         const int errcode = GetLastSocketErrno();
00370         throw Exception(ERR_CONNECT, errcode, "setsockopt/ndelay");
00371     }
00372 
00373     rc = setSocketTimeout(aSocket, SO_RCVTIMEO, mRecvTimeout);
00374     if (rc != 0) {
00375         const int errcode = GetLastSocketErrno();
00376         throw Exception(ERR_CONNECT, errcode, "setsockopt/rcvtimeo");
00377     }
00378 
00379     rc = setSocketTimeout(aSocket, SO_SNDTIMEO, mSendTimeout);
00380     if (rc != 0) {
00381         const int errcode = GetLastSocketErrno();
00382         throw Exception(ERR_CONNECT, errcode, "setsockopt/sndtimeo");
00383     }
00384 }
00385 
00386 void Socket::setSocketBufferSize(SOCKET aSocket)
00387 {
00388     if (mBufferSize <= 0) {
00389         return;
00390     }
00391 
00392     int rc;
00393     int len = 0;
00394     socklen_t siz;
00395 
00396     siz = sizeof(len);
00397     rc = getsockopt(aSocket, SOL_SOCKET, SO_RCVBUF, (char*) &len, &siz);
00398     if (rc < 0 || len < mBufferSize) {
00399         len = mBufferSize;
00400         rc = setsockopt(aSocket, SOL_SOCKET, SO_RCVBUF, (const char*) &len, sizeof(len));
00401         if (rc != 0) {
00402             const int errcode = GetLastSocketErrno();
00403             throw Exception(ERR_CONNECT, errcode, "setsockopt/rcvbuf");
00404         }
00405     }
00406 
00407     siz = sizeof(len);
00408     rc = getsockopt(aSocket, SOL_SOCKET, SO_SNDBUF, (char*) &len, &siz);
00409     if (rc < 0 || len < mBufferSize) {
00410         len = mBufferSize;
00411         rc = setsockopt(aSocket, SOL_SOCKET, SO_SNDBUF, (const char*) &len, sizeof(len));
00412         if (rc != 0) {
00413             const int errcode = GetLastSocketErrno();
00414             throw Exception(ERR_CONNECT, errcode, "setsockopt/sndbuf");
00415         }
00416     }
00417 }
00418 
00419 void Socket::Connect(const char * aIpAddress, int aPort)
00420 {
00421     mTrace.Trace(CLINFO, 
00422         "Connect socket to %s:%d (connect: %dms, send: %dms, recv: %dms, buffer: %d)",
00423         aIpAddress, aPort, mConnectTimeout, mSendTimeout, mRecvTimeout, mBufferSize
00424         );
00425 
00426     Disconnect();
00427 
00428     SOCKET s = INVALID_SOCKET;
00429     try {
00430         sockaddr_in server;
00431         server.sin_family = AF_INET;
00432         server.sin_port = htons((short)aPort);
00433         server.sin_addr.s_addr = inet_addr(aIpAddress);
00434         if (server.sin_addr.s_addr == INADDR_NONE) {
00435             const int errcode = GetLastSocketErrno();
00436             throw Exception(ERR_CONNECT, errcode, "invalid address");
00437         }
00438 
00439         s = socket(AF_INET, SOCK_STREAM, 0);
00440         if (s == INVALID_SOCKET) {
00441             const int errcode = GetLastSocketErrno();
00442             throw Exception(ERR_CONNECT, errcode, "failed to create socket");
00443         }
00444         mTrace.Trace(CLDEBUG, "socket fd = int %d, unsigned %u", (int) s, (unsigned) s);
00445 
00446         setCloseOnExec(s);
00447         setSocketBlocking(s, false);
00448         connectSocket(s, &server);
00449         setSocketBlocking(s, true);
00450         setSocketBufferSize(s);
00451         setSocketOptions(s);
00452 
00453         mSocket = s;
00454     }
00455     catch (const Exception & e) {
00456         WideString sErrorMsg;
00457         GetLastError(e.mCode, sErrorMsg);
00458         mTrace.Trace(CLERROR, "Connect socket to %s:%d failed. Error: %s, %d, %S",
00459             aIpAddress, aPort, e.mDetail, e.mCode, sErrorMsg.c_str());
00460         if (s != INVALID_SOCKET) closesocket(s);
00461         throw;
00462     }
00463 
00464     int localPort = 0;
00465     std::string localIp;
00466     struct sockaddr_in local;
00467     socklen_t addrlen = sizeof(local);
00468     if (getsockname(mSocket, (struct sockaddr *)&local, &addrlen) == 0 
00469         && local.sin_family == AF_INET 
00470         && addrlen == sizeof(local))
00471     {
00472         localPort = ntohs(local.sin_port);
00473         localIp = inet_ntoa(local.sin_addr);
00474     }
00475 
00476     mTrace.Trace(CLINFO, "Connect socket to %s:%d succeeded (local %s:%d)", 
00477         aIpAddress, aPort, localIp.c_str(), localPort);
00478 }
00479 
00480 void
00481 Socket::SendBytes(
00482     const char *    aBuf, 
00483     size_t          aBufSiz
00484     )
00485 {
00486     if (aBufSiz == 0) {
00487         return;
00488     }
00489 
00490 #ifdef CROSSBASE_API
00491     if (mTrace.IsThisModuleTracing(CLULTRA)) {
00492         // when ULTRA is enabled, log the actual data received
00493         std::string hex;
00494         const size_t MAX_LOG_SIZE = 4096;
00495         CrossTrace::hexdump(hex, aBuf, std::min(aBufSiz, MAX_LOG_SIZE));
00496         mTrace.Trace(CLULTRA, "sending %lu bytes:\n%s%s", 
00497             (unsigned long) aBufSiz, hex.c_str(), 
00498             aBufSiz > MAX_LOG_SIZE ? "\n    XXXX    *** TRUNCATED ***" : "");
00499     }
00500 #endif
00501 
00502     // blocking send, will return the number of bytes sent or 
00503     // it is an error
00504     int rc = (int) send(mSocket, aBuf, (int) aBufSiz, MSG_NOSIGNAL);
00505     while (handleEINTR(rc)) {
00506         mTrace.Trace(CLDEBUG, "send returned EINTR, retrying");
00507         rc = send(mSocket, aBuf, (int) aBufSiz, MSG_NOSIGNAL);
00508     }
00509     if (rc == (int) aBufSiz) {
00510         return;
00511     }
00512 
00513     // on error disconnect and throw Exception
00514     const int errcode = GetLastSocketErrno();
00515     WideString sErrorMsg;
00516     GetLastError(errcode, sErrorMsg);
00517     mTrace.Trace(CLINFO, "send returned %d, errno = %d, %S", 
00518         rc, errcode, sErrorMsg.c_str());
00519     Disconnect();
00520     throw Exception(ERR_SEND, errcode, "send");
00521 }
00522 
00523 int 
00524 Socket::ReceiveBytes(
00525     char *  aBuf, 
00526     int     aBufSiz
00527     ) 
00528 {
00529     int rc = recv(mSocket, aBuf, aBufSiz, 0);
00530     while (handleEINTR(rc)) {
00531         mTrace.Trace(CLDEBUG, "recv returned EINTR, retrying");
00532         rc = recv(mSocket, aBuf, aBufSiz, 0);
00533     }
00534 
00535     if (rc <= 0) {
00536         // on error disconnect and throw Exception
00537         const int errcode = GetLastSocketErrno();
00538         WideString sErrorMsg;
00539         GetLastError(errcode, sErrorMsg);
00540         mTrace.Trace(CLINFO, "recv returned %d, errno = %d, %S", 
00541             rc, errcode, sErrorMsg.c_str());
00542         Disconnect();
00543         throw Exception(ERR_RECV, errcode, "recv");
00544     }
00545 
00546 #ifdef CROSSBASE_API
00547     if (mTrace.IsThisModuleTracing(CLULTRA)) {
00548         // when ULTRA is enabled, log the actual data received
00549         std::string hex;
00550         const int MAX_LOG_SIZE = 4096;
00551         CrossTrace::hexdump(hex, aBuf, std::min(rc, MAX_LOG_SIZE));
00552         mTrace.Trace(CLULTRA, "received %d bytes:\n%s%s", 
00553             rc, hex.c_str(), rc > MAX_LOG_SIZE ? "\n    XXXX    *** TRUNCATED ***" : "");
00554     }
00555 #endif
00556 
00557     return rc;
00558 }
00559 
00560 char 
00561 Socket::GetByte() // throw Exception
00562 { 
00563     if (mIdx >= mBufLen) {
00564         mTrace.Trace(CLULTRA, "GetByte() reloading buffer");
00565         mIdx = 0;
00566         mBufLen = ReceiveBytes(mBuf, MAXBUF);
00567     }
00568     const int idx = mIdx++;
00569     const char ch = mBuf[idx];
00570     return ch;
00571 }
00572 
00573 int 
00574 Socket::GetBytes(
00575     char *  aBuf, 
00576     int     aBufSiz
00577     ) 
00578 {
00579     if (mIdx < mBufLen) {
00580         mTrace.Trace(CLULTRA, "GetBytes() returning fom buffer: req %d, curr idx %d, curr len %d, avail %d",
00581             aBufSiz, mIdx, mBufLen, mBufLen - mIdx);
00582 
00583         int nLen = mBufLen - mIdx;
00584         if (nLen > aBufSiz) {
00585             nLen = aBufSiz;
00586         }
00587         memcpy(aBuf, mBuf + mIdx, nLen);
00588         mIdx += nLen;
00589         if (mIdx == mBufLen) {
00590             mIdx = mBufLen = 0;
00591         }
00592         return nLen;
00593     }
00594 
00595     mTrace.Trace(CLULTRA, "GetBytes() receiving bytes: req %d", aBufSiz);
00596     return ReceiveBytes(aBuf, aBufSiz);
00597 }
00598 
00599 void 
00600 Socket::DiscardBytes(
00601     int aCount
00602     ) 
00603 {
00604     while (aCount > 0) {
00605         if (mIdx == mBufLen) {
00606             mIdx = 0;
00607             mBufLen = ReceiveBytes(mBuf, MAXBUF);
00608         }
00609 
00610         int nLen = mBufLen - mIdx;
00611         if (nLen > aCount) {
00612             mIdx += aCount;
00613             break;
00614         }
00615 
00616         aCount -= nLen;
00617         mIdx = mBufLen;
00618     }
00619 }
00620 
00621 void 
00622 Socket::ReceiveLine(
00623     std::string & aLine,
00624     bool          aTrim
00625     )
00626 {
00627     // get the value
00628     char c;
00629     aLine = (c = GetByte());
00630     while (c != '\n') {
00631         aLine += (c = GetByte());
00632     }
00633 
00634     // trim all space from the end
00635     if (aTrim) {
00636         while (!aLine.empty() && isspace((int)aLine[aLine.size()-1])) {
00637             aLine.resize(aLine.size()-1);
00638         }
00639     }
00640 }
00641 
00642 void
00643 Socket::GetLastError(
00644     int             aError,
00645     WideString &    aErrorMsg
00646     )
00647 {
00648 #ifdef _WIN32
00649     // load wininet.dll for the error messages 
00650     DWORD dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER |
00651         FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
00652     HMODULE hModule = LoadLibraryExA("wininet.dll", NULL,
00653         LOAD_LIBRARY_AS_DATAFILE | DONT_RESOLVE_DLL_REFERENCES);
00654     if (hModule) {
00655         dwFlags |= FORMAT_MESSAGE_FROM_HMODULE;
00656     }
00657 
00658     // note that we request a US English error message. This is to make the
00659     // message the same on servers regardless of the language that is running.
00660     // since US English is a fallback language for error messages it will always
00661     // return a valid message.
00662     LPWSTR pBuf = NULL;
00663     DWORD dwLen = FormatMessageW(
00664         dwFlags, hModule, (DWORD) aError, 
00665         MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), 
00666         (LPWSTR)&pBuf, 0, NULL);
00667 
00668     // trim the CRLF off the end
00669     while (dwLen > 0 && iswspace(pBuf[dwLen-1])) {
00670         pBuf[--dwLen] = 0;
00671     }
00672     aErrorMsg = pBuf;
00673     LocalFree(pBuf);
00674 
00675     // free the library if we loaded it
00676     if (hModule) {
00677         FreeLibrary(hModule);
00678     }
00679 #elif defined(CROSSBASE_API)
00680     ClGetLastError(aError, aErrorMsg);
00681 #else
00682     const char * pError = strerror(aError);
00683     wchar_t buf[256];
00684     mbstowcs(buf, pError, 256);
00685     aErrorMsg = buf;
00686 #endif
00687 }
00688 
00689 #ifdef CROSSBASE_API
00690 END_CL_NAMESPACE
00691 #endif