📄 gsocket.cpp
字号:
/* Copyright (C) 2006, Mike Gashler This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. see http://www.gnu.org/copyleft/lesser.html*/#include "GSocket.h"#include <time.h>#include "GSpinLock.h"#include "GArray.h"#include "GMacros.h"#include "GThread.h"#include "GString.h"#include "GPointerQueue.h"#include "GBits.h"#include <stdarg.h>#include <wchar.h>#ifdef WIN32#include "GWindows.h"#include <Ws2tcpip.h>#else // WIN32#include <unistd.h>#include <time.h>#include <string.h>#include <sys/socket.h>#include <arpa/inet.h>#include <netdb.h>#include <stdlib.h>#include <sys/ioctl.h>#include <errno.h>#define SOCKET_ERROR -1#endif // !WIN32wchar_t* g_wszErrorMessage = NULL;void ThrowError(const wchar_t* wszFormat, ...){ // Measure the required buffer size--todo: there might be a function like "vscwprintf" that does this, but I couldn't find it so I kludged my own int nSize = 0; { va_list args; const wchar_t* wsz = wszFormat; va_start(args, wszFormat); { while(*wsz != L'\0') { if(*wsz == L'%') { wsz++; switch(*wsz) { case L'c': nSize += 2; va_arg(args, int/*wchar_t*/); break; case L's': nSize += wcslen(va_arg(args, wchar_t*)); break; case L'd': nSize += 10; va_arg(args, int); break; case L'l': nSize += 20; va_arg(args, double); break; case L'f': nSize += 20; va_arg(args, double/*float*/); break; default: nSize += 20; // take a guess break; } } wsz++; nSize++; } } va_end (args); } nSize++; // Allocate the buffer delete(g_wszErrorMessage); g_wszErrorMessage = new wchar_t[nSize + 1]; // Format the message { va_list args; va_start(args, wszFormat); {#ifdef _MBCS int res = vswprintf(g_wszErrorMessage, wszFormat, args);#else int res = vswprintf(g_wszErrorMessage, nSize, wszFormat, args);#endif if(res < 0) { GAssert(false, "Error formatting string"); } } va_end (args); } // Throw the error throw (const wchar_t*)g_wszErrorMessage;}const wchar_t* gsocket_GetLastError(){ const wchar_t* wszMsg = NULL;#ifdef WIN32 int n = WSAGetLastError(); switch(n) { case WSAECONNRESET: wszMsg = L"An incoming connection was indicated, but was subsequently terminated by the remote peer prior to accepting the call."; break; case WSAEFAULT: wszMsg = L"The addrlen parameter is too small or addr is not a valid part of the user address space."; break; case WSAEINTR: wszMsg = L"A blocking Windows Sockets 1.1 call was canceled through WSACancelBlockingCall."; break; case WSAEINVAL: wszMsg = L"The listen function was not invoked prior to accept."; break; case WSAEINPROGRESS: wszMsg = L"A blocking Windows Sockets 1.1 call is in progress, or the service provider is still processing a callback function."; break; case WSAEMFILE: wszMsg = L"The queue is nonempty upon entry to accept and there are no descriptors available."; break; case WSAENETDOWN: wszMsg = L"The network subsystem has failed."; break; case WSAENOBUFS: wszMsg = L"No buffer space is available."; break; case WSAENOTSOCK: wszMsg = L"The descriptor is not a socket."; break; case WSAEOPNOTSUPP: wszMsg = L"The referenced socket is not a type that supports connection-oriented service."; break; case WSAEWOULDBLOCK: wszMsg = L"The socket is marked as nonblocking and no connections are present to be accepted."; break; case WSANOTINITIALISED: wszMsg = L"A successful WSAStartup must occur before using this function."; break; case WSAEALREADY: wszMsg = L"A nonblocking connect call is in progress on the specified socket."; break; case WSAEADDRNOTAVAIL: wszMsg = L"The remote address is not a valid address (such as ADDR_ANY)."; break; case WSAEAFNOSUPPORT: wszMsg = L"Addresses in the specified family cannot be used with this socket."; break; case WSAECONNREFUSED: wszMsg = L"The attempt to connect was forcefully rejected."; break; case WSAEISCONN: wszMsg = L"The socket is already connected (connection-oriented sockets only)."; break; case WSAENETUNREACH: wszMsg = L"The network cannot be reached from this host at this time."; break; case WSAETIMEDOUT: wszMsg = L"Attempt to connect timed out without establishing a connection."; break; case WSASYSNOTREADY: wszMsg = L"network subsystem not ready for communication."; break; case WSAVERNOTSUPPORTED: wszMsg = L"The version of Windows Sockets support requested is not provided by this implementation."; break; case WSAEPROCLIM: wszMsg = L"Limit on the number of tasks supported has been reached."; break; case WSAEHOSTUNREACH: wszMsg = L"Host unreacheable"; break; case WSAENOTCONN: wszMsg = L"Not Connected"; break; case WSAECONNABORTED: wszMsg = L"Connection Aborted"; break; case 0x2740: wszMsg = L"Port already in use"; break; case WSAHOST_NOT_FOUND: wszMsg = L"Authoritative answer host not found."; break; case WSATRY_AGAIN: wszMsg = L"Nonauthoritative host not found, or server failure."; break; case WSANO_RECOVERY: wszMsg = L"A nonrecoverable error occurred."; break; case WSANO_DATA: wszMsg = L"Valid name, no data record of requested type."; break; default: wszMsg = L"An unrecognized socket error occurred"; break; }#else // WIN32 switch(errno) { case EBADF: wszMsg = L"not a valid socket descriptor."; break; case EINVAL: wszMsg = L"The socket is already bound to an address or addrlen is wrong."; break; case EACCES: wszMsg = L"Access permission is denied."; break; case ENOTSOCK: wszMsg = L"Argument is a descriptor for a file, not a socket."; break; case EROFS: wszMsg = L"The socket inode would reside on a read-only file system."; break; case EFAULT: wszMsg = L"the addr parameter points outside the user's accessible address space."; break; case ENAMETOOLONG: wszMsg = L"A component of a pathname exceeded {NAME_MAX} characters, or an entire path name exceeded {PATH_MAX} characters."; break; case ENOENT: wszMsg = L"The file or named socket does not exist."; break; case ENOMEM: wszMsg = L"Insufficient kernel memory was available."; break; case ENOTDIR: wszMsg = L"A component of the path prefix is not a directory."; break; case ELOOP: wszMsg = L"Too many symbolic links were encountered in resolving my_addr."; break; case EOPNOTSUPP: wszMsg = L"The referenced socket is not of type SOCK_STREAM."; break; case EWOULDBLOCK: wszMsg = L"The socket is marked non-blocking and no connections are present to be accepted."; break; case EMFILE: wszMsg = L"The per-process descriptor table is full."; break; case ENFILE: wszMsg = L"The system file table is full."; break; case EADDRNOTAVAIL: wszMsg = L"The specified address is not available on this machine."; break; case EAFNOSUPPORT: wszMsg = L"Addresses in the specified address family cannot be used with this socket."; break; case EISCONN: wszMsg = L"The socket is already connected."; break; case ETIMEDOUT: wszMsg = L"Connection establishment timed out without establishing a connection."; break; case ECONNREFUSED: wszMsg = L"The attempt to connect was forcefully rejected."; break; case ENETUNREACH: wszMsg = L"The network isn't reachable from this host."; break; case EADDRINUSE: wszMsg = L"The address is already in use."; break; case EINPROGRESS: wszMsg = L"The socket is non-blocking and the connection cannot be completed immediately. It is possible to select(2) for completion by selecting the socket for writing."; break; case EALREADY: wszMsg = L"The socket is non-blocking and a previous connection attempt has not yet been completed."; break; case HOST_NOT_FOUND: wszMsg = L"The specified host is unknown."; break; case NO_ADDRESS: wszMsg = L"The requested name is valid but does not have an IP address."; break; case NO_RECOVERY: wszMsg = L"A non-recoverable name server error occurred."; break; //case TRY_AGAIN: wszMsg = L"A temporary error occurred on an authoritative name server. Try again later."; break; default: wszMsg = L"An unrecognized socket error occurred"; break; }#endif // else WIN32 return wszMsg;}void gsocket_LogError(){ const wchar_t* wszErrorMessage = gsocket_GetLastError(); ThrowError(wszErrorMessage);}inline void SetSocketToBlockingMode(SOCKET s){ unsigned long ulMode = 0;#ifdef WIN32 if(ioctlsocket(s, FIONBIO, &ulMode) != 0)#else // WIN32 if(ioctl(s, FIONBIO, &ulMode) != 0)#endif // !WIN32 { gsocket_LogError(); }}inline void SetSocketToNonBlockingMode(SOCKET s){ unsigned long ulMode = 1;#ifdef WIN32 if(ioctlsocket(s, FIONBIO, &ulMode) != 0)#else // WIN32 if(ioctl(s, FIONBIO, &ulMode) != 0)#endif // WIN32 gsocket_LogError();}inline void CloseSocket(SOCKET s){#ifdef WIN32 closesocket(s);#else close(s);#endif // WIN32}#ifdef WIN32bool gsocket_InitWinSock(){ // Initializing Winsock WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested = MAKEWORD(1, 1); err = WSAStartup( wVersionRequested, &wsaData ); if ( err != 0 ) { GAssert(false, "Failed to find a usable WinSock DLL\n"); return false; } // Confirm that the WinSock DLL supports 2.2. // Note that if the DLL supports versions greater // than 2.2 in addition to 2.2, it will still return // 2.2 in wVersion since that is the version we // requested. if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 ) { int n1 = LOBYTE( wsaData.wVersion ); int n2 = HIBYTE( wsaData.wVersion ); GAssert(false, "Found a Winsock DLL, but it only supports an older version. It needs to support version 2.2"); WSACleanup(); return false; } return true;}#endif // WIN32// ------------------------------------------------------------------------------GSocketClientBase::GSocketClientBase(bool bUDP){ m_hListenThread = BAD_HANDLE; m_s = INVALID_SOCKET; m_bKeepListening = true; m_bUDP = bUDP;#ifdef WIN32 if(!gsocket_InitWinSock()) throw "Error initializing WinSock";#endif // WIN32}GSocketClientBase::~GSocketClientBase(){ Disconnect();}void GSocketClientBase::JoinListenThread(){ m_bKeepListening = false; time_t tStart; time_t tNow; time(&tStart); while(m_hListenThread != BAD_HANDLE) { GThread::sleep(0); time(&tNow); if(tNow - tStart > 4) { GAssert(false, "Error, took too long for the listen thread to exit"); break; } }}void GSocketClientBase::JoinListenThread(int nConnectionNumber){ GAssert(false, "Error, not implemented yet");}void GSocketClientBase::Listen(){ char szReceiveBuff[520]; SOCKET s = m_s; // Mark the socket as blocking so we can call "recv" which is a blocking operation SetSocketToBlockingMode(s); // Start receiving messages unsigned long dwBytesReadyToRead; int nBytesRead = 0; while(m_bKeepListening) { // See how much data is ready to be received#ifdef WIN32 GWindows::YieldToWindows(); // This is necessary because incoming packets go through the Windows message pump if(ioctlsocket(s, FIONREAD, &dwBytesReadyToRead) != 0) gsocket_LogError();#else // WIN32 if(ioctl(s, FIONREAD, &dwBytesReadyToRead) != 0) gsocket_LogError();#endif // !WIN32 if(dwBytesReadyToRead > 0) { nBytesRead = recv(s, szReceiveBuff, 512, 0); // read from the queue (This blocks until there is some to read or connection is closed, but we already know there is something to read) if(nBytesRead > 0) // recv reads in as much data as is currently available up to the size of the buffer Receive((unsigned char*)szReceiveBuff, nBytesRead); else break; // The socket has been closed } else { // There's nothing to receive, so let's sleep for a while GThread::sleep(50); } }#ifdef WIN32 if(nBytesRead == SOCKET_ERROR) { int n = WSAGetLastError(); switch(n) { case WSAECONNABORTED: break; case WSAECONNRESET: break; default: gsocket_LogError(); break; } }#endif // WIN32 OnCloseConnection(); shutdown(m_s, 2); CloseSocket(m_s); m_s = INVALID_SOCKET; m_hListenThread = BAD_HANDLE;}unsigned int ListenThread(void* pData){ ((GSocketClientBase*)pData)->Listen(); return 0;}/*virtual*/ void GSocketClientBase::OnCloseConnection(){}bool GSocketClientBase::IsThisAnIPAddress(const char* szHost){ int n; for(n = 0; szHost[n] != '.' && szHost[n] != '\0'; n++) { if(szHost[n] < '0' || szHost[n] > '9') return false; } if(szHost[n] == '.') { for(n++; szHost[n] != '.' && szHost[n] != '\0'; n++) { if(szHost[n] < '0' || szHost[n] > '9') return false; } } return true;}// This is for parsing a URL//staticvoid GSocketClientBase::ParseURL(const char* szUrl, int* pnHostIndex, int* pnPortIndex, int* pnPathIndex, int* pnParamsIndex){ // Find the host int nHost = 0; int i; for(i = 0; szUrl[i] != ':' && szUrl[i] != '?' && szUrl[i] != '\0'; i++) { } if(strncmp(&szUrl[i], "://", 3) == 0) nHost = i + 3; // Find the port int nPort = -1; for(i = nHost; szUrl[i] != ':' && szUrl[i] != '?' && szUrl[i] != '\0'; i++) { } if(szUrl[i] == ':') nPort = i; // Find the path int nPath; for(nPath = MAX(nHost, nPort); szUrl[nPath] != '/' && szUrl[nPath] != '?' && szUrl[nPath] != '\0'; nPath++) { } if(nPort < 0) nPort = nPath; // Find the params if(pnParamsIndex) { int nParams; for(nParams = nPath; szUrl[nParams] != '?' && szUrl[nParams] != '\0'; nParams++) { } *pnParamsIndex = nParams; } // Set the return values if(pnHostIndex) *pnHostIndex = nHost; if(pnPortIndex) *pnPortIndex = nPort; if(pnPathIndex) *pnPathIndex = nPath;}//staticint GSocketClientBase::ParseUrlParams(const char* szParams, int nMaxParams, char** pNames, int* pNameLengths, char** pValues, int* pValueLengths){ if(*szParams == '?') szParams++; int nParams = 0; while(true) { if(*szParams == '\0' || nParams >= nMaxParams) return nParams; pNames[nParams] = (char*)szParams; pNameLengths[nParams] = 0; while(*szParams != '\0' && *szParams != '=' && *szParams != '&') { szParams++; pNameLengths[nParams]++; } if(*szParams == '=') szParams++; pValues[nParams] = (char*)szParams; pValueLengths[nParams] = 0; while(*szParams != '\0' && *szParams != '&') { szParams++; pValueLengths[nParams]++; } if(*szParams == '&') szParams++; nParams++; }}/*in_addr GSocketClientBase::StringToAddr(const char* szURL){ // Extract the host and port from the URL GTEMPBUF(szHost, strlen(szURL)); ParseURL(szURL, NULL, szHost, NULL, NULL, NULL); // Determine if it is a friendly-URL or an IP address if(IsThisAnIPAddress(szHost)) { in_addr iaTmp;#ifdef WIN32 iaTmp.S_un.S_addr = inet_addr(szHost);#else // WIN32 iaTmp.s_addr = inet_addr(szHost);#endif // else WIN32 return iaTmp; } else { struct hostent* psh = gethostbyname(szHost); if(!psh) { gsocket_LogError(); in_addr iaTmp;#ifdef WIN32 iaTmp.S_un.S_addr = NULL;#else // WIN32 iaTmp.s_addr = 0;#endif // else WIN32 return iaTmp; } return *(in_addr*)psh->h_addr_list[0]; }}*/bool GSocketClientBase::Connect(const char* szHost, unsigned short nPort){ // *** If you use VisualStudio 6.0 and you get an error that // says 'hints' uses undefined struct 'addrinfo' on the next // code line then you need to get the Feb 2003 update of the // Platform SDK. (This is the last version that supports VS6). struct addrinfo hints, *res, *res0; int error; memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; char szPort[32]; itoa(nPort, szPort, 10); error = getaddrinfo(szHost, szPort, &hints, &res0); if (error) { GAssert(false, gai_strerror(error)); gsocket_LogError(); } m_s = INVALID_SOCKET; for(res = res0; res; res = res->ai_next) {//printf("Attempting to connect to %d.%d.%d.%d on port %d... ", ((unsigned char*)&res->ai_addr->sa_data)[2], ((unsigned char*)&res->ai_addr->sa_data)[3], ((unsigned char*)&res->ai_addr->sa_data)[4], ((unsigned char*)&res->ai_addr->sa_data)[5], nPort);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -