/* ** ClanLib SDK ** Copyright (c) 1997-2005 The ClanLib Team ** ** This software is provided 'as-is', without any express or implied ** warranty. In no event will the authors be held liable for any damages ** arising from the use of this software. ** ** Permission is granted to anyone to use this software for any purpose, ** including commercial applications, and to alter it and redistribute it ** freely, subject to the following restrictions: ** ** 1. The origin of this software must not be misrepresented; you must not ** claim that you wrote the original software. If you use this software ** in a product, an acknowledgment in the product documentation would be ** appreciated but is not required. ** 2. Altered source versions must be plainly marked as such, and must not be ** misrepresented as being the original software. ** 3. This notice may not be removed or altered from any source distribution. ** ** Note: Some of the libraries ClanLib may link to may have additional ** requirements or restrictions. ** ** File Author(s): ** ** Magnus Norddahl ** (if your name is missing here, please add it) */ #ifdef _MSC_VER #pragma warning (disable:4786) #define snprintf _snprintf #endif #ifndef WIN32 #include #endif #include "socket_generic.h" #include #include "API/Core/System/clanstring.h" ///////////////////////////////////////////////////////////////////////////// // CL_Socket_KeepAliveHelper operations: void CL_Socket_KeepAliveHelper::keep_alive() { // performance note: it would be much more efficient if we maintained a global // list of all sockets instanciated, and then do one select for them all. // // I'm too lazy to do that - at least until it becomes a real performance problem. // -- mbn 2. marts 2001 if (impl->valid == false) return; fd_set rfds, wfds, efds; FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&efds); FD_SET(impl->sock, &rfds); FD_SET(impl->sock, &wfds); FD_SET(impl->sock, &efds); timeval tv; tv.tv_sec = 0; tv.tv_usec = 0; int nread = 0; char buf = 0; int result = select(impl->sock+1, &rfds, &wfds, &efds, &tv); if (result > 0) { if (FD_ISSET(impl->sock, &rfds)) { // Peek and see if there is anydata on the socket, if not // the remote side has disconnected. nread = recv(impl->sock, &buf, 1, MSG_PEEK); if(nread == 0) { sig_disconnected(); impl->disconnect(); return; } else sig_read_triggered(); } if (FD_ISSET(impl->sock, &wfds)) sig_write_triggered(); if (FD_ISSET(impl->sock, &efds)) sig_exception_triggered(); } } ///////////////////////////////////////////////////////////////////////////// // CL_Socket_Generic static operations: CL_IPAddress CL_Socket_Generic::create_ip_address(const sockaddr_in &addr_in) { char port[32]; memset(port, 0, 32); snprintf(port,6,"%d",ntohs(addr_in.sin_port)); char *addr = inet_ntoa(addr_in.sin_addr); return CL_IPAddress((std::string)addr, (std::string)port); } ///////////////////////////////////////////////////////////////////////////// // CL_Socket_Generic construction: CL_Socket_Generic::CL_Socket_Generic() : sock(-1), read(NULL), write(NULL), exception(NULL), keep_alive_helper(0), ref_count(0), valid(true) { } CL_Socket_Generic::~CL_Socket_Generic() { delete read; delete write; delete exception; disconnect(); delete keep_alive_helper; } ///////////////////////////////////////////////////////////////////////////// // CL_Socket_Generic attributes: ///////////////////////////////////////////////////////////////////////////// // CL_Socket_Generic operations: void CL_Socket_Generic::add_ref() { ref_count++; } void CL_Socket_Generic::release_ref() { ref_count--; if (ref_count == 0) delete this; } std::string CL_Socket_Generic::get_error_message() { #ifdef WIN32 int err = WSAGetLastError(); switch (err) { case WSAEACCES: return "Permission denied"; case WSAEADDRINUSE: return "Address already in use"; case WSAEADDRNOTAVAIL: return "Cannot assign requested address"; case WSAEAFNOSUPPORT: return "Address family not supported by protocol family"; case WSAEALREADY: return "Operation already in progress"; case WSAECONNABORTED: return "Software caused connection abort"; case WSAECONNREFUSED: return "Connection refused"; case WSAECONNRESET: return "Connection reset by peer"; case WSAEDESTADDRREQ: return "Destination address required"; case WSAEFAULT: return "Bad address"; case WSAEHOSTDOWN: return "Host is down"; case WSAEINPROGRESS: return "Operation now in progress"; case WSAEINTR: return "Interrupted function call"; case WSAEINVAL: return "Invalid argument"; case WSAEISCONN: return "Socket is already connected"; case WSAEMFILE: return "Too many open files"; case WSAEMSGSIZE: return "Message too long"; case WSAENETDOWN: return "Network is down"; case WSAENETRESET: return "Network dropped connection on reset"; case WSAENETUNREACH: return "Network is unreachable"; case WSAENOBUFS: return "No buffer space available"; case WSAENOPROTOOPT: return "Bad protocol option"; case WSAENOTCONN: return "Socket is not connected"; case WSAENOTSOCK: return "Socket operation on nonsocket"; case WSAEOPNOTSUPP: return "Operation not supported"; case WSAEPFNOSUPPORT: return "Protocol family not supported"; case WSAEPROCLIM: return "Too many processes"; case WSAEPROTONOSUPPORT: return "Protocol not supported"; case WSAEPROTOTYPE: return "Protocol wrong type for socket"; case WSAESHUTDOWN: return "Cannot send after socket shutdown"; case WSAESOCKTNOSUPPORT: return "Socket type not supported"; case WSAETIMEDOUT: return "Connection timed out"; // case WSATYPE_NOT_FOUND: // return "Class type not found"; case WSAEWOULDBLOCK: return "Resource temporarily unavailable"; case WSAHOST_NOT_FOUND: return "Host not found"; // case WSA_INVALID_HANDLE: // return "Specified event object handle is invalid"; // case WSA_INVALID_PARAMETER: // return "One or more parameters are invalid"; // case WSAINVALIDPROCTABLE: // return "Invalid procedure table from service provider"; // case WSAINVALIDPROVIDER: // return "Invalid service provider version number"; // case WSA_IO_INCOMPLETE: // return "Overlapped I/O event object not in signaled state"; // case WSA_IO_PENDING: // return "Overlapped operations will complete later"; // case WSA_NOT_ENOUGH_MEMORY: // return "Insufficient memory available"; case WSANOTINITIALISED: return "Successful WSAStartup not yet performed"; case WSANO_DATA: return "Valid name, no data record of requested type"; case WSANO_RECOVERY: return "This is a nonrecoverable error"; // case WSAPROVIDERFAILEDINIT: // return "Unable to initialize a service provider"; // case WSASYSCALLFAILURE: // return "System call failure"; case WSASYSNOTREADY: return "Network subsystem is unavailable"; case WSATRY_AGAIN: return "Nonauthoritative host not found"; case WSAVERNOTSUPPORTED: return "Winsock.dll version out of range"; case WSAEDISCON: return "Graceful shutdown in progress"; // case WSA_OPERATION_ABORTED: // return "Overlapped operation aborted"; default: return CL_String::format("Unknown socket error %1", err); } #else return std::string(strerror(errno)); #endif } ///////////////////////////////////////////////////////////////////////////// // CL_Socket_Generic overridables: CL_EventTrigger_Socket *CL_Socket_Generic::create_read_trigger() { return new CL_EventTrigger_Socket(this, CL_EventTrigger_Socket::read); } CL_EventTrigger_Socket *CL_Socket_Generic::create_write_trigger() { return new CL_EventTrigger_Socket(this, CL_EventTrigger_Socket::write); } CL_EventTrigger_Socket *CL_Socket_Generic::create_exception_trigger() { return new CL_EventTrigger_Socket(this, CL_EventTrigger_Socket::exception); } void CL_Socket_Generic::disconnect() { if(sock != -1) { #ifdef WIN32 // Must stop and wait for all send data to be sent (thanks Microsoft): HANDLE event_object = WSACreateEvent(); if (event_object != WSA_INVALID_EVENT) { if (WSAEventSelect(sock, event_object, FD_CLOSE) != SOCKET_ERROR) WSAWaitForMultipleEvents(1, &event_object, FALSE, 60*1000, FALSE); WSACloseEvent(event_object); } closesocket(sock); #else close(sock); #endif } sock = -1; valid = false; } ///////////////////////////////////////////////////////////////////////////// // CL_Socket_Generic implementation: