|
MemCacheClient
|
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
1.7.3