📄 sockutil.cpp
字号:
void SocketSet::reset(){ FD_ZERO(&set); maxSocket = 0; used = false; numSockets = 0;}int SocketSet::select(bool read, bool write, bool except, /*const*/ struct timeval *timeout){ xassert(!used); xassert(numSockets > 0); // need at least one valid socket! selectDiagnostic("blocked on select()"); int result = ::select( maxSocket+1, // nfds read? &set : (fd_set*)NULL, // readfds write? &set : (fd_set*)NULL, // writefds except? &set : (fd_set*)NULL, // exceptfds timeout); if (result == SOCKET_ERROR) { xsocket((SOCKET)NULL, "select"); } selectDiagnostic("select() unblocked"); used = true; return result;}// ----------------- xsocket -----------------------// compose a complete error message stringstring socketErrString(char const *msg){ int code = getSocketErrorCode(); char const *codetext = errorCodeString(code); if (!codetext) { codetext = "unknown error"; } return stringb( msg << ": " << codetext << " (code " << code << ")");}// handle an error// (originally had 3rd param named 'close'; that is a problem// on unix, where 'closesocket' is #defined to 'close')void xsocket(SOCKET s, char const *msg, bool doClose){ xSocket x(s, socketErrString(msg)); if (doClose && closesocket(s) != SOCKET_ERROR) { decOpenSockets(); } THROW(x);}void xsocketClosed(SOCKET s){ xSocket tmp(s); THROW(tmp);}// ------------------- endian junk ---------------------// force the definition of either SAFETP_LITTLE_ENDIAN or SAFETP_BIG_ENDIAN#if !defined(SAFETP_LITTLE_ENDIAN) && !defined(SAFETP_BIG_ENDIAN)# error define SAFETP_LITTLE_ENDIAN or SAFETP_BIG_ENDIAN#endif// return true if this is a big-endian machine, false if is// little-endian (reference to endianness discussion might// be nice here..)#if 0 // this code crashes gcc-2.95.2 -O2 on solaris/sparcbool isBigEndian(){ union { int i; // machine word byte b[sizeof(int)]; // b[0] overlaid with first byte of i }; i = 0xFF; // set lsb, zero all others return b[0] != 0xFF;}#else // 1// x must be global to work around an egcs-1.1.2 optimizer bugstatic int endianTestValue = 0; // start with all bits 0bool isBigEndian(){ // little endian: sets endianTestValue to 1 // big endian: sets endianTestValue to a large number (e.g. 0x01000000 if 32 bits) *((char*)(&endianTestValue)) = 1; if (endianTestValue == 1) { return false; // little } else { return true; // big }}#endif // 0/1// --------------- nonportable routines ----------------------------#ifdef __WIN32__// (exported)// return string associated with a given codechar const *errorCodeString(int code){ static struct { int code; char const *message; } const arr[] = { { WSAEACCES, "Broadcast address requested, proper flags not set" }, { WSAEADDRINUSE, "Specified address already in use" }, { WSAEADDRNOTAVAIL, "Address not available or accessible" }, { WSAEAFNOSUPPORT, "Address format not supported" }, { WSAEALREADY, "\"already\" something (?)" }, // already what? { WSAEBADF, "Bad file number" }, { WSAECONNABORTED, "Connection aborted, possibly due to timeout" }, { WSAECONNREFUSED, "Connection refused" }, { WSAECONNRESET, "Connection reset by remote side" }, { WSAEDESTADDRREQ, "Destination address required" }, { WSAEDISCON, "Socket disconnected" }, { WSAEDQUOT, "\"DQUOT\" (?)" }, // what does this mean? { WSAEFAULT, "Memory buffer too small" }, { WSAEHOSTDOWN, "Remote host is down" }, { WSAEHOSTUNREACH, "Remote host is unreachable" }, { WSAEINPROGRESS, "Blocking call in progress" }, { WSAEINTR, "Blocking call interrupted" }, { WSAEINVAL, "Invalid parameter" }, { WSAEISCONN, "Socket already connected" }, { WSAELOOP, "Routing loop discovered (?)" }, { WSAEMFILE, "No more file descriptors available" }, { WSAEMSGSIZE, "Message size exceeds maximum datagram size" }, { WSAENAMETOOLONG, "Name too long" }, { WSAENETDOWN, "Network subsystem failure detected" }, { WSAENETRESET, "Connection was reset" }, { WSAENETUNREACH, "Network is unreachable" }, { WSAENOBUFS, "Insufficient buffer space available" }, { WSAENOPROTOOPT, "Invalid protocol option" }, { WSAENOTCONN, "Socket is not connected" }, { WSAENOTEMPTY, "Buffer not empty" }, { WSAENOTSOCK, "Connection is already closed" }, { WSAEOPNOTSUPP, "Operation not supported (e.g. OOB)" }, { WSAEPFNOSUPPORT, "Protocol family not supported" }, { WSAEPROCLIM, "\"PROCLIM\" (?)" }, // anyone know what this means? { WSAEPROTONOSUPPORT, "The specified protocol is not supported" }, { WSAEPROTOTYPE, "Specified protocol is wrong type" }, { WSAEREMOTE, "Error at remote host" }, { WSAESHUTDOWN, "Socket has been shut down" }, { WSAESOCKTNOSUPPORT, "Socket type not supported" }, { WSAESTALE, "Stale handle" }, { WSAETIMEDOUT, "Connection timed out" }, { WSAETOOMANYREFS, "Too many references" }, { WSAEUSERS, "Too many users" }, { WSAEWOULDBLOCK, "Asynchronous operation cannot be scheduled" }, { WSAHOST_NOT_FOUND, "Authoritative Answer Host not found" }, { WSANO_ADDRESS, "No address, look for MX record (?)" }, { WSANO_DATA, "Valid name, no data record of requested type" }, { WSANO_RECOVERY, "Non-recoverable error" }, { WSANOTINITIALISED, "Winsock not initialized" }, { WSASYSNOTREADY, "Network subsystem not ready" }, { WSATRY_AGAIN, "Non-Authoritative Host not found" }, { WSAVERNOTSUPPORTED, "Winsock version not supported" }, { 0, "No error" } }; loopi(TABLESIZE(arr)) { if (arr[i].code == code) { return arr[i].message; } } // unknown code return NULL;}// (exported)int getSocketErrorCode(){ return WSAGetLastError();}// (exported)void socket_lib_init(){ WSADATA d; if (0 != WSAStartup(MAKEWORD(1,1), &d)) { xsocket(INVALID_SOCKET, "WSAStartup"); } // randomize srand(getProcessId());}// (exported)void socket_lib_deinit(){ // added this because Dan says he wants sftpd to do this WSACleanup();}#else // -- win32 above, unix below --#include <errno.h> // errno, strerrorchar const *errorCodeString(int code){ return strerror(code);}int getSocketErrorCode(){ return errno;}void socket_lib_init(){ // nothing special required for berkeley sockets // but I want to randomize rand() so that two different // server processes won't request the same sequence of ports // (which would exacerbate the reliability problem with // SO_REUSEADDR) srand(getProcessId());}void socket_lib_deinit(){ // nothing special required for berkeley sockets}#endif // ^^ unix// --------------------- test code ----------------------#ifdef TEST_SOCKUTIL#include "test.h" // USUAL_MAIN#ifdef __UNIX__#include <sys/wait.h> // wait// actually try to make some connections and send some datavoid communicationTests(){ printf("start of communicationTests\n"); int childPid = fork(); if (childPid != 0) { // --------------- parent --------------- // don't let this take too long alarm(20); // listen for a connection from the child printf("parent: waiting for child to connect\n"); SOCKET listener = interface_listen_socket(INADDR_LOOPBACK, 2345); SOCKET s = accept_socket(listener); close_socket(listener); // connected; send something trivial printf("parent: sending int\n"); sendNBO32(s, 0x4567890A); // receive something trivial printf("parent: waiting for char\n"); char ch = recvChar(s); printf("parent: received char: %c\n", ch); xassert(ch == 'x'); // listen on a range printf("parent: making a range listener\n"); PortRange range(5000,5050); listener = interface_listen_socket_range(INADDR_LOOPBACK, range); // check that we got a port in the range requested int port = getLocalPort(listener); printf("parent: got a listener on port %d\n", port); xassert(5000 <= port && port <= 5050); // tell the client which port we got sendNBO32(s, (unsigned long)port); // wait for the connection SOCKET t = accept_socket(listener); close_socket(listener); printf("parent: got child's second connection\n"); // close everything down close_socket(t); close_socket(s); // wait for child to exit int status; wait(&status); printf("parent: child exited with code %d\n", status); xassert(status == 0); } else { // ---------------- child --------------- // wait for parent to be ready to accept a connection sleep(1); // connect to parent printf("child: connecting\n"); SOCKET s = connect_socket(INADDR_LOOPBACK, 2345); // receive the integer printf("child: connected; waiting for int\n"); unsigned long val = recvNBO32(s); printf("child: received 0x%lX\n", val); xassert(val == 0x4567890A); // send a char printf("child: sending char\n"); sendChar(s, 'x'); // receive port that parent is now listening at int remotePort = (int)recvNBO32(s); // connect to it, using a range for my local port printf("child: connecting to parent at port %d\n", remotePort); PortRange range(6000,6070); SOCKET t = connect_socket(INADDR_LOOPBACK, remotePort, &range); // verify range int localPort = getLocalPort(t); printf("child: my local port is %d\n", localPort); xassert(6000 <= localPort && localPort <= 6070); // close it all down close_socket(t); close_socket(s); exit(0); } printf("end of communicationTests\n");} #else // windowsvoid communicationTests(){ // windows doesn't have fork (right?) so this would be more painful.. // forget it}#endif // !__UNIX__void testParseDottedDecimal(char const *decimal, IPAddress expected){ try { IPAddress got = parseDottedDecimal(decimal); xassert(got == expected); } catch (...) { // did we expect the failure? xassert(expected == INADDR_NONE); }}void testParseAddrAndPort(char const *addrAndPort, IPAddress expAddr, int expPort){ try { IPAddress gotAddr; int gotPort; parseAddrAndPort(gotAddr, gotPort, addrAndPort); xassert(gotAddr == expAddr && gotPort == expPort); } catch (...) { xassert(expAddr == INADDR_NONE); }}int entry(){ socket_lib_init(); // reduce clutter xBase::logExceptions = false; // verify endianness #if defined(SAFETP_LITTLE_ENDIAN) if (isBigEndian()) { xfailure("SAFETP_LITTLE_ENDIAN is defined but isBigEndian() returns true"); } #elif defined(SAFETP_BIG_ENDIAN) if (!isBigEndian()) { xfailure("SAFETP_BIG_ENDIAN is defined but isBigEndian() returns false"); } #else xfailure("neither SAFETP_LITTLE_ENDIAN nor SAFETP_BIG_ENDIAN are defined"); #endif // test vectors for parseDottedDecimal testParseDottedDecimal("1.2.3.4", 0x01020304); testParseDottedDecimal("0.0.0.0", 0); testParseDottedDecimal("255.255.255.255", 0xffffffff); testParseDottedDecimal("1234", INADDR_NONE); // test vectors for parseAddrAndPort testParseAddrAndPort("1.2.3.4:5", 0x01020304, 5); testParseAddrAndPort("67", INADDR_ANY, 67); testParseAddrAndPort(":", INADDR_NONE, 0); testParseAddrAndPort("2.3.4.5:", INADDR_NONE, 0); // verify that binding the same port twice doesn't work (at least // from the same process...) SOCKET s = listen_socket(3456); try { listen_socket(3456); xfailure("it let me bind the same port twice!"); } catch (xSocket&) { printf("good: it didn't let me bind the same port twice\n"); } close_socket(s); communicationTests(); printf("sockutil passed tests\n"); socket_lib_deinit(); return 0;}USUAL_MAIN#endif // TEST_SOCKUTIL
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -