diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ad61c9..4c45873 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,7 @@ cmake_minimum_required(VERSION 2.8 FATAL_ERROR) project(clsocket) + # set up versioning. set(BUILD_MAJOR "1") set(BUILD_MINOR "4") @@ -19,7 +20,6 @@ src/StatTimer.h SET(CLSOCKET_SOURCES src/SimpleSocket.cpp -src/ActiveSocket.cpp src/PassiveSocket.cpp ) @@ -44,6 +44,18 @@ elseif(WIN32) SET(PROJECT_LIBS Ws2_32.lib) if(MINGW) # Special MINGW stuff here + OPTION(CLSOCKET_OWN_INET_PTON "Use own inet_pton() implementation (required on MINGW)" ON) + if(CLSOCKET_OWN_INET_PTON) + add_definitions(-DCLSOCKET_OWN_INET_PTON) + endif() + # see https://cmake.org/pipermail/cmake/2012-September/051970.html + # see http://stackoverflow.com/questions/13768515/how-to-do-static-linking-of-libwinpthread-1-dll-in-mingw + # looks, the following compiler options produce linker errors. -static, .. options should only be used for linker + #set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static -static-libgcc") + #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static -static-libgcc -static-libstdc++") + # commenting out upper two CMAKE_C[XX]_FLAG now compiles and links with and without CLSOCKET_SHARED cmake option + set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS} -static -static-libgcc -s") + set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "${CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS} -static -static-libgcc -static-libstdc++ -s") elseif(MSVC) # Special MSVC stuff here add_definitions(-D_WINSOCK_DEPRECATED_NO_WARNINGS) @@ -63,12 +75,29 @@ if(CLSOCKET_SHARED) else() ADD_LIBRARY(clsocket SHARED ${CLSOCKET_SOURCES}) endif() + # linking against shared library requires the symbols + target_compile_definitions(clsocket PRIVATE EXPORT_CLSOCKET_SYMBOLS) + # have internal symbols hidden by default + set_target_properties(clsocket PROPERTIES CXX_VISIBILITY_PRESET hidden) else() if(CLSOCKET_DEP_ONLY) ADD_LIBRARY(clsocket STATIC EXCLUDE_FROM_ALL ${CLSOCKET_SOURCES}) else() ADD_LIBRARY(clsocket STATIC ${CLSOCKET_SOURCES}) endif() + # no need for export symbols with a static library + + if(MSVC) + # Special MSVC stuff here + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -MT") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -MT") + set(CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL} -MT") + set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} -MT") + set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -MT") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -MT") + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -MTd") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -MTd") + endif() endif() TARGET_LINK_LIBRARIES(clsocket ${PROJECT_LIBS}) @@ -83,21 +112,50 @@ endif() set_target_properties(clsocket PROPERTIES VERSION ${BUILD_VERSION} SOVERSION ${BUILD_MAJOR}) -if(UNIX) +if(UNIX OR (WIN32 OR MINGW)) OPTION(CLSOCKET_EXAMPLES "Build the examples" OFF) if(CLSOCKET_EXAMPLES) - ADD_EXECUTABLE(clsocket-example examples/RecvAsync.cpp) - TARGET_LINK_LIBRARIES(clsocket-example clsocket pthread) + + if (NOT MSVC) + # pthread not available with MSVC + ADD_EXECUTABLE(clsocket-example examples/RecvAsync.cpp) + TARGET_LINK_LIBRARIES(clsocket-example clsocket pthread) + if(NOT CLSOCKET_DEP_ONLY) + install(TARGETS clsocket-example DESTINATION bin) + endif() + endif() + + ADD_EXECUTABLE(testmaxconnections-example examples/TestMaxConnections.cpp) + TARGET_LINK_LIBRARIES(testmaxconnections-example clsocket) if(NOT CLSOCKET_DEP_ONLY) - install(TARGETS clsocket-example DESTINATION bin) + install(TARGETS testmaxconnections-example DESTINATION bin) endif() + set_target_properties(testmaxconnections-example + PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED YES + CXX_EXTENSIONS NO + ) ADD_EXECUTABLE(querydaytime-example examples/QueryDayTime.cpp) TARGET_LINK_LIBRARIES(querydaytime-example clsocket) ADD_EXECUTABLE(echoserver-example examples/EchoServer.cpp) TARGET_LINK_LIBRARIES(echoserver-example clsocket) + + ADD_EXECUTABLE(delayedechoserver-example examples/DelayedEchoServer.cpp) + TARGET_LINK_LIBRARIES(delayedechoserver-example clsocket) + + ADD_EXECUTABLE(txtoserver-example examples/TxToServer.cpp) + TARGET_LINK_LIBRARIES(txtoserver-example clsocket) + + ADD_EXECUTABLE(udpserver-example examples/UdpServer.cpp) + TARGET_LINK_LIBRARIES(udpserver-example clsocket) + + ADD_EXECUTABLE(txtoudpserver-example examples/TxToUdpServer.cpp) + TARGET_LINK_LIBRARIES(txtoudpserver-example clsocket) + endif() endif() diff --git a/examples/DelayedEchoServer.cpp b/examples/DelayedEchoServer.cpp new file mode 100644 index 0000000..84f1c10 --- /dev/null +++ b/examples/DelayedEchoServer.cpp @@ -0,0 +1,78 @@ + +#include "PassiveSocket.h" // Include header for active socket object definition + +#define MAX_PACKET 4096 + + +#ifdef WIN32 +#include + + static void sleep( unsigned int seconds ) + { + Sleep( seconds * 1000 ); + } +#elif defined(_LINUX) || defined (_DARWIN) + #include +#endif + +int main(int argc, char **argv) +{ + CPassiveSocket socket; + CSimpleSocket *pClient = NULL; + + const char * bindAddr = 0; + unsigned bindPort = 6789; + if ( argc <= 1 ) + fprintf(stderr, "usage: %s [ []]\n", argv[0] ); + if ( 1 < argc ) + bindAddr = argv[1]; + if ( 2 < argc ) + bindPort = atoi(argv[2]) & 65535; + + //-------------------------------------------------------------------------- + // Initialize our socket object + //-------------------------------------------------------------------------- + socket.Initialize(); + fprintf(stderr, "binding to %s:%u\n", bindAddr, bindPort); + socket.Listen( bindAddr, uint16(bindPort) ); // not "127.0.0.1" to allow testing with remotes + + while (true) + { + if ((pClient = socket.Accept()) != NULL) + { + fprintf(stderr, "\nLocal is %s. Local: %s:%u " + , ( pClient->IsServerSide() ? "Server" : "Client" ) + , pClient->GetLocalAddr(), (unsigned)pClient->GetLocalPort()); + fprintf(stderr, "Peer: %s:%u\n", pClient->GetPeerAddr(), (unsigned)pClient->GetPeerPort()); + + //---------------------------------------------------------------------- + // Receive request from the client. + //---------------------------------------------------------------------- + if (pClient->Receive(MAX_PACKET)) + { + //------------------------------------------------------------------ + // Send response to client and close connection to the client. + //------------------------------------------------------------------ + int32 rx = pClient->GetBytesReceived(); + uint8 *txt = pClient->GetData(); + fprintf(stderr, "received %d bytes:\n", rx ); + for ( int k = 0; k < rx; ++k ) + fprintf(stderr, "%c", txt[k]); + fprintf(stderr, "\n"); + sleep(5); + pClient->Send( txt, rx ); + fprintf(stderr, "sent back after 5 seconds\n" ); + pClient->Close(); + } + + delete pClient; + } + } + + //----------------------------------------------------------------------------- + // Receive request from the client. + //----------------------------------------------------------------------------- + socket.Close(); + + return 1; +} diff --git a/examples/EchoServer.cpp b/examples/EchoServer.cpp index 4a27d27..7d39486 100644 --- a/examples/EchoServer.cpp +++ b/examples/EchoServer.cpp @@ -1,33 +1,67 @@ #include "PassiveSocket.h" // Include header for active socket object definition -#define MAX_PACKET 4096 +#define MAX_PACKET 4096 int main(int argc, char **argv) { CPassiveSocket socket; CActiveSocket *pClient = NULL; + const char * bindAddr = 0; + unsigned bindPort = 6789; + if ( argc <= 1 ) + fprintf(stderr, "usage: %s [ []]\n", argv[0] ); + if ( 1 < argc ) + bindAddr = argv[1]; + if ( 2 < argc ) + bindPort = atoi(argv[2]) & 65535; + //-------------------------------------------------------------------------- // Initialize our socket object //-------------------------------------------------------------------------- socket.Initialize(); - - socket.Listen("127.0.0.1", 6789); + fprintf(stderr, "binding to %s:%u\n", bindAddr, bindPort); + socket.Listen( bindAddr, uint16(bindPort) ); // not "127.0.0.1" to allow testing with remotes while (true) { if ((pClient = socket.Accept()) != NULL) { + fprintf(stderr, "\nLocal is %s. Local: %s:%u " + , ( pClient->IsServerSide() ? "Server" : "Client" ) + , pClient->GetLocalAddr(), (unsigned)pClient->GetLocalPort()); + fprintf(stderr, "Peer: %s:%u\n", pClient->GetPeerAddr(), (unsigned)pClient->GetPeerPort()); + //---------------------------------------------------------------------- // Receive request from the client. //---------------------------------------------------------------------- + + pClient->WaitUntilReadable(100); + int32 peekRx = pClient->GetNumReceivableBytes(); + if (pClient->Receive(MAX_PACKET)) { + fprintf(stderr, "\n%s. Local: %s:%u Peer: %s:%u\n" + , ( pClient->IsServerSide() ? "Local is Server" : "Local is Client" ) + , pClient->GetLocalAddr(), (unsigned)pClient->GetLocalPort() + , pClient->GetPeerAddr(), (unsigned)pClient->GetPeerPort() + ); + //------------------------------------------------------------------ // Send response to client and close connection to the client. //------------------------------------------------------------------ - pClient->Send( pClient->GetData(), pClient->GetBytesReceived() ); + int32 rx = pClient->GetBytesReceived(); + + if ( peekRx != rx && !( peekRx == 0 && rx < 0 ) ) + fprintf( stderr, "GetNumReceivableBytes() = %d != %d = Receive() !\n", peekRx, rx ); + + uint8 *txt = pClient->GetData(); + pClient->Send( txt, rx ); + fprintf(stderr, "received and sent %d bytes:\n", rx ); + for ( int k = 0; k < rx; ++k ) + fprintf(stderr, "%c", txt[k]); + fprintf(stderr, "\n"); pClient->Close(); } diff --git a/examples/QueryDayTime.cpp b/examples/QueryDayTime.cpp index 7abf7aa..a914cf5 100644 --- a/examples/QueryDayTime.cpp +++ b/examples/QueryDayTime.cpp @@ -18,8 +18,16 @@ int main(int argc, char **argv) // Create a connection to the time server so that data can be sent // and received. //-------------------------------------------------------------------------- - if (socket.Open("time-C.timefreq.bldrdoc.gov", 13)) + const char * timeServer = ( argc >= 2 ) ? argv[1] : "time-C.timefreq.bldrdoc.gov"; + int PortNo = ( argc >= 3 ) ? atoi(argv[2]) : 13; + fprintf(stderr, "trying to connect to timeserver %s:%d ..\n", timeServer, PortNo); + if (socket.Open(timeServer, (uint16)PortNo)) { + fprintf(stderr, "\nLocal is %s. Local: %s:%u " + , ( socket.IsServerSide() ? "Server" : "Client" ) + , socket.GetLocalAddr(), (unsigned)socket.GetLocalPort()); + fprintf(stderr, "Peer: %s:%u\n", socket.GetPeerAddr(), (unsigned)socket.GetPeerPort()); + //---------------------------------------------------------------------- // Send a requtest the server requesting the current time. //---------------------------------------------------------------------- diff --git a/examples/RecvAsync.cpp b/examples/RecvAsync.cpp index d4e415f..7f31211 100644 --- a/examples/RecvAsync.cpp +++ b/examples/RecvAsync.cpp @@ -43,7 +43,7 @@ void *CreateTCPEchoServer(void *param) } } - sleep(100); + sleep(1); delete pClient; } @@ -78,23 +78,41 @@ int main(int argc, char **argv) int numBytes = -1; int bytesReceived = 0; - client.Select(); + client.WaitUntilReadable(100); // wait up to 100 ms - while (bytesReceived != strlen(TEST_PACKET)) + //while (bytesReceived != strlen(TEST_PACKET)) + while ( numBytes != 0 ) // Receive() until socket gets closed { + int32 peekRx = client.GetNumReceivableBytes(); + numBytes = client.Receive(MAX_PACKET); + if ( peekRx != numBytes && !( peekRx == 0 && numBytes < 0 ) ) + fprintf( stderr, "\nGetNumReceivableBytes() = %d != %d = Receive() !\n", peekRx, numBytes ); + if (numBytes > 0) { bytesReceived += numBytes; memset(result, 0, 1024); memcpy(result, client.GetData(), numBytes); - printf("received %d bytes: '%s'\n", numBytes, result); + printf("\nreceived %d bytes: '%s'\n", numBytes, result); } + else if ( CSimpleSocket::SocketEwouldblock == client.GetSocketError() ) + fprintf(stderr, "."); + else if ( !client.IsNonblocking() && CSimpleSocket::SocketTimedout == client.GetSocketError() ) + fprintf(stderr, "w"); else { - printf("Received %d bytes\n", numBytes); + printf("\nreceived %d bytes\n", numBytes); + + fprintf(stderr, "Error: %s\n", client.DescribeError() ); + fprintf(stderr, "Error: %s, code %d\n" + , ( client.IsNonblocking() ? "NonBlocking socket" : "Blocking socket" ) + , (int) client.GetSocketError() + ); } + + client.WaitUntilReadable(100); // wait up to 100 ms } } } diff --git a/examples/TestMaxConnections.cpp b/examples/TestMaxConnections.cpp new file mode 100644 index 0000000..9df35d8 --- /dev/null +++ b/examples/TestMaxConnections.cpp @@ -0,0 +1,279 @@ + +#include "PassiveSocket.h" + +#include +#include +#include +#include + +#define MAX_CLIENTS 8192 +#define MAX_PACKET 4096 +//#define TEST_PACKET "Test Packet" +#define TEST_PACKET "Test" + + +struct thread_data +{ + const int max_clients; + const char * pszServerAddr; + short int nPort; + int nNumBytesToReceive; + int nTotalPayloadSize; + CPassiveSocket server_socket; + int nClients; + CSimpleSocket * client_sockets[MAX_CLIENTS]; + bool terminate; + bool ready; + + thread_data(int max_clients_) + : max_clients(max_clients_) + { + pszServerAddr = "127.0.0.1"; + nPort = 6789; + nNumBytesToReceive = 1; + nTotalPayloadSize = (int)strlen(TEST_PACKET); + nClients = 0; + terminate = false; + ready = false; + for (int k=0; k < MAX_CLIENTS; ++k) + client_sockets[k] = 0; + if (!server_socket.Initialize()) + { + fprintf(stderr, "Error: initializing socket!\n"); + return; + } + if (!server_socket.SetNonblocking()) + { + fprintf(stderr, "Error: server/listen socket could set Nonblocking!\n"); + return; + } + if (!server_socket.Listen(pszServerAddr, nPort)) + { + fprintf(stderr, "Error: listen failed!\n"); + return; + } + ready = true; + } + + ~thread_data() + { + printf("\nserver: shutting down server!\n"); + server_socket.Close(); + printf("server: cleanup/delete connections ..\n"); + for (int k = 0; k < nClients; ++k) + delete client_sockets[k]; + } + + bool serve(); +}; + + +bool thread_data::serve() +{ + if (!ready) + return false; + while (!terminate) + { + if (nClients >= max_clients) + { + fprintf(stderr, "\nserver: have served %d clients. shut down\n", nClients); + return false; + } + if (!server_socket.Select(0, 100000)) // 100ms + { + // fprintf(stderr, "server: wait select\n"); + continue; + } + + // printf("server: accept next connection %d ..\n", nClients); + CSimpleSocket *pClient = server_socket.Accept(); + if (!pClient) + { + if (server_socket.GetSocketError() == CPassiveSocket::SocketEwouldblock) + { + fprintf(stderr, "server: accept() would block\n"); + continue; + } + + fprintf(stderr, "server: Error at accept() for %d: code %d: %s\n", + nClients, (int)server_socket.GetSocketError(), server_socket.DescribeError() ); + break; + } + printf("server: accepted %d ..\n", nClients); + + client_sockets[nClients++] = pClient; + int nBytesReceived = 0; + while (!terminate && nBytesReceived < nTotalPayloadSize) + { + if (nBytesReceived += pClient->Receive(nNumBytesToReceive)) + { + pClient->Send((const uint8 *)pClient->GetData(), pClient->GetBytesReceived()); + } + } + // keep the new connection open! + } + + return true; +} + + +bool connect_new_client(CActiveSocket &client, int k, const thread_data &thData) +{ + bool conn_ok = client.Open("127.0.0.1", 6789); + if (!conn_ok) + { + fprintf(stderr, "Error: %d could not connect to server!\n", k); + return false; + } + + int32 r = client.Send((uint8 *)TEST_PACKET, strlen(TEST_PACKET)); + if (!r) + { + fprintf(stderr, "Error: connection %d closed from peer while sending packet!\n", k); + return false; + } + + int numBytes = -1; + int bytesReceived = 0; + char result[1024]; + + client.WaitUntilReadable(100); // wait up to 100 ms + + // Receive() until socket gets closed or we already received the full packet + while ( numBytes != 0 && bytesReceived < thData.nTotalPayloadSize ) + { + numBytes = client.Receive(MAX_PACKET); + + if (numBytes > 0) + { + bytesReceived += numBytes; + // memcpy(result, client.GetData(), numBytes); + // printf("\nreceived %d bytes: '%s'\n", numBytes, result); + } + else if ( CSimpleSocket::SocketEwouldblock == client.GetSocketError() ) + fprintf(stderr, "."); + else if ( !client.IsNonblocking() && CSimpleSocket::SocketTimedout == client.GetSocketError() ) + fprintf(stderr, "w"); + else + { + printf("\n%d: received %d bytes\n", k, numBytes); + + fprintf(stderr, "Error: %s\n", client.DescribeError() ); + fprintf(stderr, "Error: %s, code %d\n" + , ( client.IsNonblocking() ? "NonBlocking socket" : "Blocking socket" ) + , (int) client.GetSocketError() + ); + } + + if ( bytesReceived >= thData.nTotalPayloadSize ) + return true; // no need to wait + // else + // printf("read %d of %d\n", bytesReceived, thData.nTotalPayloadSize); + + client.WaitUntilReadable(100); // wait up to 100 ms + } + + if (bytesReceived >= thData.nTotalPayloadSize) + ; // printf("%d: received %d bytes response.\n", k, bytesReceived); + else + { + fprintf(stderr, "Error at %d: received %d bytes - expected %d!\n", k, bytesReceived, thData.nTotalPayloadSize); + return false; + } + if (!numBytes) + { + fprintf(stderr, "Error: connection %d closed from peer\n", k); + return false; + } + return true; +} + + + +int main(int argc, char **argv) +{ + int max_clients = MAX_CLIENTS; + int init_sockets_at_startup = 1; + + if ( 1 < argc ) + { + if (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-help") || !strcmp(argv[1], "-h") || !strcmp(argv[1], "/h")) + { + printf("usage: %s [ [] ]\n", argv[0]); + printf(" max_num_connections: number of connections to test. default = %d, max = %d\n", max_clients, max_clients); + printf(" init_sockets_at_startup: 0 or 1. default = %d\n", init_sockets_at_startup); + return 0; + } + max_clients = atoi(argv[1]); + } + + if ( 2 < argc ) + init_sockets_at_startup = atoi(argv[2]); + + if (max_clients >= MAX_CLIENTS) + max_clients = MAX_CLIENTS; + + thread_data thData{max_clients}; + if (!thData.ready) + return 1; + std::thread server_thread(&thread_data::serve, &thData); + + CActiveSocket * clients = new CActiveSocket[MAX_CLIENTS]; + printf("try to setup/communicate %d conncections ..\n", max_clients); + + bool have_err = false; + if (init_sockets_at_startup) + { + for (int k = 0; k < max_clients; ++k) + { + CActiveSocket &client = clients[k]; + printf("init socket %d ..\n", k); + if (!client.Initialize()) + { + fprintf(stderr, "Error: socket %d could get initialized!\n", k); + have_err = true; + break; + } + if (!client.SetNonblocking()) + { + fprintf(stderr, "Error: socket %d could set Nonblocking!\n", k); + have_err = true; + break; + } + } + } + + for (int k = 0; !have_err && k < max_clients; ++k) + { + CActiveSocket &client = clients[k]; + printf("\nclient %d ..\n", k); + if (!init_sockets_at_startup) + { + if (!client.Initialize()) + { + fprintf(stderr, "Error: socket %d could get initialized!\n", k); + break; + } + if (!client.SetNonblocking()) + { + fprintf(stderr, "Error: socket %d could set Nonblocking!\n", k); + break; + } + } + have_err = !connect_new_client(client, k, thData); + } + + thData.terminate = true; + printf("trigger server to terminate ..\n"); + + printf("cleanup clients connections ..\n"); + for (int k=0; k < MAX_CLIENTS; ++k) + { + CActiveSocket &client = clients[k]; + if (client.IsSocketValid()) + client.Close(); + } + delete []clients; + + server_thread.join(); +} diff --git a/examples/TxToServer.cpp b/examples/TxToServer.cpp new file mode 100644 index 0000000..0706ac5 --- /dev/null +++ b/examples/TxToServer.cpp @@ -0,0 +1,79 @@ + +#include "SimpleSocket.h" // Include header for active socket object definition + +#include + +#define MAX_PACKET 4096 + +int main(int argc, char **argv) +{ + CSimpleSocket socket; + + if ( argc < 3 ) + { + fprintf(stderr, "usage: %s [ ]\n", argv[0] ); + return 10; + } + + //-------------------------------------------------------------------------- + // Initialize our socket object + //-------------------------------------------------------------------------- + socket.Initialize(); + + if ( 4 < argc ) + { + unsigned bindPort = atoi(argv[4]); + bool ret = socket.Bind( argv[3], uint16(bindPort) ); + fprintf(stderr, "bind to %s:%u %s\n", argv[3], bindPort, (ret ? "successful":"failed") ); + } + + bool bConnOK = socket.Open( argv[1], 6789 ); + if ( !bConnOK ) + { + fprintf(stderr, "error connecting to '%s'!\n", argv[1] ); + return 10; + } + + fprintf(stderr, "\nLocal is %s. Local: %s:%u " + , ( socket.IsServerSide() ? "Server" : "Client" ) + , socket.GetLocalAddr(), (unsigned)socket.GetLocalPort()); + fprintf(stderr, "Peer: %s:%u\n", socket.GetPeerAddr(), (unsigned)socket.GetPeerPort()); + + uint32 nSendBufSize = socket.GetSendWindowSize(); + fprintf(stderr, "default Send Buffer Size %u\n", nSendBufSize ); + nSendBufSize = socket.SetSendWindowSize( 80000 ); + fprintf(stderr, "new Send Buffer Size %u\n", nSendBufSize ); + + size_t sendSize = strlen( argv[2] ); + socket.Transmit( (const uint8 *)argv[2], (int32)sendSize ); + + socket.SetNonblocking(); + //socket.SetBlocking(); + + int32 rxSize = 0; + char rxBuffer[16]; + while (rxSize < (int32)sendSize) + { + if ( !socket.WaitUntilReadable(500) ) + { + fprintf(stderr, "."); + continue; + } + + int32 peekRx = socket.GetNumReceivableBytes(); + int32 rx = socket.Receive( 15, rxBuffer ); + if ( peekRx != rx && !( peekRx == 0 && rx < 0 ) ) + fprintf( stderr, "GetNumReceivableBytes() = %d != %d = Receive() !\n", peekRx, rx ); + + if ( rx > 0 ) + { + rxBuffer[rx] = '\0'; + fprintf(stderr, "\nreceived %d bytes: '%s'\n", rx, rxBuffer ); + rxSize += rx; + } + else if ( !socket.IsSocketValid() ) + break; + } + + return 1; +} diff --git a/examples/TxToUdpServer.cpp b/examples/TxToUdpServer.cpp new file mode 100644 index 0000000..f10c019 --- /dev/null +++ b/examples/TxToUdpServer.cpp @@ -0,0 +1,81 @@ + +#include "SimpleSocket.h" + +#include + +#define MAX_PACKET 4096 + +int main(int argc, char **argv) +{ + const char *pcHostname = 0; + const char *pcSendText = 0; + const char *pcBindAddr = 0; + const uint16 portno = 6789; + int bindPort = -1; + int mcastTTL = -1; + int argidx = 1; + + if ( argidx < argc ) + pcHostname = argv[argidx++]; + + if ( argidx < argc ) + pcSendText = argv[argidx++]; + + if ( argidx + 1 < argc && !strcmp(argv[argidx], "-m") ) + { + argidx++; + mcastTTL = atoi(argv[argidx++]); + } + + if ( argidx + 1 < argc ) + { + pcBindAddr = argv[argidx++]; + bindPort = atoi(argv[argidx++]); + } + + if ( !pcHostname || !pcSendText ) + { + fprintf(stderr, "usage: %s [-m ] [ ]\n", argv[0] ); + fprintf(stderr, " -m : activate multicast on socket\n"); + return 10; + } + + //-------------------------------------------------------------------------- + // Initialize our socket object + //-------------------------------------------------------------------------- + CSimpleSocket socket( CSimpleSocket::SocketTypeUdp ); + socket.Initialize(); + + if ( pcBindAddr && bindPort >= 0 ) + { + bool ret = socket.Bind( pcBindAddr, uint16(bindPort) ); + fprintf(stderr, "bind to %s:%u %s\n", pcBindAddr, bindPort, (ret ? "successful":"failed") ); + } + else + fprintf(stderr, "binding not parametrized\n"); + + bool bConnOK = socket.Open( pcHostname, portno ); + if ( !bConnOK ) + { + fprintf(stderr, "error connecting to '%s'!\n", pcHostname ); + return 10; + } + + if ( mcastTTL >= 0 && mcastTTL < 256 ) + { + bool ret = socket.SetMulticast(true, uint8(mcastTTL)); + fprintf(stderr, "Setting Multicast with TTL %d %s\n", mcastTTL, ret ? "was successful" : "failed"); + } + else + fprintf(stderr, "multicast not parametrized\n"); + + fprintf(stderr, "\nLocal is %s. Local: %s:%u " + , ( socket.IsServerSide() ? "Server" : "Client" ) + , socket.GetLocalAddr(), (unsigned)socket.GetLocalPort()); + fprintf(stderr, "Peer: %s:%u\n", socket.GetPeerAddr(), (unsigned)socket.GetPeerPort()); + + size_t sendSize = strlen( pcSendText ); + socket.Send( pcSendText, (int32)sendSize ); + + return 1; +} diff --git a/examples/UdpServer.cpp b/examples/UdpServer.cpp new file mode 100644 index 0000000..4484955 --- /dev/null +++ b/examples/UdpServer.cpp @@ -0,0 +1,89 @@ + +#include "PassiveSocket.h" // Include header for active socket object definition + +#define MAX_PACKET 4096 + +int main(int argc, char **argv) +{ + char buffer[ MAX_PACKET ]; + CPassiveSocket passiveSocket( CSimpleSocket::SocketTypeUdp ); + + const char * bindAddr = 0; + const char * multicastGroup = 0; + unsigned bindPort = 6789; + bool bindRet; + + if ( argc <= 1 ) + { + fprintf(stderr, "usage: %s [ [ []]]\n", argv[0] ); + fprintf(stderr, " do not use '127.0.0.1' if you want to accept reception from remotes\n"); + fprintf(stderr, " suggestion for : 224.0.0.1\n"); + } + if ( 1 < argc ) + bindAddr = argv[1]; + if ( 2 < argc ) + bindPort = atoi(argv[2]) & 65535; + if ( 3 < argc ) + multicastGroup = argv[3]; + + //-------------------------------------------------------------------------- + // Initialize our socket object + //-------------------------------------------------------------------------- + passiveSocket.Initialize(); + if (!multicastGroup) + fprintf(stderr, "binding to %s:%u\n", bindAddr, bindPort); + else + fprintf(stderr, "binding to %s:%u for multicast-group %s\n", bindAddr, bindPort, multicastGroup); + bindRet = passiveSocket.BindMulticast( bindAddr, multicastGroup, uint16(bindPort) ); + fprintf(stderr, "bind %s\n", bindRet ? "was successful" : "failed"); + CSimpleSocket &socket = passiveSocket; + + fprintf(stderr, "\nLocal is %s. Local: %s:%u " + , ( socket.IsServerSide() ? "Server" : "Client" ) + , socket.GetLocalAddr(), (unsigned)socket.GetLocalPort()); + fprintf(stderr, "(invalid) Peer: %s:%u\n", socket.GetPeerAddr(), (unsigned)socket.GetPeerPort()); + + socket.SetReceiveTimeoutMillis(500); + + while (true) + { + //---------------------------------------------------------------------- + // Receive request from the client. + //---------------------------------------------------------------------- + + //socket.WaitUntilReadable(100); + + int32 peekRx = socket.GetNumReceivableBytes(); + int32 rx = socket.Receive(MAX_PACKET, buffer); + if ( peekRx != rx && !( peekRx == 0 && rx < 0 ) ) + fprintf( stderr, "GetNumReceivableBytes() = %d != %d = Receive() !\n", peekRx, rx ); + if (rx > 0) + { + fprintf(stderr, "\nLocal is %s. Local: %s:%u " + , ( socket.IsServerSide() ? "Server" : "Client" ) + , socket.GetLocalAddr(), (unsigned)socket.GetLocalPort()); + fprintf(stderr, "Peer: %s:%u\n", socket.GetPeerAddr(), (unsigned)socket.GetPeerPort()); + + fprintf(stderr, "\nreceived %d bytes:\n", rx ); + for ( int k = 0; k < rx; ++k ) + fprintf(stderr, "%c", buffer[k]); + fprintf(stderr, "\n"); + if ( 0 == memcmp(buffer, "quit", 4) ) + break; + } + else if ( CSimpleSocket::SocketEwouldblock == socket.GetSocketError() ) + fprintf(stderr, "."); + else if ( !socket.IsNonblocking() && CSimpleSocket::SocketTimedout == socket.GetSocketError() ) + fprintf(stderr, "."); + else + { + fprintf(stderr, "Error: %s\n", socket.DescribeError() ); + fprintf(stderr, "Error: %s, code %d\n" + , ( socket.IsNonblocking() ? "NonBlocking socket" : "Blocking socket" ) + , (int) socket.GetSocketError() + ); + } + } + + return 1; +} diff --git a/src/ActiveSocket.cpp b/src/ActiveSocket.cpp deleted file mode 100644 index 3dd5b12..0000000 --- a/src/ActiveSocket.cpp +++ /dev/null @@ -1,310 +0,0 @@ -/*---------------------------------------------------------------------------*/ -/* */ -/* CActiveSocket.cpp - Active Socket Implementation */ -/* */ -/* Author : Mark Carrier (mark@carrierlabs.com) */ -/* */ -/*---------------------------------------------------------------------------*/ -/* Copyright (c) 2007-2009 CarrierLabs, LLC. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * 4. The name "CarrierLabs" must not be used to - * endorse or promote products derived from this software without - * prior written permission. For written permission, please contact - * mark@carrierlabs.com. - * - * THIS SOFTWARE IS PROVIDED BY MARK CARRIER ``AS IS'' AND ANY - * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MARK CARRIER OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - *----------------------------------------------------------------------------*/ -#include "ActiveSocket.h" - -CActiveSocket::CActiveSocket(CSocketType nType) : CSimpleSocket(nType) -{ -} - -//------------------------------------------------------------------------------ -// -// ConnectTCP() - -// -//------------------------------------------------------------------------------ -bool CActiveSocket::ConnectTCP(const char *pAddr, uint16 nPort) -{ - bool bRetVal = false; - struct in_addr stIpAddress; - - //------------------------------------------------------------------ - // Preconnection setup that must be preformed - //------------------------------------------------------------------ - memset(&m_stServerSockaddr, 0, sizeof(m_stServerSockaddr)); - m_stServerSockaddr.sin_family = AF_INET; - - if ((m_pHE = GETHOSTBYNAME(pAddr)) == NULL) - { -#ifdef WIN32 - TranslateSocketError(); -#else - if (h_errno == HOST_NOT_FOUND) - { - SetSocketError(SocketInvalidAddress); - } -#endif - return bRetVal; - } - - memcpy(&stIpAddress, m_pHE->h_addr_list[0], m_pHE->h_length); - m_stServerSockaddr.sin_addr.s_addr = stIpAddress.s_addr; - - if ((int32)m_stServerSockaddr.sin_addr.s_addr == CSimpleSocket::SocketError) - { - TranslateSocketError(); - return bRetVal; - } - - m_stServerSockaddr.sin_port = htons(nPort); - - //------------------------------------------------------------------ - // Connect to address "xxx.xxx.xxx.xxx" (IPv4) address only. - // - //------------------------------------------------------------------ - m_timer.Initialize(); - m_timer.SetStartTime(); - - if (connect(m_socket, (struct sockaddr*)&m_stServerSockaddr, sizeof(m_stServerSockaddr)) == - CSimpleSocket::SocketError) - { - //-------------------------------------------------------------- - // Get error value this might be a non-blocking socket so we - // must first check. - //-------------------------------------------------------------- - TranslateSocketError(); - - //-------------------------------------------------------------- - // If the socket is non-blocking and the current socket error - // is SocketEinprogress or SocketEwouldblock then poll connection - // with select for designated timeout period. - // Linux returns EINPROGRESS and Windows returns WSAEWOULDBLOCK. - //-------------------------------------------------------------- - if ((IsNonblocking()) && - ((GetSocketError() == CSimpleSocket::SocketEwouldblock) || - (GetSocketError() == CSimpleSocket::SocketEinprogress))) - { - bRetVal = Select(GetConnectTimeoutSec(), GetConnectTimeoutUSec()); - } - } - else - { - TranslateSocketError(); - bRetVal = true; - } - - m_timer.SetEndTime(); - - return bRetVal; -} - -//------------------------------------------------------------------------------ -// -// ConnectUDP() - -// -//------------------------------------------------------------------------------ -bool CActiveSocket::ConnectUDP(const char *pAddr, uint16 nPort) -{ - bool bRetVal = false; - struct in_addr stIpAddress; - - //------------------------------------------------------------------ - // Pre-connection setup that must be preformed - //------------------------------------------------------------------ - memset(&m_stServerSockaddr, 0, sizeof(m_stServerSockaddr)); - m_stServerSockaddr.sin_family = AF_INET; - - if ((m_pHE = GETHOSTBYNAME(pAddr)) == NULL) - { -#ifdef WIN32 - TranslateSocketError(); -#else - if (h_errno == HOST_NOT_FOUND) - { - SetSocketError(SocketInvalidAddress); - } -#endif - return bRetVal; - } - - memcpy(&stIpAddress, m_pHE->h_addr_list[0], m_pHE->h_length); - m_stServerSockaddr.sin_addr.s_addr = stIpAddress.s_addr; - - if ((int32)m_stServerSockaddr.sin_addr.s_addr == CSimpleSocket::SocketError) - { - TranslateSocketError(); - return bRetVal; - } - - m_stServerSockaddr.sin_port = htons(nPort); - - //------------------------------------------------------------------ - // Connect to address "xxx.xxx.xxx.xxx" (IPv4) address only. - // - //------------------------------------------------------------------ - m_timer.Initialize(); - m_timer.SetStartTime(); - - if (connect(m_socket, (struct sockaddr*)&m_stServerSockaddr, sizeof(m_stServerSockaddr)) != CSimpleSocket::SocketError) - { - bRetVal = true; - } - - TranslateSocketError(); - - m_timer.SetEndTime(); - - return bRetVal; -} - -//------------------------------------------------------------------------------ -// -// ConnectRAW() - -// -//------------------------------------------------------------------------------ -bool CActiveSocket::ConnectRAW(const char *pAddr, uint16 nPort) -{ - bool bRetVal = false; - struct in_addr stIpAddress; - //------------------------------------------------------------------ - // Pre-connection setup that must be preformed - //------------------------------------------------------------------ - memset(&m_stServerSockaddr, 0, sizeof(m_stServerSockaddr)); - m_stServerSockaddr.sin_family = AF_INET; - - if ((m_pHE = GETHOSTBYNAME(pAddr)) == NULL) - { -#ifdef WIN32 - TranslateSocketError(); -#else - if (h_errno == HOST_NOT_FOUND) - { - SetSocketError(SocketInvalidAddress); - } -#endif - return bRetVal; - } - - memcpy(&stIpAddress, m_pHE->h_addr_list[0], m_pHE->h_length); - m_stServerSockaddr.sin_addr.s_addr = stIpAddress.s_addr; - - if ((int32)m_stServerSockaddr.sin_addr.s_addr == CSimpleSocket::SocketError) - { - TranslateSocketError(); - return bRetVal; - } - - m_stServerSockaddr.sin_port = htons(nPort); - - //------------------------------------------------------------------ - // Connect to address "xxx.xxx.xxx.xxx" (IPv4) address only. - // - //------------------------------------------------------------------ - m_timer.Initialize(); - m_timer.SetStartTime(); - - if (connect(m_socket, (struct sockaddr*)&m_stServerSockaddr, sizeof(m_stServerSockaddr)) != CSimpleSocket::SocketError) - { - bRetVal = true; - } - - TranslateSocketError(); - - m_timer.SetEndTime(); - - return bRetVal; -} - - -//------------------------------------------------------------------------------ -// -// Open() - Create a connection to a specified address on a specified port -// -//------------------------------------------------------------------------------ -bool CActiveSocket::Open(const char *pAddr, uint16 nPort) -{ - bool bRetVal = false; - - if (IsSocketValid() == false) - { - SetSocketError(CSimpleSocket::SocketInvalidSocket); - return bRetVal; - } - - if (pAddr == NULL) - { - SetSocketError(CSimpleSocket::SocketInvalidAddress); - return bRetVal; - } - - if (nPort == 0) - { - SetSocketError(CSimpleSocket::SocketInvalidPort); - return bRetVal; - } - - switch (m_nSocketType) - { - case CSimpleSocket::SocketTypeTcp : - { - bRetVal = ConnectTCP(pAddr, nPort); - break; - } - case CSimpleSocket::SocketTypeUdp : - { - bRetVal = ConnectUDP(pAddr, nPort); - break; - } - case CSimpleSocket::SocketTypeRaw : - break; - default: - break; - } - - //-------------------------------------------------------------------------- - // If successful then create a local copy of the address and port - //-------------------------------------------------------------------------- - if (bRetVal) - { - socklen_t nSockLen = sizeof(struct sockaddr); - - memset(&m_stServerSockaddr, 0, nSockLen); - getpeername(m_socket, (struct sockaddr *)&m_stServerSockaddr, &nSockLen); - - nSockLen = sizeof(struct sockaddr); - memset(&m_stClientSockaddr, 0, nSockLen); - getsockname(m_socket, (struct sockaddr *)&m_stClientSockaddr, &nSockLen); - - SetSocketError(SocketSuccess); - } - - return bRetVal; -} diff --git a/src/ActiveSocket.h b/src/ActiveSocket.h index 14e0ad9..c0ed796 100644 --- a/src/ActiveSocket.h +++ b/src/ActiveSocket.h @@ -45,47 +45,11 @@ #include "SimpleSocket.h" -class CPassiveSocket; - /// Provides a platform independent class to create an active socket. /// An active socket is used to create a socket which connects to a server. /// This type of object would be used when an application needs to send/receive /// data from a server. -class EXPORT CActiveSocket : public CSimpleSocket { -public: - friend class CPassiveSocket; - - CActiveSocket(CSocketType type = SocketTypeTcp); - virtual ~CActiveSocket() { - Close(); - }; - - /// Established a connection to the address specified by pAddr. - /// Connection-based protocol sockets (CSocket::SocketTypeTcp) may - /// successfully call Open() only once, however; connectionless protocol - /// sockets (CSocket::SocketTypeUdp) may use Open() multiple times to - /// change their association. - /// @param pAddr specifies the destination address to connect. - /// @param nPort specifies the destination port. - /// @return true if successful connection made, otherwise false. - virtual bool Open(const char *pAddr, uint16 nPort); - -private: - /// Utility function used to create a TCP connection, called from Open(). - /// @return true if successful connection made, otherwise false. - bool ConnectTCP(const char *pAddr, uint16 nPort); - /// Utility function used to create a UDP connection, called from Open(). - /// @return true if successful connection made, otherwise false. - bool ConnectUDP(const char *pAddr, uint16 nPort); - - /// Utility function used to create a RAW connection, called from Open(). - /// @return true if successful connection made, otherwise false. - bool ConnectRAW(const char *pAddr, uint16 nPort); - -private: - struct hostent *m_pHE; -}; +typedef CSimpleSocket CActiveSocket; #endif /* __ACTIVESOCKET_H__ */ - diff --git a/src/Host.h b/src/Host.h index 91b3d16..d333dc2 100644 --- a/src/Host.h +++ b/src/Host.h @@ -251,11 +251,37 @@ extern "C" #endif #ifdef _MSC_VER - #define EXPORT __declspec(dllexport) -#else - #define EXPORT + #ifdef CLSOCKET_STATIC_LIB + #define CLSOCKET_API + #elif defined(EXPORT_CLSOCKET_SYMBOLS) + #define CLSOCKET_API __declspec(dllexport) + #else + #define CLSOCKET_API __declspec(dllimport) + #endif +#elif defined(_LINUX) || defined(_DARWIN) + #ifdef EXPORT_CLSOCKET_SYMBOLS + #define CLSOCKET_API __attribute__ ((visibility("default"))) + #endif +#endif + +#ifndef CLSOCKET_API + #define CLSOCKET_API +#endif + + +#if defined(_LINUX) || defined(_DARWIN) + #ifdef EXPORT_CLSOCKET_SYMBOLS + #define CLSOCKET_NO_EXPORT __attribute__ ((visibility("hidden"))) + #endif +#endif + +#ifndef CLSOCKET_NO_EXPORT + #define CLSOCKET_NO_EXPORT #endif + + + #ifdef __cplusplus } #endif diff --git a/src/PassiveSocket.cpp b/src/PassiveSocket.cpp index b9dce77..959ca93 100644 --- a/src/PassiveSocket.cpp +++ b/src/PassiveSocket.cpp @@ -42,12 +42,23 @@ *----------------------------------------------------------------------------*/ #include "PassiveSocket.h" - +#ifdef CLSOCKET_OWN_INET_PTON +// implemented in SimpleSocket.cpp +extern int inet_pton(int af, const char *src, void *dst); +#endif CPassiveSocket::CPassiveSocket(CSocketType nType) : CSimpleSocket(nType) { + // PassiveSocket is intended for "Server" purpose + m_bIsServerSide = true; +} + +CPassiveSocket::~CPassiveSocket() +{ + Close(); } + bool CPassiveSocket::BindMulticast(const char *pInterface, const char *pGroup, uint16 nPort) { bool bRetVal = false; @@ -70,34 +81,53 @@ bool CPassiveSocket::BindMulticast(const char *pInterface, const char *pGroup, u // If no IP Address (interface ethn) is supplied, or the loop back is // specified then bind to any interface, else bind to specified interface. //-------------------------------------------------------------------------- - if ((pInterface == NULL) || (!strlen(pInterface))) + if (pInterface && pInterface[0]) { - m_stMulticastGroup.sin_addr.s_addr = htonl(INADDR_ANY); + inet_pton(AF_INET, pInterface, &inAddr); } else { - if ((inAddr = inet_addr(pInterface)) != INADDR_NONE) - { - m_stMulticastGroup.sin_addr.s_addr = inAddr; - } + inAddr = INADDR_ANY; } + if (pGroup && pGroup[0] != 0) + { + m_stMulticastGroup.sin_addr.s_addr = htonl(INADDR_ANY); + } + else + { + m_stMulticastGroup.sin_addr.s_addr = inAddr; + } + + // multicast address/port is the server + memcpy(&m_stServerSockaddr,&m_stMulticastGroup,sizeof(m_stServerSockaddr)); + ClearSystemError(); + //-------------------------------------------------------------------------- // Bind to the specified port //-------------------------------------------------------------------------- + m_bIsServerSide = true; if (bind(m_socket, (struct sockaddr *)&m_stMulticastGroup, sizeof(m_stMulticastGroup)) == 0) { - //---------------------------------------------------------------------- - // Join the multicast group - //---------------------------------------------------------------------- - m_stMulticastRequest.imr_multiaddr.s_addr = inet_addr(pGroup); - m_stMulticastRequest.imr_interface.s_addr = m_stMulticastGroup.sin_addr.s_addr; - - if (SETSOCKOPT(m_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, - (void *)&m_stMulticastRequest, - sizeof(m_stMulticastRequest)) == CSimpleSocket::SocketSuccess) + if ( pGroup && pGroup[0] != 0 ) { - bRetVal = true; + //---------------------------------------------------------------------- + // Join the multicast group + //---------------------------------------------------------------------- + inet_pton(AF_INET, pGroup, &m_stMulticastRequest.imr_multiaddr.s_addr); + m_stMulticastRequest.imr_interface.s_addr = inAddr; + + if ( SETSOCKOPT(m_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, + (void *)&m_stMulticastRequest, + sizeof(m_stMulticastRequest)) == CSimpleSocket::SocketSuccess) + { + bRetVal = true; + } + } + else + { + // all OK, if we shall not join to a group + bRetVal = true; } m_timer.SetEndTime(); @@ -124,19 +154,21 @@ bool CPassiveSocket::BindMulticast(const char *pInterface, const char *pGroup, u //------------------------------------------------------------------------------ // -// Listen() - +// Listen() - Create a listening socket (server) at local ip address 'x.x.x.x' or 'localhost' +// waiting for an incoming connection from client(s) +// also see .h // //------------------------------------------------------------------------------ bool CPassiveSocket::Listen(const char *pAddr, uint16 nPort, int32 nConnectionBacklog) { + ClearSystemError(); + bool bRetVal = false; #ifdef WIN32 ULONG inAddr; #else in_addr_t inAddr; - - int32 nReuse; - nReuse = IPTOS_LOWDELAY; + int32 nReuse = IPTOS_LOWDELAY; //-------------------------------------------------------------------------- // Set the following socket option SO_REUSEADDR. This will allow the file @@ -161,7 +193,8 @@ bool CPassiveSocket::Listen(const char *pAddr, uint16 nPort, int32 nConnectionBa } else { - if ((inAddr = inet_addr(pAddr)) != INADDR_NONE) + inet_pton(AF_INET, pAddr, &inAddr); + if (inAddr != INADDR_NONE) { m_stServerSockaddr.sin_addr.s_addr = inAddr; } @@ -173,6 +206,7 @@ bool CPassiveSocket::Listen(const char *pAddr, uint16 nPort, int32 nConnectionBa //-------------------------------------------------------------------------- // Bind to the specified port //-------------------------------------------------------------------------- + m_bIsServerSide = true; if (bind(m_socket, (struct sockaddr *)&m_stServerSockaddr, sizeof(m_stServerSockaddr)) != CSimpleSocket::SocketError) { if (m_nSocketType == CSimpleSocket::SocketTypeTcp) @@ -202,6 +236,13 @@ bool CPassiveSocket::Listen(const char *pAddr, uint16 nPort, int32 nConnectionBa Close(); SetSocketError(err); } + else if (nPort == 0) + { + // for later GetLocalPort() - after system choosed a free port + socklen_t nSockLen = sizeof(struct sockaddr); + memset(&m_stServerSockaddr, 0, nSockLen); + getsockname(m_socket, (struct sockaddr *)&m_stServerSockaddr, &nSockLen); + } return bRetVal; } @@ -212,10 +253,10 @@ bool CPassiveSocket::Listen(const char *pAddr, uint16 nPort, int32 nConnectionBa // Accept() - // //------------------------------------------------------------------------------ -CActiveSocket *CPassiveSocket::Accept() +CSimpleSocket *CPassiveSocket::Accept() { uint32 nSockLen; - CActiveSocket *pClientSocket = NULL; + CSimpleSocket *pClientSocket = NULL; SOCKET socket = CSimpleSocket::SocketError; if (m_nSocketType != CSimpleSocket::SocketTypeTcp) @@ -224,58 +265,54 @@ CActiveSocket *CPassiveSocket::Accept() return pClientSocket; } - pClientSocket = new CActiveSocket(); - //-------------------------------------------------------------------------- // Wait for incoming connection. //-------------------------------------------------------------------------- - if (pClientSocket != NULL) - { - CSocketError socketErrno = SocketSuccess; + CSocketError socketErrno = SocketSuccess; - m_timer.Initialize(); - m_timer.SetStartTime(); + m_timer.Initialize(); + m_timer.SetStartTime(); - nSockLen = sizeof(m_stClientSockaddr); + nSockLen = sizeof(m_stClientSockaddr); - do - { - errno = 0; - socket = accept(m_socket, (struct sockaddr *)&m_stClientSockaddr, (socklen_t *)&nSockLen); + ClearSystemError(); - if (socket != -1) - { - pClientSocket->SetSocketHandle(socket); - pClientSocket->TranslateSocketError(); - socketErrno = pClientSocket->GetSocketError(); - socklen_t nSockLen = sizeof(struct sockaddr); - - //------------------------------------------------------------- - // Store client and server IP and port information for this - // connection. - //------------------------------------------------------------- - getpeername(m_socket, (struct sockaddr *)&pClientSocket->m_stClientSockaddr, &nSockLen); - memcpy((void *)&pClientSocket->m_stClientSockaddr, (void *)&m_stClientSockaddr, nSockLen); - - memset(&pClientSocket->m_stServerSockaddr, 0, nSockLen); - getsockname(m_socket, (struct sockaddr *)&pClientSocket->m_stServerSockaddr, &nSockLen); - } - else - { - TranslateSocketError(); - socketErrno = GetSocketError(); - } - - } while (socketErrno == CSimpleSocket::SocketInterrupted); - - m_timer.SetEndTime(); + do + { + errno = 0; + socket = accept(m_socket, (struct sockaddr *)&m_stClientSockaddr, (socklen_t *)&nSockLen); - if (socketErrno != CSimpleSocket::SocketSuccess) + if (socket != INVALID_SOCKET) { - delete pClientSocket; - pClientSocket = NULL; + pClientSocket = new CSimpleSocket(); + if ( !pClientSocket ) + break; + + pClientSocket->SetSocketHandle(socket); + pClientSocket->TranslateSocketError(); + socketErrno = pClientSocket->GetSocketError(); + socklen_t nSockLen = sizeof(struct sockaddr); + + //------------------------------------------------------------- + // Store client and server IP and port information for this + // connection. + //------------------------------------------------------------- + getpeername(m_socket, (struct sockaddr *)&pClientSocket->m_stClientSockaddr, &nSockLen); + memcpy((void *)&pClientSocket->m_stClientSockaddr, (void *)&m_stClientSockaddr, nSockLen); + + memset(&pClientSocket->m_stServerSockaddr, 0, nSockLen); + getsockname(m_socket, (struct sockaddr *)&pClientSocket->m_stServerSockaddr, &nSockLen); } - } + else + { + // fprintf(stderr, "error at accept(): got socket %d\n", socket); + TranslateSocketError(); + socketErrno = GetSocketError(); + } + + } while (socketErrno == CSimpleSocket::SocketInterrupted); + + m_timer.SetEndTime(); return pClientSocket; } @@ -319,6 +356,10 @@ int32 CPassiveSocket::Send(const uint8 *pBuf, size_t bytesToSend) case CSimpleSocket::SocketTypeTcp: CSimpleSocket::Send(pBuf, bytesToSend); break; + case CSimpleSocket::SocketTypeInvalid: + case CSimpleSocket::SocketTypeTcp6: + case CSimpleSocket::SocketTypeUdp6: + case CSimpleSocket::SocketTypeRaw: default: SetSocketError(SocketProtocolError); break; diff --git a/src/PassiveSocket.h b/src/PassiveSocket.h index e958777..8023b42 100644 --- a/src/PassiveSocket.h +++ b/src/PassiveSocket.h @@ -52,25 +52,23 @@ /// in a similar fashion. The big difference is that the method /// CPassiveSocket::Accept should not be called on the latter two socket /// types. -class EXPORT CPassiveSocket : public CSimpleSocket { +class CLSOCKET_API CPassiveSocket : public CSimpleSocket { public: CPassiveSocket(CSocketType type = SocketTypeTcp); - virtual ~CPassiveSocket() { - Close(); - }; + virtual ~CPassiveSocket(); /// Extracts the first connection request on the queue of pending /// connections and creates a newly connected socket. Used with /// CSocketType CSimpleSocket::SocketTypeTcp. It is the responsibility of /// the caller to delete the returned object when finished. - /// @return if successful a pointer to a newly created CActiveSocket object + /// @return if successful a pointer to a newly created CSimpleSocket object /// will be returned and the internal error condition of the CPassiveSocket /// object will be CPassiveSocket::SocketSuccess. If an error condition was encountered /// the NULL will be returned and one of the following error conditions will be set: /// CPassiveSocket::SocketEwouldblock, CPassiveSocket::SocketInvalidSocket, /// CPassiveSocket::SocketConnectionAborted, CPassiveSocket::SocketInterrupted /// CPassiveSocket::SocketProtocolError, CPassiveSocket::SocketFirewallError - virtual CActiveSocket *Accept(void); + virtual CSimpleSocket *Accept(void); /// Bind to a multicast group on a specified interface, multicast group, and port /// @@ -79,12 +77,28 @@ class EXPORT CPassiveSocket : public CSimpleSocket { /// @param nPort - port on which multicast /// @return true if able to bind to interface and multicast group. /// If not successful, the false is returned and one of the following error - /// condiitions will be set: CPassiveSocket::SocketAddressInUse, CPassiveSocket::SocketProtocolError, + /// conditions will be set: CPassiveSocket::SocketAddressInUse, CPassiveSocket::SocketProtocolError, /// CPassiveSocket::SocketInvalidSocket. The following socket errors are for Linux/Unix /// derived systems only: CPassiveSocket::SocketInvalidSocketBuffer bool BindMulticast(const char *pInterface, const char *pGroup, uint16 nPort); - /// Create a listening socket at local ip address 'x.x.x.x' or 'localhost' + /// Bind to a specified interface and port + /// + /// @param pInterface - interface on which to bind. + /// @param nPort - port on which multicast + /// @return true if able to bind to interface. + /// If not successful, the false is returned and one of the following error + /// conditions will be set: CPassiveSocket::SocketAddressInUse, CPassiveSocket::SocketProtocolError, + /// CPassiveSocket::SocketInvalidSocket. The following socket errors are for Linux/Unix + /// derived systems only: CPassiveSocket::SocketInvalidSocketBuffer + inline bool Bind(const char *pInterface, uint16 nPort) + { + const char *pNoGroup = 0; + return BindMulticast(pInterface, pNoGroup, nPort); + } + + /// Create a listening socket (server) at local ip address 'x.x.x.x' or 'localhost' + /// waiting for an incoming connection from client(s) /// if pAddr is NULL on port nPort. /// /// @param pAddr specifies the IP address on which to listen. @@ -109,7 +123,7 @@ class EXPORT CPassiveSocket : public CSimpleSocket { /// CPassiveSocket::SocketProtocolError, CPassiveSocket::SocketNotconnected ///
\b Note: This function is used only for a socket of type /// CSimpleSocket::SocketTypeUdp - virtual int32 Send(const uint8 *pBuf, size_t bytesToSend); + virtual int32 Send(const uint8 *pBuf, size_t bytesToSend) CLSOCKET_OVERRIDE; private: struct ip_mreq m_stMulticastRequest; /// group address for multicast diff --git a/src/SimpleSocket.cpp b/src/SimpleSocket.cpp index 38b8fea..4001d53 100644 --- a/src/SimpleSocket.cpp +++ b/src/SimpleSocket.cpp @@ -42,18 +42,229 @@ *----------------------------------------------------------------------------*/ #include "SimpleSocket.h" +#if defined(_LINUX) || defined(_DARWIN) +#include +#endif +#include +#include + +#ifdef CLSOCKET_OWN_INET_PTON +#include +#endif + +#ifdef _MSC_VER +#pragma warning( disable : 4996 ) +#define snprintf _snprintf +#endif + +#define PRINT_CLOSE 0 +#define PRINT_ERRORS 0 +#define CHECK_PRINT_RUNTIME_ERR 0 + + +#if CHECK_PRINT_RUNTIME_ERR + +#include + +static void CoreDump() +{ + int * ptr = nullptr; + while (1) + *ptr++ = -1; +} + +#endif + +#ifdef CLSOCKET_OWN_INET_PTON +// source from +// https://stackoverflow.com/questions/15370033/how-to-use-inet-pton-with-the-mingw-compiler +// Author: Paul Vixie, 1996. +// added "static" for inet_pton4() and inet_pton6() + +#define NS_INADDRSZ 4 +#define NS_IN6ADDRSZ 16 +#define NS_INT16SZ 2 + +static +int inet_pton4(const char *src, char *dst) +{ + uint8_t tmp[NS_INADDRSZ], *tp; + + int saw_digit = 0; + int octets = 0; + *(tp = tmp) = 0; + + int ch; + while ((ch = *src++) != '\0') + { + if (ch >= '0' && ch <= '9') + { + uint32_t n = *tp * 10 + (ch - '0'); + + if (saw_digit && *tp == 0) + return 0; + + if (n > 255) + return 0; + + *tp = n; + if (!saw_digit) + { + if (++octets > 4) + return 0; + saw_digit = 1; + } + } + else if (ch == '.' && saw_digit) + { + if (octets == 4) + return 0; + *++tp = 0; + saw_digit = 0; + } + else + return 0; + } + if (octets < 4) + return 0; + + memcpy(dst, tmp, NS_INADDRSZ); + + return 1; +} + +static +int inet_pton6(const char *src, char *dst) +{ + static const char xdigits[] = "0123456789abcdef"; + uint8_t tmp[NS_IN6ADDRSZ]; + + uint8_t *tp = (uint8_t*) memset(tmp, '\0', NS_IN6ADDRSZ); + uint8_t *endp = tp + NS_IN6ADDRSZ; + uint8_t *colonp = NULL; + + /* Leading :: requires some special handling. */ + if (*src == ':') + { + if (*++src != ':') + return 0; + } + + const char *curtok = src; + int saw_xdigit = 0; + uint32_t val = 0; + int ch; + while ((ch = tolower(*src++)) != '\0') + { + const char *pch = strchr(xdigits, ch); + if (pch != NULL) + { + val <<= 4; + val |= (pch - xdigits); + if (val > 0xffff) + return 0; + saw_xdigit = 1; + continue; + } + if (ch == ':') + { + curtok = src; + if (!saw_xdigit) + { + if (colonp) + return 0; + colonp = tp; + continue; + } + else if (*src == '\0') + { + return 0; + } + if (tp + NS_INT16SZ > endp) + return 0; + *tp++ = (uint8_t) (val >> 8) & 0xff; + *tp++ = (uint8_t) val & 0xff; + saw_xdigit = 0; + val = 0; + continue; + } + if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) && + inet_pton4(curtok, (char*) tp) > 0) + { + tp += NS_INADDRSZ; + saw_xdigit = 0; + break; /* '\0' was seen by inet_pton4(). */ + } + return 0; + } + if (saw_xdigit) + { + if (tp + NS_INT16SZ > endp) + return 0; + *tp++ = (uint8_t) (val >> 8) & 0xff; + *tp++ = (uint8_t) val & 0xff; + } + if (colonp != NULL) + { + /* + * Since some memmove()'s erroneously fail to handle + * overlapping regions, we'll do the shift by hand. + */ + const int n = tp - colonp; + + if (tp == endp) + return 0; + + for (int i = 1; i <= n; i++) + { + endp[-i] = colonp[n - i]; + colonp[n - i] = 0; + } + tp = endp; + } + if (tp != endp) + return 0; + + memcpy(dst, tmp, NS_IN6ADDRSZ); + + return 1; +} + +int inet_pton(int af, const char *src, void *dst) +{ + switch (af) + { + case AF_INET: + return inet_pton4(src, (char*)dst); + case AF_INET6: + return inet_pton6(src, (char*)dst); + default: + return -1; + } +} + +#endif + + CSimpleSocket::CSimpleSocket(CSocketType nType) : m_socket(INVALID_SOCKET), m_socketErrno(CSimpleSocket::SocketInvalidSocket), m_pBuffer(NULL), m_nBufferSize(0), m_nSocketDomain(AF_INET), m_nSocketType(SocketTypeInvalid), m_nBytesReceived(-1), m_nBytesSent(-1), m_nFlags(0), - m_bIsBlocking(true) + m_bIsBlocking(true), + m_bIsServerSide(false), + m_bPeerHasClosed(true) { - SetConnectTimeout(1, 0); + memset(&m_stConnectTimeout, 0, sizeof(struct timeval)); memset(&m_stRecvTimeout, 0, sizeof(struct timeval)); memset(&m_stSendTimeout, 0, sizeof(struct timeval)); memset(&m_stLinger, 0, sizeof(struct linger)); + SetConnectTimeout(1, 0); // default timeout for connection + + memset(&m_stServerSockaddr, 0, sizeof(struct sockaddr)); + memset(&m_stClientSockaddr, 0, sizeof(struct sockaddr)); + memset(&m_stMulticastGroup, 0, sizeof(struct sockaddr)); switch(nType) { @@ -101,6 +312,7 @@ CSimpleSocket::CSimpleSocket(CSocketType nType) : #endif break; } + case CSimpleSocket::SocketTypeInvalid: default: m_nSocketType = CSimpleSocket::SocketTypeInvalid; break; @@ -114,6 +326,17 @@ CSimpleSocket::CSimpleSocket(CSimpleSocket &socket) memcpy(m_pBuffer, socket.m_pBuffer, socket.m_nBufferSize); } +CSimpleSocket::~CSimpleSocket() +{ + if (m_pBuffer != NULL) + { + delete [] m_pBuffer; + m_pBuffer = NULL; + } + + Close(); +} + CSimpleSocket *CSimpleSocket::operator=(CSimpleSocket &socket) { if (m_nBufferSize != socket.m_nBufferSize) @@ -128,6 +351,373 @@ CSimpleSocket *CSimpleSocket::operator=(CSimpleSocket &socket) } +bool CSimpleSocket::GetAddrInfoStatic(const char *pAddr, uint16 nPort, struct in_addr * pOutIpAddress, CSocketType nSocketType ) +{ + char aServ[64]; + char * pServ = aServ; + struct addrinfo hints, *servinfo, *p; + int rv; + int nSocketDomain = 0; + + memset(&hints, 0, sizeof(hints)); + switch ( nSocketType ) + { + default: + case SocketTypeInvalid: +#ifdef _WIN32 + case SocketTypeRaw: +#endif + return false; + case SocketTypeTcp6: + hints.ai_family = nSocketDomain = AF_INET6; + hints.ai_socktype = SOCK_STREAM; + break; + case SocketTypeTcp: + hints.ai_family = nSocketDomain = AF_INET; + hints.ai_socktype = SOCK_STREAM; + break; + case SocketTypeUdp6: + hints.ai_family = nSocketDomain = AF_INET6; + hints.ai_socktype = SOCK_DGRAM; + break; + case SocketTypeUdp: + hints.ai_family = nSocketDomain = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + break; +#if defined(_LINUX) && !defined(_DARWIN) + case SocketTypeRaw: + hints.ai_family = nSocketDomain = AF_PACKET; + hints.ai_socktype = SOCK_RAW; + break; +#endif + } + + if ( nPort ) + snprintf( aServ, 63, "%d", (int)nPort ); + else + pServ = 0; + + rv = ::getaddrinfo(pAddr, pServ, &hints, &servinfo ); + if ( rv != 0 ) + return false; + + // loop through all the results + for ( p = servinfo; p != NULL; p = p->ai_next ) + { + // creation of socket already done in Initialize() + // check, that protocol and socktype fit + if ( p->ai_family != nSocketDomain || p->ai_socktype != nSocketType + || p->ai_addr->sa_family != nSocketDomain ) + { + continue; + } + + // struct sockaddr_in * pOutSin + //*pOutSin = *( (struct sockaddr_in *) p->ai_addr ); + + // struct in_addr * pOutIpAddress + *pOutIpAddress = ( (struct sockaddr_in *)p->ai_addr )->sin_addr; + + freeaddrinfo(servinfo); // all done with this structure + return true; + } + + freeaddrinfo(servinfo); // all done with this structure + return false; +} + + +bool CSimpleSocket::GetAddrInfo(const char *pAddr, uint16 nPort, struct in_addr * pOutIpAddress ) +{ + char aServ[64]; + char * pServ = aServ; + struct addrinfo hints, *servinfo, *p; + int rv; + + if (m_socket == INVALID_SOCKET) + { + return false; + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = m_nSocketDomain; // AF_INET for IPv4, AF_UNSPEC for any, AF_INET6 to force IPv6 + switch ( m_nSocketType ) + { + default: + case SocketTypeInvalid: + return false; + case SocketTypeTcp6: + case SocketTypeTcp: + hints.ai_socktype = SOCK_STREAM; + break; + case SocketTypeUdp6: + case SocketTypeUdp: + hints.ai_socktype = SOCK_DGRAM; + break; + case SocketTypeRaw: + hints.ai_socktype = SOCK_RAW; + break; + } + + if ( nPort ) + snprintf( aServ, 63, "%d", (int)nPort ); + else + pServ = 0; + + rv = ::getaddrinfo(pAddr, pServ, &hints, &servinfo ); + if ( rv != 0 ) + { + // gai_strerror(rv); + SetSocketError(CSimpleSocket::SocketInvalidAddress); + return false; + } + + // loop through all the results + for ( p = servinfo; p != NULL; p = p->ai_next ) + { + // creation of socket already done in Initialize() + // check, that protocol and socktype fit + if ( p->ai_family != m_nSocketDomain || p->ai_socktype != m_nSocketType + || p->ai_addr->sa_family != m_nSocketDomain ) + { + continue; + } + + // struct sockaddr_in * pOutSin + //*pOutSin = *( (struct sockaddr_in *) p->ai_addr ); + + // struct in_addr * pOutIpAddress + *pOutIpAddress = ( (struct sockaddr_in *)p->ai_addr )->sin_addr; + + freeaddrinfo(servinfo); // all done with this structure + return true; + } + + freeaddrinfo(servinfo); // all done with this structure + + SetSocketError(CSimpleSocket::SocketInvalidAddress); + return false; +} + + +//------------------------------------------------------------------------------ +// +// ConnectTCP() - +// +//------------------------------------------------------------------------------ +bool CSimpleSocket::ConnectTCP(const char *pAddr, uint16 nPort) +{ + bool bRetVal = false; + struct in_addr stIpAddress; + + // this is the Client + m_bIsServerSide = false; + + ClearSystemError(); + + //------------------------------------------------------------------ + // Preconnection setup that must be performed + //------------------------------------------------------------------ + memset(&m_stServerSockaddr, 0, sizeof(m_stServerSockaddr)); + m_stServerSockaddr.sin_family = AF_INET; + + if ( ! GetAddrInfo(pAddr, nPort, &stIpAddress) ) + return bRetVal; + + m_stServerSockaddr.sin_addr.s_addr = stIpAddress.s_addr; + + if ((int32)m_stServerSockaddr.sin_addr.s_addr == CSimpleSocket::SocketError) + { + TranslateSocketError(); + return bRetVal; + } + + m_stServerSockaddr.sin_port = htons(nPort); + + //------------------------------------------------------------------ + // Connect to address "xxx.xxx.xxx.xxx" (IPv4) address only. + // + //------------------------------------------------------------------ + m_timer.Initialize(); + m_timer.SetStartTime(); + + if (connect(m_socket, (struct sockaddr*)&m_stServerSockaddr, sizeof(m_stServerSockaddr)) == + CSimpleSocket::SocketError) + { + //-------------------------------------------------------------- + // Get error value this might be a non-blocking socket so we + // must first check. + //-------------------------------------------------------------- + TranslateSocketError(); + + //-------------------------------------------------------------- + // If the socket is non-blocking and the current socket error + // is SocketEinprogress or SocketEwouldblock then poll connection + // with select for designated timeout period. + // Linux returns EINPROGRESS and Windows returns WSAEWOULDBLOCK. + //-------------------------------------------------------------- + if ((IsNonblocking()) && + ((GetSocketError() == CSimpleSocket::SocketEwouldblock) || + (GetSocketError() == CSimpleSocket::SocketEinprogress))) + { + bRetVal = Select( m_stConnectTimeout.tv_sec, m_stConnectTimeout.tv_usec ); + } + } + else + { + TranslateSocketError(); + bRetVal = true; + } + + m_timer.SetEndTime(); + if ( bRetVal ) + m_bPeerHasClosed = false; + + return bRetVal; +} + +//------------------------------------------------------------------------------ +// +// ConnectUDP() - +// +//------------------------------------------------------------------------------ +bool CSimpleSocket::ConnectUDP(const char *pAddr, uint16 nPort) +{ + bool bRetVal = false; + struct in_addr stIpAddress; + + // this is the Client + m_bIsServerSide = false; + + ClearSystemError(); + + //------------------------------------------------------------------ + // Pre-connection setup that must be performed + //------------------------------------------------------------------ + memset(&m_stServerSockaddr, 0, sizeof(m_stServerSockaddr)); + m_stServerSockaddr.sin_family = AF_INET; + + if ( ! GetAddrInfo(pAddr, nPort, &stIpAddress) ) + return bRetVal; + + m_stServerSockaddr.sin_addr.s_addr = stIpAddress.s_addr; + + if ((int32)m_stServerSockaddr.sin_addr.s_addr == CSimpleSocket::SocketError) + { + TranslateSocketError(); + return bRetVal; + } + + m_stServerSockaddr.sin_port = htons(nPort); + + //------------------------------------------------------------------ + // Connect to address "xxx.xxx.xxx.xxx" (IPv4) address only. + // + //------------------------------------------------------------------ + m_timer.Initialize(); + m_timer.SetStartTime(); + + bRetVal = true; + m_bPeerHasClosed = false; + + TranslateSocketError(); + + m_timer.SetEndTime(); + + return bRetVal; +} + +//------------------------------------------------------------------------------ +// +// ConnectRAW() - +// +//------------------------------------------------------------------------------ +bool CSimpleSocket::ConnectRAW(const char *pAddr, uint16 nPort) +{ + bool bRetVal = false; + struct in_addr stIpAddress; + + // this is the Client + m_bIsServerSide = false; + + ClearSystemError(); + + //------------------------------------------------------------------ + // Pre-connection setup that must be performed + //------------------------------------------------------------------ + memset(&m_stServerSockaddr, 0, sizeof(m_stServerSockaddr)); + m_stServerSockaddr.sin_family = AF_INET; + + struct hostent *pHE = GETHOSTBYNAME(pAddr); + if (pHE == NULL) + { +#ifdef WIN32 + TranslateSocketError(); +#else + if (h_errno == HOST_NOT_FOUND) + { + SetSocketError(SocketInvalidAddress); + } +#endif + return bRetVal; + } + + if ( pHE->h_addrtype == AF_INET && (int)sizeof(stIpAddress) >= pHE->h_length ) + { + memcpy(&stIpAddress, pHE->h_addr_list[0], pHE->h_length); + m_stServerSockaddr.sin_addr.s_addr = stIpAddress.s_addr; + } + else + { +#if CHECK_PRINT_RUNTIME_ERR + fprintf(stderr, "\nERROR at ConnectRAW(): hostent * result of gethostbyname(\"%s\"): h_addrtype = %s , sizeof(in_addr) = %d < %d = h_length !\n\n" + , pAddr + , ( pHE->h_addrtype == AF_INET ? "IPv4" : ( pHE->h_addrtype == AF_INET6 ? "IPv6!" : "unknown" ) ) + , (int)sizeof(stIpAddress) + , pHE->h_length + ); + fprintf(stdout, "\nERROR at ConnectRAW(): hostent * result of gethostbyname(\"%s\"): h_addrtype = %s , sizeof(in_addr) = %d < %d = h_length !\n\n" + , pAddr + , ( pHE->h_addrtype == AF_INET ? "IPv4" : ( pHE->h_addrtype == AF_INET6 ? "IPv6!" : "unknown" ) ) + , (int)sizeof(stIpAddress) + , pHE->h_length + ); + fflush(stdout); + CoreDump(); + assert(0); // check, when this happens +#endif + memset(&m_stServerSockaddr.sin_addr.s_addr, CSimpleSocket::SocketError, sizeof(m_stServerSockaddr.sin_addr.s_addr)); + } + + if ((int32)m_stServerSockaddr.sin_addr.s_addr == CSimpleSocket::SocketError) + { + TranslateSocketError(); + return bRetVal; + } + + m_stServerSockaddr.sin_port = htons(nPort); + + //------------------------------------------------------------------ + // Connect to address "xxx.xxx.xxx.xxx" (IPv4) address only. + // + //------------------------------------------------------------------ + m_timer.Initialize(); + m_timer.SetStartTime(); + + if (connect(m_socket, (struct sockaddr*)&m_stServerSockaddr, sizeof(m_stServerSockaddr)) != CSimpleSocket::SocketError) + { + bRetVal = true; + m_bPeerHasClosed = false; + } + + TranslateSocketError(); + + m_timer.SetEndTime(); + + return bRetVal; +} + + //------------------------------------------------------------------------------ // // Initialize() - Initialize socket class @@ -135,19 +725,33 @@ CSimpleSocket *CSimpleSocket::operator=(CSimpleSocket &socket) //------------------------------------------------------------------------------ bool CSimpleSocket::Initialize() { - errno = CSimpleSocket::SocketSuccess; + ClearSystemError(); #ifdef WIN32 //------------------------------------------------------------------------- // Data structure containing general Windows Sockets Info //------------------------------------------------------------------------- + // use WinSock 2.2 + // see https://msdn.microsoft.com/en-us/library/windows/desktop/ms742213(v=vs.85).aspx + // Windows Sockets version 2.2 is supported on + // Windows Server 2008, + // Windows Vista, + // Windows Server 2003, + // Windows XP, + // Windows 2000, + // Windows NT 4.0 with Service Pack 4 (SP4) and later, + // Windows Me, + // Windows 98, + // Windows 95 OSR2, + // Windows 95 with the Windows Socket 2 Update memset(&m_hWSAData, 0, sizeof(m_hWSAData)); - WSAStartup(MAKEWORD(2, 0), &m_hWSAData); + WSAStartup(MAKEWORD(2, 2), &m_hWSAData); #endif //------------------------------------------------------------------------- // Create the basic Socket Handle //------------------------------------------------------------------------- + m_bPeerHasClosed = true; m_timer.Initialize(); m_timer.SetStartTime(); m_socket = socket(m_nSocketDomain, m_nSocketType, 0); @@ -159,6 +763,139 @@ bool CSimpleSocket::Initialize() } +//------------------------------------------------------------------------------ +// +// Open() - Create a connection to a specified address on a specified port +// also see .h +// +//------------------------------------------------------------------------------ +bool CSimpleSocket::Open(const char *pAddr, uint16 nPort) +{ + bool bRetVal = false; + m_bIsServerSide = false; + m_bPeerHasClosed = true; + + if (IsSocketValid() == false) + { + SetSocketError(CSimpleSocket::SocketInvalidSocket); + return bRetVal; + } + + if (pAddr == NULL) + { + SetSocketError(CSimpleSocket::SocketInvalidAddress); + return bRetVal; + } + + if (nPort == 0) + { + SetSocketError(CSimpleSocket::SocketInvalidPort); + return bRetVal; + } + + switch (m_nSocketType) + { + case CSimpleSocket::SocketTypeTcp : + { + bRetVal = ConnectTCP(pAddr, nPort); + break; + } + case CSimpleSocket::SocketTypeUdp : + { + bRetVal = ConnectUDP(pAddr, nPort); + break; + } + case CSimpleSocket::SocketTypeRaw : + break; + case CSimpleSocket::SocketTypeInvalid: + case CSimpleSocket::SocketTypeTcp6: + case CSimpleSocket::SocketTypeUdp6: + default: + break; + } + + //-------------------------------------------------------------------------- + // If successful then create a local copy of the address and port + //-------------------------------------------------------------------------- + if (bRetVal) + { + socklen_t nSockLen; + if(m_nSocketType != CSimpleSocket::SocketTypeUdp) + { + nSockLen = sizeof(struct sockaddr); + + memset(&m_stServerSockaddr, 0, nSockLen); + getpeername(m_socket, (struct sockaddr *)&m_stServerSockaddr, &nSockLen); + } + + nSockLen = sizeof(struct sockaddr); + memset(&m_stClientSockaddr, 0, nSockLen); + getsockname(m_socket, (struct sockaddr *)&m_stClientSockaddr, &nSockLen); + + SetSocketError(SocketSuccess); + } + + return bRetVal; +} + + +//------------------------------------------------------------------------------ +// +// Bind() +// +//------------------------------------------------------------------------------ +bool CSimpleSocket::Bind(const char *pInterface, uint16 nPort) +{ + bool bRetVal = false; +#ifdef WIN32 + ULONG inAddr; +#else + in_addr_t inAddr; +#endif + + memset(&m_stClientSockaddr,0,sizeof(m_stClientSockaddr)); + m_stClientSockaddr.sin_family = AF_INET; + m_stClientSockaddr.sin_port = htons(nPort); + + //-------------------------------------------------------------------------- + // If no IP Address (interface ethn) is supplied, or the loop back is + // specified then bind to any interface, else bind to specified interface. + //-------------------------------------------------------------------------- + if (pInterface && pInterface[0]) + { + inet_pton(AF_INET, pInterface, &inAddr); + } + else + { + inAddr = INADDR_ANY; + } + + m_stClientSockaddr.sin_addr.s_addr = inAddr; + + ClearSystemError(); + + //-------------------------------------------------------------------------- + // Bind to the specified port + //-------------------------------------------------------------------------- + if (bind(m_socket, (struct sockaddr *)&m_stClientSockaddr, sizeof(m_stClientSockaddr)) == 0) + { + bRetVal = true; + } + + //-------------------------------------------------------------------------- + // If there was a socket error then close the socket to clean out the + // connection in the backlog. + //-------------------------------------------------------------------------- + TranslateSocketError(); + + if (bRetVal == false) + { + Close(); + } + + return bRetVal; +} + //------------------------------------------------------------------------------ // // BindInterface() @@ -173,7 +910,7 @@ bool CSimpleSocket::BindInterface(const char *pInterface) { if (pInterface) { - stInterfaceAddr.s_addr= inet_addr(pInterface); + inet_pton(AF_INET, pInterface, &stInterfaceAddr.s_addr); if (SETSOCKOPT(m_socket, IPPROTO_IP, IP_MULTICAST_IF, &stInterfaceAddr, sizeof(stInterfaceAddr)) == SocketSuccess) { bRetVal = true; @@ -271,6 +1008,91 @@ int32 CSimpleSocket::GetSocketDscp(void) } +/// Returns clients Internet host address as a string in standard numbers-and-dots notation. +/// @return NULL if invalid +const char * CSimpleSocket::GetClientAddr() const +{ + return inet_ntoa(m_stClientSockaddr.sin_addr); +} + +/// Returns the port number on which the client is connected. +/// @return client port number. +uint16 CSimpleSocket::GetClientPort() const +{ + return ntohs(m_stClientSockaddr.sin_port); +} + +/// Returns server Internet host address as a string in standard numbers-and-dots notation. +/// @return NULL if invalid +const char * CSimpleSocket::GetServerAddr() const +{ + return inet_ntoa(m_stServerSockaddr.sin_addr); +} + +/// Returns the port number on which the server is connected. +/// @return server port number. +uint16 CSimpleSocket::GetServerPort() const +{ + return ntohs(m_stServerSockaddr.sin_port); +} + + +/// Returns if this is the Server side of the connection +bool CSimpleSocket::IsServerSide() const +{ + return m_bIsServerSide; +} + +/// Returns local Internet host address as a string in standard numbers-and-dots notation. +/// @return NULL if invalid +const char * CSimpleSocket::GetLocalAddr() const +{ + return (m_bIsServerSide) ? GetServerAddr() : GetClientAddr(); +} + +/// Returns the port number on which the local socket is connected. +/// @return client port number. +uint16 CSimpleSocket::GetLocalPort() const +{ + return (m_bIsServerSide) ? GetServerPort() : GetClientPort(); +} + + +/// Returns Peer's Internet host address as a string in standard numbers-and-dots notation. +/// @return NULL if invalid +const char * CSimpleSocket::GetPeerAddr() const +{ + return (m_bIsServerSide) ? GetClientAddr() : GetServerAddr(); +} + +/// Returns the port number on which the peer is connected. +/// @return client port number. +uint16 CSimpleSocket::GetPeerPort() const +{ + return (m_bIsServerSide) ? GetClientPort() : GetServerPort(); +} + +uint32 CSimpleSocket::GetReceiveWindowSize() +{ + return GetWindowSize(SO_RCVBUF); +} + +uint32 CSimpleSocket::GetSendWindowSize() +{ + return GetWindowSize(SO_SNDBUF); +} + +uint32 CSimpleSocket::SetReceiveWindowSize(uint32 nWindowSize) +{ + return SetWindowSize(SO_RCVBUF, nWindowSize); +} + +uint32 CSimpleSocket::SetSendWindowSize(uint32 nWindowSize) +{ + return SetWindowSize(SO_SNDBUF, nWindowSize); +} + + //------------------------------------------------------------------------------ // // GetWindowSize() @@ -283,15 +1105,17 @@ uint32 CSimpleSocket::GetWindowSize(uint32 nOptionName) //------------------------------------------------------------------------- // no socket given, return system default allocate our own new socket //------------------------------------------------------------------------- - if (m_socket != CSimpleSocket::SocketError) + if (m_socket != INVALID_SOCKET) { socklen_t nLen = sizeof(nTcpWinSize); //--------------------------------------------------------------------- // query for buffer size //--------------------------------------------------------------------- - GETSOCKOPT(m_socket, SOL_SOCKET, nOptionName, &nTcpWinSize, &nLen); - TranslateSocketError(); + if (GETSOCKOPT(m_socket, SOL_SOCKET, nOptionName, &nTcpWinSize, &nLen) == SocketError) + { + TranslateSocketError(); + } } else { @@ -312,10 +1136,14 @@ uint32 CSimpleSocket::SetWindowSize(uint32 nOptionName, uint32 nWindowSize) //------------------------------------------------------------------------- // no socket given, return system default allocate our own new socket //------------------------------------------------------------------------- - if (m_socket != CSimpleSocket::SocketError) + if (m_socket != INVALID_SOCKET) { - SETSOCKOPT(m_socket, SOL_SOCKET, nOptionName, &nWindowSize, sizeof(nWindowSize)); - TranslateSocketError(); + if (SETSOCKOPT(m_socket, SOL_SOCKET, nOptionName, &nWindowSize, sizeof(nWindowSize)) == SocketError) + { + TranslateSocketError(); + } + + return GetWindowSize(nOptionName); } else { @@ -343,8 +1171,10 @@ bool CSimpleSocket::DisableNagleAlgoritm() { bRetVal = true; } - - TranslateSocketError(); + else + { + TranslateSocketError(); + } return bRetVal; } @@ -367,13 +1197,49 @@ bool CSimpleSocket::EnableNagleAlgoritm() { bRetVal = true; } - - TranslateSocketError(); + else + { + TranslateSocketError(); + } return bRetVal; } +uint32 CSimpleSocket::GetIPv4AddrInfoStatic( const char *pAddr, CSocketType nSocketType ) +{ + if (!pAddr || !pAddr[0]) + return 0; + struct in_addr stIpAddress; + bool ret = GetAddrInfoStatic( pAddr, 0, &stIpAddress, nSocketType ); + if (!ret) + return 0; + return ntohl(stIpAddress.s_addr); +} + + +uint32 CSimpleSocket::GetIPv4AddrInfo( const char *pAddr ) +{ + if (!pAddr || !pAddr[0]) + return 0; + struct in_addr stIpAddress; + bool ret = GetAddrInfo( pAddr, 0, &stIpAddress ); + if (!ret) + return 0; + return ntohl(stIpAddress.s_addr); +} + + +/// Set object socket handle to that specified as parameter +/// @param socket value of socket descriptor +void CSimpleSocket::SetSocketHandle(SOCKET socket, bool bIsServerSide ) +{ + m_socket = socket; + m_bIsServerSide = bIsServerSide; + m_bPeerHasClosed = false; +} + + //------------------------------------------------------------------------------ // // Send() - Send data on a valid socket @@ -381,6 +1247,7 @@ bool CSimpleSocket::EnableNagleAlgoritm() //------------------------------------------------------------------------------ int32 CSimpleSocket::Send(const uint8 *pBuf, size_t bytesToSend) { + ClearSystemError(); SetSocketError(SocketSuccess); m_nBytesSent = 0; @@ -423,29 +1290,23 @@ int32 CSimpleSocket::Send(const uint8 *pBuf, size_t bytesToSend) // Check error condition and attempt to resend if call // was interrupted by a signal. //--------------------------------------------------------- - // if (GetMulticast()) - // { - // do - // { - // m_nBytesSent = SENDTO(m_socket, pBuf, bytesToSend, 0, (const sockaddr *)&m_stMulticastGroup, - // sizeof(m_stMulticastGroup)); - // TranslateSocketError(); - // } while (GetSocketError() == CSimpleSocket::SocketInterrupted); - // } - // else + do { - do - { - m_nBytesSent = SENDTO(m_socket, pBuf, bytesToSend, 0, (const sockaddr *)&m_stServerSockaddr, sizeof(m_stServerSockaddr)); - TranslateSocketError(); - } while (GetSocketError() == CSimpleSocket::SocketInterrupted); - } + m_nBytesSent = SENDTO(m_socket, pBuf, bytesToSend, 0, + (const sockaddr *)&m_stServerSockaddr, + sizeof(m_stServerSockaddr)); + TranslateSocketError(); + } while (GetSocketError() == CSimpleSocket::SocketInterrupted); m_timer.SetEndTime(); } } break; } + case CSimpleSocket::SocketTypeInvalid: + case CSimpleSocket::SocketTypeTcp6: + case CSimpleSocket::SocketTypeUdp6: + case CSimpleSocket::SocketTypeRaw: default: break; } @@ -477,15 +1338,15 @@ bool CSimpleSocket::Close(void) //-------------------------------------------------------------------------- if (IsSocketValid()) { + ClearSystemError(); if (CLOSE(m_socket) != CSimpleSocket::SocketError) { m_socket = INVALID_SOCKET; bRetVal = true; } + TranslateSocketError(); } - TranslateSocketError(); - return bRetVal; } @@ -499,13 +1360,22 @@ bool CSimpleSocket::Shutdown(CShutdownMode nShutdown) { CSocketError nRetVal = SocketEunknown; - nRetVal = (CSocketError)shutdown(m_socket, CSimpleSocket::Sends); - TranslateSocketError(); + nRetVal = (CSocketError)shutdown(m_socket, nShutdown); + if (nRetVal == SocketError) + { + TranslateSocketError(); + } return (nRetVal == CSimpleSocket::SocketSuccess) ? true: false; } +bool CSimpleSocket::Select(void) +{ + return Select(0,0); +} + + //------------------------------------------------------------------------------ // // Flush() @@ -602,6 +1472,18 @@ int32 CSimpleSocket::Send(const struct iovec *sendVector, int32 nNumItems) } +//------------------------------------------------------------------------------ +// +// SetConnectTimeout() +// +//------------------------------------------------------------------------------ +void CSimpleSocket::SetConnectTimeout(int32 nConnectTimeoutSec, int32 nConnectTimeoutUsec) +{ + m_stConnectTimeout.tv_sec = nConnectTimeoutSec; + m_stConnectTimeout.tv_usec = nConnectTimeoutUsec; +} + + //------------------------------------------------------------------------------ // // SetReceiveTimeout() @@ -611,16 +1493,29 @@ bool CSimpleSocket::SetReceiveTimeout(int32 nRecvTimeoutSec, int32 nRecvTimeoutU { bool bRetVal = true; - memset(&m_stRecvTimeout, 0, sizeof(struct timeval)); +#ifdef WIN32 + uint32 nMillis = nRecvTimeoutSec * 1000 + nRecvTimeoutUsec / 1000; + if ( nMillis == 0 && ( nRecvTimeoutUsec > 0 || nRecvTimeoutSec > 0 ) ) + nMillis = 1; + nRecvTimeoutSec = nMillis / 1000; + nRecvTimeoutUsec = nMillis % 1000; +#endif + memset(&m_stRecvTimeout, 0, sizeof(struct timeval)); m_stRecvTimeout.tv_sec = nRecvTimeoutSec; m_stRecvTimeout.tv_usec = nRecvTimeoutUsec; //-------------------------------------------------------------------------- // Sanity check to make sure the options are supported! //-------------------------------------------------------------------------- +#ifdef WIN32 + // see https://msdn.microsoft.com/en-us/library/windows/desktop/ms740476(v=vs.85).aspx + if (SETSOCKOPT(m_socket, SOL_SOCKET, SO_RCVTIMEO, &nMillis, + sizeof(nMillis)) == CSimpleSocket::SocketError) +#else if (SETSOCKOPT(m_socket, SOL_SOCKET, SO_RCVTIMEO, &m_stRecvTimeout, sizeof(struct timeval)) == CSimpleSocket::SocketError) +#endif { bRetVal = false; TranslateSocketError(); @@ -639,6 +1534,14 @@ bool CSimpleSocket::SetSendTimeout(int32 nSendTimeoutSec, int32 nSendTimeoutUsec { bool bRetVal = true; +#ifdef WIN32 + uint32 nMillis = nSendTimeoutSec * 1000 + nSendTimeoutUsec / 1000; + if ( nMillis == 0 && ( nSendTimeoutUsec > 0 || nSendTimeoutSec > 0 ) ) + nMillis = 1; + nSendTimeoutSec = nMillis / 1000; + nSendTimeoutUsec = nMillis % 1000; +#endif + memset(&m_stSendTimeout, 0, sizeof(struct timeval)); m_stSendTimeout.tv_sec = nSendTimeoutSec; m_stSendTimeout.tv_usec = nSendTimeoutUsec; @@ -646,8 +1549,14 @@ bool CSimpleSocket::SetSendTimeout(int32 nSendTimeoutSec, int32 nSendTimeoutUsec //-------------------------------------------------------------------------- // Sanity check to make sure the options are supported! //-------------------------------------------------------------------------- +#ifdef WIN32 + // see https://msdn.microsoft.com/en-us/library/windows/desktop/ms740476(v=vs.85).aspx + if (SETSOCKOPT(m_socket, SOL_SOCKET, SO_SNDTIMEO, &nMillis, + sizeof(nMillis)) == CSimpleSocket::SocketError) +#else if (SETSOCKOPT(m_socket, SOL_SOCKET, SO_SNDTIMEO, &m_stSendTimeout, sizeof(struct timeval)) == CSimpleSocket::SocketError) +#endif { bRetVal = false; TranslateSocketError(); @@ -671,8 +1580,10 @@ bool CSimpleSocket::SetOptionReuseAddr() { bRetVal = true; } - - TranslateSocketError(); + else + { + TranslateSocketError(); + } return bRetVal; } @@ -683,19 +1594,21 @@ bool CSimpleSocket::SetOptionReuseAddr() // SetOptionLinger() // //------------------------------------------------------------------------------ -bool CSimpleSocket::SetOptionLinger(bool bEnable, uint16 nTime) +bool CSimpleSocket::SetOptionLinger(bool bEnable, uint16 nTimeInSeconds) { bool bRetVal = false; m_stLinger.l_onoff = (bEnable == true) ? 1: 0; - m_stLinger.l_linger = nTime; + m_stLinger.l_linger = nTimeInSeconds; if (SETSOCKOPT(m_socket, SOL_SOCKET, SO_LINGER, &m_stLinger, sizeof(m_stLinger)) == 0) { bRetVal = true; } - - TranslateSocketError(); + else + { + TranslateSocketError(); + } return bRetVal; } @@ -747,6 +1660,7 @@ int32 CSimpleSocket::Receive(int32 nMaxBytes, uint8 * pBuffer ) pWorkBuffer = m_pBuffer; } + ClearSystemError(); SetSocketError(SocketSuccess); m_timer.Initialize(); @@ -764,6 +1678,13 @@ int32 CSimpleSocket::Receive(int32 nMaxBytes, uint8 * pBuffer ) { m_nBytesReceived = RECV(m_socket, (pWorkBuffer + m_nBytesReceived), nMaxBytes, m_nFlags); + if ( 0 == m_nBytesReceived ) + { +#if PRINT_CLOSE + fprintf(stderr, "CSimpleSocket::Receive() = 0 Bytes ==> peer closed connection!\n"); +#endif + m_bPeerHasClosed = true; + } TranslateSocketError(); } while ((GetSocketError() == CSimpleSocket::SocketInterrupted)); @@ -796,6 +1717,10 @@ int32 CSimpleSocket::Receive(int32 nMaxBytes, uint8 * pBuffer ) break; } + case CSimpleSocket::SocketTypeInvalid: + case CSimpleSocket::SocketTypeTcp6: + case CSimpleSocket::SocketTypeUdp6: + case CSimpleSocket::SocketTypeRaw: default: break; } @@ -822,6 +1747,36 @@ int32 CSimpleSocket::Receive(int32 nMaxBytes, uint8 * pBuffer ) } +/// Attempts to return the number of Bytes waiting at next Receive() +/// @return number of bytes ready for Receive() +/// @return of -1 means that an error has occurred. +int32 CSimpleSocket::GetNumReceivableBytes() +{ + //-------------------------------------------------------------------------- + // If the socket is invalid then return false. + //-------------------------------------------------------------------------- + if (IsSocketValid() == false) + { + return -1; + } + +#if WIN32 + u_long numBytesInSocket = 0; + if ( ::ioctlsocket(m_socket, FIONREAD, &numBytesInSocket) < 0 ) + return -1; + + return (int32)numBytesInSocket; +#elif defined(_LINUX) || defined(_DARWIN) + int32 numBytesInSocket = 0; + if ( ioctl(m_socket, FIONREAD, (char*)&numBytesInSocket) < 0 ) + return -1; + + return numBytesInSocket; +#else +#error missing implementation for GetNumReceivableBytes() +#endif +} + //------------------------------------------------------------------------------ // // SetNonblocking() @@ -875,6 +1830,7 @@ bool CSimpleSocket::SetBlocking(void) if (ioctlsocket(m_socket, FIONBIO, (ULONG *)&nCurFlags) != 0) { + TranslateSocketError(); return false; } #else @@ -907,6 +1863,8 @@ int32 CSimpleSocket::SendFile(int32 nOutFd, int32 nInFd, off_t *pOffset, int32 n { int32 nOutCount = CSimpleSocket::SocketError; + ClearSystemError(); + static char szData[SOCKET_SENDFILE_BLOCKSIZE]; int32 nInCount = 0; @@ -940,6 +1898,44 @@ int32 CSimpleSocket::SendFile(int32 nOutFd, int32 nInFd, off_t *pOffset, int32 n } +bool CSimpleSocket::IsSocketValid(void) const +{ +#if PRINT_CLOSE + if ( m_socket == SocketError ) + fprintf(stderr, "reporting CSimpleSocket::IsSocketValid() == false.\n" ); +#endif + return (m_socket != INVALID_SOCKET); +} + + +bool CSimpleSocket::IsSocketPeerClosed(void) const +{ +#if PRINT_CLOSE + if ( m_bPeerHasClosed ) + fprintf(stderr, "reporting CSimpleSocket::IsSocketPeerClosed() == true.\n" ); +#endif + return m_bPeerHasClosed; +} + + +//------------------------------------------------------------------------------ +// +// ClearSystemError() - +// +//------------------------------------------------------------------------------ + +void CSimpleSocket::ClearSystemError(void) +{ +#if defined(_LINUX) || defined(_DARWIN) + errno = EXIT_SUCCESS; +#elif defined(WIN32) + WSASetLastError( EXIT_SUCCESS ); +#else +#error unsupported platform! +#endif +} + + //------------------------------------------------------------------------------ // // TranslateSocketError() - @@ -947,8 +1943,14 @@ int32 CSimpleSocket::SendFile(int32 nOutFd, int32 nInFd, off_t *pOffset, int32 n //------------------------------------------------------------------------------ void CSimpleSocket::TranslateSocketError(void) { + // see + // * http://man7.org/linux/man-pages/man3/errno.3.html + // * https://msdn.microsoft.com/en-us/library/windows/desktop/ms740668(v=vs.85).aspx# + // for the system's error code's + // it's quite difficult to map all of them to some CSocketError! #if defined(_LINUX) || defined(_DARWIN) - switch (errno) + int systemErrno = errno; + switch (systemErrno) { case EXIT_SUCCESS: SetSocketError(CSimpleSocket::SocketSuccess); @@ -1002,15 +2004,31 @@ void CSimpleSocket::TranslateSocketError(void) case ENOPROTOOPT: SetSocketError(CSimpleSocket::SocketConnectionReset); break; + case EADDRNOTAVAIL: case EADDRINUSE: SetSocketError(CSimpleSocket::SocketAddressInUse); break; + case ESHUTDOWN: + case EHOSTUNREACH: + case ENETUNREACH: + case ENETDOWN: + SetSocketError(CSimpleSocket::SocketNetworkError); + break; default: + #if PRINT_ERRORS + fprintf( stderr, "\nCSimpleSocket::SocketEunknown: errno=%d: %s\n", systemErrno, strerror(systemErrno) ); + #endif SetSocketError(CSimpleSocket::SocketEunknown); break; } -#endif -#ifdef WIN32 + #if PRINT_ERRORS + if ( systemErrno != EXIT_SUCCESS && systemErrno != EWOULDBLOCK ) + { + fprintf( stderr, "\nCSimpleSocket Error: %s == system %s\n", DescribeError(), strerror(systemErrno) ); + } + #endif + +#elif defined(WIN32) int32 nError = WSAGetLastError(); switch (nError) { @@ -1057,15 +2075,24 @@ void CSimpleSocket::TranslateSocketError(void) SetSocketError(CSimpleSocket::SocketInvalidAddress); break; case WSAEADDRINUSE: + case WSAEADDRNOTAVAIL: SetSocketError(CSimpleSocket::SocketAddressInUse); break; case WSAEFAULT: SetSocketError(CSimpleSocket::SocketInvalidPointer); break; + case WSAENETDOWN: + case WSAENETUNREACH: + case WSAENETRESET: + case WSAESHUTDOWN: + SetSocketError(CSimpleSocket::SocketNetworkError); + break; default: SetSocketError(CSimpleSocket::SocketEunknown); break; } +#else +#error unsupported platform! #endif } @@ -1116,6 +2143,8 @@ const char *CSimpleSocket::DescribeError(CSocketError err) return "Pointer type supplied as argument is invalid."; case CSimpleSocket::SocketEunknown: return "Unknown error"; + case CSimpleSocket::SocketNetworkError: + return "Network error"; default: return "No such CSimpleSocket error"; } @@ -1126,7 +2155,7 @@ const char *CSimpleSocket::DescribeError(CSocketError err) // Select() // //------------------------------------------------------------------------------ -bool CSimpleSocket::Select(int32 nTimeoutSec, int32 nTimeoutUSec) +bool CSimpleSocket::Select(int32 nTimeoutSec, int32 nTimeoutUSec, bool bAwakeWhenReadable, bool bAwakeWhenWritable) { bool bRetVal = false; struct timeval *pTimeout = NULL; @@ -1134,12 +2163,23 @@ bool CSimpleSocket::Select(int32 nTimeoutSec, int32 nTimeoutUSec) int32 nNumDescriptors = -1; int32 nError = 0; + // if (m_socket >= 1024) + // { + // SetSocketError(CSimpleSocket::SocketEunknown); + // fprintf(stderr, "error: select() with socket %d >= 1024\n", m_socket); + // // return false; + // } + FD_ZERO(&m_errorFds); FD_ZERO(&m_readFds); FD_ZERO(&m_writeFds); + + if ( bAwakeWhenReadable ) + FD_SET(m_socket, &m_readFds); + if ( bAwakeWhenWritable ) + FD_SET(m_socket, &m_writeFds); + FD_SET(m_socket, &m_errorFds); - FD_SET(m_socket, &m_readFds); - FD_SET(m_socket, &m_writeFds); //--------------------------------------------------------------------- // If timeout has been specified then set value, otherwise set timeout @@ -1153,8 +2193,9 @@ bool CSimpleSocket::Select(int32 nTimeoutSec, int32 nTimeoutUSec) pTimeout = &timeout; } + ClearSystemError(); + nNumDescriptors = SELECT(m_socket+1, &m_readFds, &m_writeFds, &m_errorFds, pTimeout); -// nNumDescriptors = SELECT(m_socket+1, &m_readFds, NULL, NULL, pTimeout); //---------------------------------------------------------------------- // Handle timeout @@ -1164,6 +2205,13 @@ bool CSimpleSocket::Select(int32 nTimeoutSec, int32 nTimeoutUSec) SetSocketError(CSimpleSocket::SocketTimedout); } //---------------------------------------------------------------------- + // Handle error + //---------------------------------------------------------------------- + else if ( nNumDescriptors == SocketError ) + { + TranslateSocketError(); + } + //---------------------------------------------------------------------- // If a file descriptor (read/write) is set then check the // socket error (SO_ERROR) to see if there is a pending error. //---------------------------------------------------------------------- diff --git a/src/SimpleSocket.h b/src/SimpleSocket.h index 4a5a9be..423a57d 100644 --- a/src/SimpleSocket.h +++ b/src/SimpleSocket.h @@ -93,13 +93,32 @@ #define SOCKET_SENDFILE_BLOCKSIZE 8192 + +#ifndef __cplusplus + #error C++ compiler required! +#else + #if ( __cplusplus >= 201103L ) + #define CLSOCKET_OVERRIDE override + #elif ( 0 && defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ > 7 || \ + (__GNUC_MINOR__ == 7 && __GNUC_PATCHLEVEL__ > 1)) ) + #define CLSOCKET_OVERRIDE override + #elif ( defined(_MSC_VER) && ( _MSC_VER >= 1400) ) + #define CLSOCKET_OVERRIDE override + #else + #define CLSOCKET_OVERRIDE + #endif +#endif + + + /// Provides a platform independent class to for socket development. /// This class is designed to abstract socket communication development in a /// platform independent manner. /// - Socket types /// -# CActiveSocket Class /// -# CPassiveSocket Class -class EXPORT CSimpleSocket { +class CLSOCKET_API CSimpleSocket { + friend class CPassiveSocket; public: /// Defines the three possible states for shuting down a socket. typedef enum @@ -130,9 +149,10 @@ class EXPORT CSimpleSocket { SocketInvalidPort, ///< Invalid destination port specified. SocketConnectionRefused, ///< No server is listening at remote address. SocketTimedout, ///< Timed out while attempting operation. - SocketEwouldblock, ///< Operation would block if socket were blocking. + SocketEwouldblock, ///< Operation would block if socket were blocking. On Accept/Receive/Send/ SocketNotconnected, ///< Currently not connected. - SocketEinprogress, ///< Socket is non-blocking and the connection cannot be completed immediately + SocketEinprogress, ///< Socket is non-blocking and the connection cannot be completed immediately. + /// only on Open/ConnectTo/.. SocketInterrupted, ///< Call was interrupted by a signal that was caught before a valid connection arrived. SocketConnectionAborted, ///< The connection has been aborted. SocketProtocolError, ///< Invalid protocol for operation. @@ -141,21 +161,15 @@ class EXPORT CSimpleSocket { SocketConnectionReset, ///< Connection was forcibly closed by the remote host. SocketAddressInUse, ///< Address already in use. SocketInvalidPointer, ///< Pointer type supplied as argument is invalid. - SocketEunknown ///< Unknown error please report to mark@carrierlabs.com + SocketEunknown, ///< Unknown error + SocketNetworkError ///< Several network errors } CSocketError; public: CSimpleSocket(CSocketType type = SocketTypeTcp); CSimpleSocket(CSimpleSocket &socket); - virtual ~CSimpleSocket() - { - if (m_pBuffer != NULL) - { - delete [] m_pBuffer; - m_pBuffer = NULL; - } - }; + virtual ~CSimpleSocket(); /// Initialize instance of CSocket. This method MUST be called before an /// object can be used. Errors : CSocket::SocketProtocolError, @@ -163,6 +177,21 @@ class EXPORT CSimpleSocket { /// @return true if properly initialized. virtual bool Initialize(void); + /// Establishes a connection to the (server-)address specified by pAddr + /// and (server-)port specified by nPort. + /// Connection-based protocol sockets (CSocket::SocketTypeTcp) may + /// successfully call Open() only once, however; connectionless protocol + /// sockets (CSocket::SocketTypeUdp) may use Open() multiple times to + /// change their association. + /// @param pAddr specifies the destination address to connect. + /// @param nPort specifies the destination port. + /// @return true if successful connection made, otherwise false. + virtual bool Open(const char *pAddr, uint16 nPort); + + inline bool ConnectTo( const char * pAddr, uint16 nPort ) { + return Open(pAddr, nPort); + }; + /// Close socket /// @return true if successfully closed otherwise returns false. virtual bool Close(void); @@ -170,20 +199,27 @@ class EXPORT CSimpleSocket { /// Shutdown shut down socket send and receive operations /// CShutdownMode::Receives - Disables further receive operations. /// CShutdownMode::Sends - Disables further send operations. - /// CShutdownBoth:: - Disables further send and receive operations. + /// CShutdownMode::Both - Disables further send and receive operations. + /// see http://stackoverflow.com/questions/4160347/close-vs-shutdown-socket /// @param nShutdown specifies the type of shutdown. /// @return true if successfully shutdown otherwise returns false. virtual bool Shutdown(CShutdownMode nShutdown); + inline bool CloseForReads() { + return Shutdown( CSimpleSocket::Receives ); + } + + inline bool CloseForWrites() { + return Shutdown( CSimpleSocket::Sends ); + } + /// Examine the socket descriptor sets currently owned by the instance of /// the socket class (the readfds, writefds, and errorfds parameters) to /// see whether some of their descriptors are ready for reading, are ready /// for writing, or have an exceptional condition pending, respectively. /// Block until an event happens on the specified file descriptors. /// @return true if socket has data ready, or false if not ready or timed out. - virtual bool Select(void) { - return Select(0,0); - }; + virtual bool Select(void); /// Examine the socket descriptor sets currently owned by the instance of /// the socket class (the readfds, writefds, and errorfds parameters) to @@ -192,15 +228,43 @@ class EXPORT CSimpleSocket { /// @param nTimeoutSec timeout in seconds for select. /// @param nTimeoutUSec timeout in micro seconds for select. /// @return true if socket has data ready, or false if not ready or timed out. - virtual bool Select(int32 nTimeoutSec, int32 nTimeoutUSec); + virtual bool Select(int32 nTimeoutSec, int32 nTimeoutUSec, + bool bAwakeWhenReadable = true, bool bAwakeWhenWritable = true ); + + inline bool WaitUntilReadable( int32 nTimeoutMillis ) { + return Select( nTimeoutMillis / 1000 , 1000 * (nTimeoutMillis % 1000), true, false ); + } + + inline bool WaitUntilWritable( int32 nTimeoutMillis ) { + return Select( nTimeoutMillis / 1000 , 1000 * (nTimeoutMillis % 1000), false, true ); + } /// Does the current instance of the socket object contain a valid socket /// descriptor. /// @return true if the socket object contains a valid socket descriptor. - virtual bool IsSocketValid(void) { - return (m_socket != SocketError); + virtual bool IsSocketValid(void) const; + + + inline bool IsSocketInvalid(void) const { + return !IsSocketValid(); + }; + + + /// Is the current instance of the socket already closed from peer? + /// Information is updated on Receive() ! + /// @return true if the socket was closed + virtual bool IsSocketPeerClosed(void) const; + + + inline bool IsSocketPeerOpen(void) const { + return !IsSocketPeerClosed(); }; + + /// Clear "last" system error + /// - so that TranslateSocketError() does not try to translate someone else's error! + void ClearSystemError(void); + /// Provides a standard error code for cross platform development by /// mapping the operating system error to an error defined by the CSocket /// class. @@ -213,6 +277,14 @@ class EXPORT CSimpleSocket { return DescribeError(m_socketErrno); }; + static inline const char * GetSocketErrorText(CSocketError err) { + return DescribeError(err); + } + + inline const char * GetSocketErrorText() { + return DescribeError(m_socketErrno); + } + /// Attempts to receive a block of data on an established connection. /// @param nMaxBytes maximum number of bytes to receive. /// @param pBuffer, memory where to receive the data, @@ -223,6 +295,17 @@ class EXPORT CSimpleSocket { /// @return of -1 means that an error has occurred. virtual int32 Receive(int32 nMaxBytes = 1, uint8 * pBuffer = 0); + inline int32 Receive(int32 nMaxBytes, char * pBuffer) { + return Receive(nMaxBytes, (uint8*)pBuffer); + }; + + + /// Attempts to return the number of Bytes waiting at next Receive() + /// @return number of bytes ready for Receive() + /// @return of -1 means that an error has occurred. + virtual int32 GetNumReceivableBytes(); + + /// Attempts to send a block of data on an established connection. /// @param pBuf block of data to be sent. /// @param bytesToSend size of data block to be sent. @@ -231,6 +314,18 @@ class EXPORT CSimpleSocket { /// @return of -1 means that an error has occurred. virtual int32 Send(const uint8 *pBuf, size_t bytesToSend); + inline int32 Send(const char *pBuf, size_t bytesToSend) { + return Send( (const uint8 *)pBuf, bytesToSend ); + } + + inline int32 Transmit(const uint8 *pBuf, size_t bytesToSend) { + return Send( pBuf, bytesToSend ); + } + + inline int32 Transmit(const char *pBuf, size_t bytesToSend) { + return Send( (const uint8 *)pBuf, bytesToSend ); + } + /// Attempts to send at most nNumItem blocks described by sendVector /// to the socket descriptor associated with the socket object. /// @param sendVector pointer to an array of iovec structures @@ -257,10 +352,15 @@ class EXPORT CSimpleSocket { /// Returns blocking/non-blocking state of socket. /// @return true if the socket is non-blocking, else return false. - bool IsNonblocking(void) { + bool IsNonblocking(void) const { return (m_bIsBlocking == false); }; + inline bool IsBlocking(void) const { + return !IsNonblocking(); + } + + /// Set the socket to blocking. /// @return true if successful set to blocking, else return false; bool SetBlocking(void); @@ -304,9 +404,9 @@ class EXPORT CSimpleSocket { /// Closed by the remote system). ///

/// @param bEnable true to enable option false to disable option. - /// @param nTime time in seconds to linger. + /// @param nTimeInSeconds time in seconds to linger. /// @return true if option successfully set - bool SetOptionLinger(bool bEnable, uint16 nTime); + bool SetOptionLinger(bool bEnable, uint16 nTimeInSeconds); /// Tells the kernel that even if this port is busy (in the TIME_WAIT state), /// go ahead and reuse it anyway. If it is busy, but with another state, @@ -338,10 +438,10 @@ class EXPORT CSimpleSocket { /// @param nConnectTimeoutSec of timeout in seconds. /// @param nConnectTimeoutUsec of timeout in microseconds. /// @return true if socket connection timeout was successfully set. - void SetConnectTimeout(int32 nConnectTimeoutSec, int32 nConnectTimeoutUsec = 0) - { - m_stConnectTimeout.tv_sec = nConnectTimeoutSec; - m_stConnectTimeout.tv_usec = nConnectTimeoutUsec; + void SetConnectTimeout(int32 nConnectTimeoutSec, int32 nConnectTimeoutUsec = 0); + + inline void SetConnectTimeoutMillis(int32 nConnectTimeoutMillis) { + SetConnectTimeout( nConnectTimeoutMillis / 1000, 1000 * ( nConnectTimeoutMillis % 1000 ) ); }; /// Gets the timeout value that specifies the maximum number of seconds a @@ -370,6 +470,10 @@ class EXPORT CSimpleSocket { /// @return true if socket timeout was successfully set. bool SetReceiveTimeout(int32 nRecvTimeoutSec, int32 nRecvTimeoutUsec = 0); + inline bool SetReceiveTimeoutMillis(int32 nRecvTimeoutMillis) { + return SetReceiveTimeout( nRecvTimeoutMillis / 1000 , 1000 * (nRecvTimeoutMillis % 1000) ); + }; + /// Enable/disable multicast for a socket. This options is only valid for /// socket descriptors of type CSimpleSocket::SocketTypeUdp. /// @return true if multicast was enabled or false if socket type is not @@ -387,6 +491,13 @@ class EXPORT CSimpleSocket { /// @return true if successfully bound to interface bool BindInterface(const char *pInterface); + + /// Bind socket to a specific interface in general for tcp/udp clients + /// @param pInterface - interface on which to bind. + /// @param nPort - port on which multicast + /// @return true if able to bind to interface. + bool Bind(const char *pInterface, uint16 nPort); + /// Gets the timeout value that specifies the maximum number of seconds a /// a call to CSimpleSocket::Send waits until it completes. /// @return the length of time in seconds @@ -406,10 +517,14 @@ class EXPORT CSimpleSocket { /// @return the length of time in seconds bool SetSendTimeout(int32 nSendTimeoutSec, int32 nSendTimeoutUsec = 0); - /// Returns the last error that occured for the instace of the CSimpleSocket + inline bool SetSendTimeoutMillis(int32 nSendTimeoutMillis) { + return SetSendTimeout( nSendTimeoutMillis / 1000 , 1000 * (nSendTimeoutMillis % 1000) ); + }; + + /// Returns the last error that occurred for the instance of the CSimpleSocket /// instance. This method should be called immediately to retrieve the /// error code for the failing mehtod call. - /// @return last error that occured. + /// @return last error that occurred. CSocketError GetSocketError(void) { return m_socketErrno; }; @@ -450,56 +565,73 @@ class EXPORT CSimpleSocket { }; /// Returns clients Internet host address as a string in standard numbers-and-dots notation. + /// on TCP Server the Client and Peer Address/Port get valid with successful Accept() + /// on UDP Server the Client and Peer Address/Port get valid with successful Receive() + /// ATTENTION: return is same static buffer as in inet_ntoa(), GetClientAddr(), GetServerAddr(), GetLocalAddr(), GetPeerAddr() /// @return NULL if invalid - const char *GetClientAddr() { - return inet_ntoa(m_stClientSockaddr.sin_addr); - }; + const char * GetClientAddr() const; /// Returns the port number on which the client is connected. + /// on TCP Server the Client and Peer Address/Port get valid with successful Accept() + /// on UDP Server the Client and Peer Address/Port get valid with successful Receive() /// @return client port number. - uint16 GetClientPort() { - return m_stClientSockaddr.sin_port; - }; + uint16 GetClientPort() const; /// Returns server Internet host address as a string in standard numbers-and-dots notation. + /// ATTENTION: return is same static buffer as in inet_ntoa(), GetClientAddr(), GetServerAddr(), GetLocalAddr(), GetPeerAddr() /// @return NULL if invalid - const char *GetServerAddr() { - return inet_ntoa(m_stServerSockaddr.sin_addr); - }; + const char * GetServerAddr() const; /// Returns the port number on which the server is connected. /// @return server port number. - uint16 GetServerPort() { - return ntohs(m_stServerSockaddr.sin_port); - }; + uint16 GetServerPort() const; + + /// Returns if this is the Server side of the connection + bool IsServerSide() const; + + /// Returns local Internet host address as a string in standard numbers-and-dots notation. + /// ATTENTION: return is same static buffer as in inet_ntoa(), GetClientAddr(), GetServerAddr(), GetLocalAddr(), GetPeerAddr() + /// @return NULL if invalid + const char * GetLocalAddr() const; + + /// Returns the port number on which the local socket is connected. + /// @return client port number. + uint16 GetLocalPort() const; + + + /// Returns Peer's Internet host address as a string in standard numbers-and-dots notation. + /// on TCP Server the Client and Peer Address/Port get valid with successful Accept() + /// on UDP Server the Client and Peer Address/Port get valid with successful Receive() + /// ATTENTION: return is same static buffer as in inet_ntoa(), GetClientAddr(), GetServerAddr(), GetLocalAddr(), GetPeerAddr() + /// @return NULL if invalid + const char * GetPeerAddr() const; + + /// Returns the port number on which the peer is connected. + /// on TCP Server the Client and Peer Address/Port get valid with successful Accept() + /// on UDP Server the Client and Peer Address/Port get valid with successful Receive() + /// @return client port number. + uint16 GetPeerPort() const; + /// Get the TCP receive buffer window size for the current socket object. ///

\b NOTE: Linux will set the receive buffer to twice the value passed. /// @return zero on failure else the number of bytes of the TCP receive buffer window size if successful. - uint32 GetReceiveWindowSize() { - return GetWindowSize(SO_RCVBUF); - }; + uint32 GetReceiveWindowSize(); /// Get the TCP send buffer window size for the current socket object. ///

\b NOTE: Linux will set the send buffer to twice the value passed. /// @return zero on failure else the number of bytes of the TCP receive buffer window size if successful. - uint32 GetSendWindowSize() { - return GetWindowSize(SO_SNDBUF); - }; + uint32 GetSendWindowSize(); /// Set the TCP receive buffer window size for the current socket object. ///

\b NOTE: Linux will set the receive buffer to twice the value passed. /// @return zero on failure else the number of bytes of the TCP send buffer window size if successful. - uint32 SetReceiveWindowSize(uint32 nWindowSize) { - return SetWindowSize(SO_RCVBUF, nWindowSize); - }; + uint32 SetReceiveWindowSize(uint32 nWindowSize); /// Set the TCP send buffer window size for the current socket object. ///

\b NOTE: Linux will set the send buffer to twice the value passed. /// @return zero on failure else the number of bytes of the TCP send buffer window size if successful. - uint32 SetSendWindowSize(uint32 nWindowSize) { - return SetWindowSize(SO_SNDBUF, nWindowSize); - }; + uint32 SetSendWindowSize(uint32 nWindowSize); /// Disable the Nagle algorithm (Set TCP_NODELAY to true) /// @return false if failed to set socket option otherwise return true; @@ -509,6 +641,14 @@ class EXPORT CSimpleSocket { /// @return false if failed to set socket option otherwise return true; bool EnableNagleAlgoritm(); + /// retrieve IPv4 address via getaddrinfo() as uint32 in HostByteOrder + /// as static member + /// @return 0 if failed + static uint32 GetIPv4AddrInfoStatic( const char *pAddr, CSocketType nSocketType = SocketTypeTcp ); + + /// retrieve IPv4 address via getaddrinfo() as uint32 in HostByteOrder + /// @return 0 if failed + uint32 GetIPv4AddrInfo( const char *pAddr ); protected: /// Set internal socket error to that specified error @@ -519,18 +659,16 @@ class EXPORT CSimpleSocket { /// Set object socket handle to that specified as parameter /// @param socket value of socket descriptor - void SetSocketHandle(SOCKET socket) { - m_socket = socket; - }; + void SetSocketHandle(SOCKET socket, bool bIsServerSide = true ); private: /// Generic function used to get the send/receive window size /// @return zero on failure else the number of bytes of the TCP window size if successful. - uint32 GetWindowSize(uint32 nOptionName); + CLSOCKET_NO_EXPORT uint32 GetWindowSize(uint32 nOptionName); /// Generic function used to set the send/receive window size /// @return zero on failure else the number of bytes of the TCP window size if successful. - uint32 SetWindowSize(uint32 nOptionName, uint32 nWindowSize); + CLSOCKET_NO_EXPORT uint32 SetWindowSize(uint32 nOptionName, uint32 nWindowSize); /// Attempts to send at most nNumItem blocks described by sendVector @@ -542,13 +680,29 @@ class EXPORT CSimpleSocket { /// @return number of bytes actually sent, return of zero means the /// connection has been shutdown on the other side, and a return of -1 /// means that an error has occurred. - int32 Writev(const struct iovec *pVector, size_t nCount); + CLSOCKET_NO_EXPORT int32 Writev(const struct iovec *pVector, size_t nCount); /// Flush the socket descriptor owned by the object. /// @return true data was successfully sent, else return false; - bool Flush(); + CLSOCKET_NO_EXPORT bool Flush(); + + CLSOCKET_NO_EXPORT CSimpleSocket *operator=(CSimpleSocket &socket); + + static bool GetAddrInfoStatic(const char *pAddr, uint16 nPort, struct in_addr * pOutIpAddress, CSocketType nSocketType = SocketTypeTcp ); + + CLSOCKET_NO_EXPORT bool GetAddrInfo(const char *pAddr, uint16 nPort, struct in_addr * pOutIpAddress ); + + /// Utility function used to create a TCP connection, called from Open(). + /// @return true if successful connection made, otherwise false. + CLSOCKET_NO_EXPORT bool ConnectTCP(const char *pAddr, uint16 nPort); + + /// Utility function used to create a UDP connection, called from Open(). + /// @return true if successful connection made, otherwise false. + CLSOCKET_NO_EXPORT bool ConnectUDP(const char *pAddr, uint16 nPort); - CSimpleSocket *operator=(CSimpleSocket &socket); + /// Utility function used to create a RAW connection, called from Open(). + /// @return true if successful connection made, otherwise false. + CLSOCKET_NO_EXPORT bool ConnectRAW(const char *pAddr, uint16 nPort); protected: SOCKET m_socket; /// socket handle @@ -562,6 +716,10 @@ class EXPORT CSimpleSocket { uint32 m_nFlags; /// socket flags bool m_bIsBlocking; /// is socket blocking bool m_bIsMulticast; /// is the UDP socket multicast; + bool m_bIsServerSide; /// is this the server? => m_stServerSockaddr == localAddr() + /// and m_stClientSockaddr == peerAddr() + /// else: m_stClientSockaddr == localAddr() + bool m_bPeerHasClosed; /// is socket closed from peer? struct timeval m_stConnectTimeout; /// connection timeout struct timeval m_stRecvTimeout; /// receive timeout struct timeval m_stSendTimeout; /// send timeout diff --git a/src/StatTimer.h b/src/StatTimer.h index c8097b1..9f30e04 100644 --- a/src/StatTimer.h +++ b/src/StatTimer.h @@ -68,7 +68,7 @@ /// Class to abstract socket communications in a cross platform manner. /// This class is designed -class EXPORT CStatTimer { +class CLSOCKET_API CStatTimer { public: CStatTimer() {