📄 socket.cpp
字号:
// Copyright (C) 1999-2005 Open Source Telecom Corporation.//// This program is free software; you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation; either version 2 of the License, or// (at your option) any later version.// // This program is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.// // As a special exception, you may use this file as part of a free software// library without restriction. Specifically, if other files instantiate// templates or use macros or inline functions from this file, or you compile// this file and link it with other files to produce an executable, this// file does not by itself cause the resulting executable to be covered by// the GNU General Public License. This exception does not however // invalidate any other reasons why the executable file might be covered by// the GNU General Public License. //// This exception applies only to the code released under the name GNU// Common C++. If you copy code from other releases into a copy of GNU// Common C++, as the General Public License permits, the exception does// not apply to the code that you add in this way. To avoid misleading// anyone as to the status of such modified files, you must delete// this exception notice from them.//// If you write modifications of your own for GNU Common C++, it is your choice// whether to permit this exception to apply to your modifications.// If you do not wish that, delete this exception notice.//#include <cc++/config.h>#include <cc++/export.h>#include <cc++/exception.h>#include <cc++/thread.h>#include <cc++/address.h>#include <cc++/socket.h>#include "private.h"#include "nat.h"#include <fcntl.h>#include <cerrno>#include <cstdlib>#include <cstdarg>#include <cstdio>#ifndef WIN32#include <netinet/tcp.h>#endif#ifdef WIN32#include <io.h>#define socket_errno WSAGetLastError()#endif#ifndef WIN32#include <sys/ioctl.h>#ifdef HAVE_NET_IP6_H#include <netinet/ip6.h>#endif#if defined(__hpux) && defined(_XOPEN_SOURCE_EXTENDED)#undef _XOPEN_SOURCE_EXTENDED#endif#ifdef HAVE_NETINET_IN_H#include <netinet/in.h>#endif#if defined(__hpux)#define _XOPEN_SOURCE_EXTENDED#endif#ifdef HAVE_NET_IF_H#include <net/if.h>#endif#endif#ifndef WIN32#define socket_errno errno# ifndef O_NONBLOCK# define O_NONBLOCK O_NDELAY# endif# ifdef IPPROTO_IP# ifndef SOL_IP# define SOL_IP IPPROTO_IP# endif // !SOL_IP# endif // IPPROTO_IP#endif // !WIN32#ifndef INADDR_LOOPBACK#define INADDR_LOOPBACK (unsigned long)0x7f000001#endif#ifdef CCXX_NAMESPACESnamespace ost {using namespace std;#endif#if defined(WIN32) && !defined(__MINGW32__)static SOCKET dupSocket(SOCKET so,enum Socket::State state){ if (state == Socket::STREAM) return dup((int)so); HANDLE pidHandle = GetCurrentProcess(); HANDLE dupHandle; if(DuplicateHandle(pidHandle, reinterpret_cast<HANDLE>(so), pidHandle, &dupHandle, 0, FALSE, DUPLICATE_SAME_ACCESS)) return reinterpret_cast<SOCKET>(dupHandle); return INVALID_SOCKET;}# define DUP_SOCK(s,state) dupSocket(s,state)#else# define DUP_SOCK(s,state) dup(s)#endif Mutex Socket::mutex;bool Socket::check(Family fam){ SOCKET so = INVALID_SOCKET; switch(fam) { case IPV4: so = socket(fam, SOCK_DGRAM, IPPROTO_UDP); break;#ifdef CCXX_IPV6 case IPV6: so = socket(fam, SOCK_DGRAM, IPPROTO_UDP); break;#endif } if(so == INVALID_SOCKET) return false;#ifdef WIN32 closesocket(so);#else close(so);#endif return true;}Socket::Socket(){ setSocket();}Socket::Socket(int domain, int type, int protocol){ setSocket(); so = socket(domain, type, protocol); if(so == INVALID_SOCKET) { error(errCreateFailed,"Could not create socket",socket_errno); return; }#ifdef SO_NOSIGPIPE int opt = 1; setsockopt(so, SOL_SOCKET, SO_NOSIGPIPE, (char *)&opt, sizeof(opt));#endif state = AVAILABLE;}Socket::Socket(SOCKET fd){ setSocket(); if (fd == INVALID_SOCKET) { error(errCreateFailed,"Invalid socket handle passed",0); return; } so = fd; state = AVAILABLE;}Socket::Socket(const Socket &orig){ setSocket(); so = DUP_SOCK(orig.so,orig.state); if(so == INVALID_SOCKET) error(errCopyFailed,"Could not duplicate socket handle",socket_errno); state = orig.state;}Socket::~Socket(){ endSocket();}void Socket::setSocket(void){ flags.thrown = false; flags.broadcast = false; flags.route = true; flags.keepalive = false; flags.loopback = true; flags.multicast = false; flags.linger = false; flags.ttl = 1; errid = errSuccess; errstr = NULL; syserr = 0; state = INITIAL; so = INVALID_SOCKET;}Socket::Error Socket::sendLimit(int limit){#ifdef SO_SNDLOWAT if(setsockopt(so, SOL_SOCKET, SO_SNDLOWAT, (char *)&limit, sizeof(limit))) return errInvalidValue; return errSuccess; #else return errServiceUnavailable;#endif}Socket::Error Socket::receiveLimit(int limit){#ifdef SO_RCVLOWAT if(setsockopt(so, SOL_SOCKET, SO_RCVLOWAT, (char *)&limit, sizeof(limit))) return errInvalidValue; return errSuccess;#else return errServiceUnavailable;#endif}Socket::Error Socket::sendTimeout(timeout_t to){#ifdef SO_SNDTIMEO struct timeval tv; tv.tv_sec = to / 1000; tv.tv_usec = (to % 1000) * 1000; if(setsockopt(so, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof(tv))) return errInvalidValue; return errSuccess;#else return errServiceUnavailable;#endif}Socket::Error Socket::receiveTimeout(timeout_t to){#ifdef SO_RCVTIMEO struct timeval tv; tv.tv_sec = to / 1000; tv.tv_usec = (to % 1000) * 1000; if(setsockopt(so, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv))) return errInvalidValue; return errSuccess;#else return errServiceUnavailable;#endif} Socket::Error Socket::sendBuffer(unsigned bufsize){#ifdef SO_SNDBUF if(setsockopt(so, SOL_SOCKET, SO_SNDBUF, (char *)&bufsize, sizeof(bufsize))) return errInvalidValue; return errSuccess;#else return errServiceUnavailable;#endif} Socket::Error Socket::bufferSize(unsigned bufsize){ Error err = receiveBuffer(bufsize); if(err == errSuccess) err = sendBuffer(bufsize); return err;}Socket::Error Socket::receiveBuffer(unsigned bufsize){#ifdef SO_RCVBUF if(setsockopt(so, SOL_SOCKET, SO_RCVBUF, (char *)&bufsize, sizeof(bufsize))) return errInvalidValue; return errSuccess;#else return errServiceUnavailable; #endif}ssize_t Socket::readLine(char *str, size_t request, timeout_t timeout){ bool crlf = false; bool nl = false; size_t nleft = request - 1; // leave also space for terminator int nstat,c; if(request < 1) return 0; str[0] = 0; while(nleft && !nl) { if(timeout) { if(!isPending(pendingInput, timeout)) { error(errTimeout,"Read timeout", 0); return -1; } } nstat = ::recv(so, str, _IOLEN64 nleft, MSG_PEEK); if(nstat <= 0) { error(errInput,"Could not read from socket", socket_errno); return -1; } // FIXME: if unique char in buffer is '\r' return "\r" // if buffer end in \r try to read another char? // and if timeout ?? // remember last \r for(c=0; c < nstat; ++c) { if(str[c] == '\n') { if (c > 0 && str[c-1] == '\r') crlf = true; ++c; nl = true; break; } } nstat = ::recv(so, str, c, 0); // TODO: correct ??? if(nstat < 0) break; // adjust ending \r\n in \n if(crlf) { --nstat; str[nstat - 1] = '\n'; } str += nstat; nleft -= nstat; } *str = 0; return (ssize_t)(request - nleft - 1);}ssize_t Socket::readData(void *Target, size_t Size, char Separator, timeout_t timeout){ if ((Separator == 0x0D) || (Separator == 0x0A)) return (readLine ((char *) Target, Size, timeout)); if (Size < 1) return (0); ssize_t nstat; if (Separator == 0) // Flat-out read for a number of bytes. { if (timeout) if (!isPending (pendingInput, timeout)) { error(errTimeout); return (-1); } nstat =::recv (so, (char *)Target, _IOLEN64 Size, 0); if (nstat < 0) { error (errInput); return (-1); } return (nstat); } ///////////////////////////////////////////////////////////// // Otherwise, we have a special char separator to use ///////////////////////////////////////////////////////////// bool found = false; size_t nleft = Size; int c; char *str = (char *) Target; memset (str, 0, Size); while (nleft && !found) { if (timeout) if (!isPending (pendingInput, timeout)) { error(errTimeout); return (-1); } nstat =::recv (so, str, _IOLEN64 nleft, MSG_PEEK); if (nstat <= 0) { error (errInput); return (-1); } for (c = 0; (c < nstat) && !found; ++c) if (str[c] == Separator) found = true; memset (str, 0, nleft); nstat =::recv (so, str, c, 0); if (nstat < 0) break; str += nstat; nleft -= nstat; } return (ssize_t)(Size - (ssize_t) nleft);}ssize_t Socket::writeData(const void *Source, size_t Size, timeout_t timeout){ if (Size < 1) return (0); ssize_t nstat; const char *Slide = (const char *) Source; while (true) { if (timeout) if (!isPending (pendingOutput, timeout)) { error(errOutput); return (-1); } nstat =::send (so, Slide, _IOLEN64 Size, 0); if (nstat <= 0) { error(errOutput); return (-1); } Size -= nstat; Slide += Size; if (Size <= 0) break; } return (nstat);}bool Socket::isConnected(void) const{ return (Socket::state == CONNECTED) ? true : false;}bool Socket::isActive(void) const{ return (state != INITIAL) ? true : false;}bool Socket::operator!() const{ return (Socket::state == INITIAL) ? true : false;}void Socket::endSocket(void){ if(Socket::state == STREAM) { state = INITIAL;#ifdef WIN32 if(so != (UINT)-1) { SOCKET sosave = so; so = INVALID_SOCKET; closesocket((int)sosave); }#else if(so > -1) { SOCKET sosave = so; so = INVALID_SOCKET; close(sosave); }#endif return; } state = INITIAL; if(so == INVALID_SOCKET) return;#ifdef SO_LINGER struct linger linger; if(flags.linger) { linger.l_onoff = 1; linger.l_linger = 60; } else linger.l_onoff = linger.l_linger = 0; setsockopt(so, SOL_SOCKET, SO_LINGER, (char *)&linger, (socklen_t)sizeof(linger));#endif// shutdown(so, 2);#ifdef WIN32 closesocket(so);#else close(so);#endif so = INVALID_SOCKET;}#ifdef WIN32Socket::Error Socket::connectError(void){ char* str = "Could not connect to remote host"; switch(WSAGetLastError()) { case WSAENETDOWN: return error(errResourceFailure,str,socket_errno); case WSAEINPROGRESS: return error(errConnectBusy,str,socket_errno); case WSAEADDRNOTAVAIL: return error(errConnectInvalid,str,socket_errno); case WSAECONNREFUSED: return error(errConnectRefused,str,socket_errno); case WSAENETUNREACH: return error(errConnectNoRoute,str,socket_errno); default: return error(errConnectFailed,str,socket_errno); }}#elseSocket::Error Socket::connectError(void){ char* str = "Could not connect to remote host"; switch(errno) {#ifdef EHOSTUNREACH case EHOSTUNREACH: return error(errConnectNoRoute,str,socket_errno);#endif#ifdef ENETUNREACH case ENETUNREACH: return error(errConnectNoRoute,str,socket_errno);#endif case EINPROGRESS: return error(errConnectBusy,str,socket_errno);#ifdef EADDRNOTAVAIL case EADDRNOTAVAIL: return error(errConnectInvalid,str,socket_errno);#endif case ECONNREFUSED: return error(errConnectRefused,str,socket_errno); case ETIMEDOUT: return error(errConnectTimeout,str,socket_errno); default: return error(errConnectFailed,str,socket_errno); }}#endifSocket::Error Socket::error(Error err, char *errs, long systemError) const{ errid = err; errstr = errs; syserr = systemError; if(!err) return err; if(flags.thrown) return err; // prevents recursive throws flags.thrown = true;#ifdef CCXX_EXCEPTIONS switch(Thread::getException()) { case Thread::throwObject: throw((Socket *)this);#ifdef COMMON_STD_EXCEPTION case Thread::throwException: { if(!errs) errs = ""; throw SockException(String(errs), err, systemError); }#endif case Thread::throwNothing: break; }#endif return err;}const char *Socket::getSystemErrorString(void) const{#ifdef CCXX_EXCEPTIONS SockException e(errstr, errid, syserr); return e.getSystemErrorString();#else return NULL;#endif}Socket::Error Socket::setBroadcast(bool enable){ int opt = (enable ? 1 : 0); if(setsockopt(so, SOL_SOCKET, SO_BROADCAST, (char *)&opt, (socklen_t)sizeof(opt))) return error(errBroadcastDenied,"Could not set socket broadcast option",socket_errno); flags.broadcast = enable; return errSuccess;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -