etisserant@355: /* etisserant@355: Socket.cpp etisserant@355: etisserant@355: Copyright (C) 2002-2004 René Nyffenegger etisserant@355: etisserant@355: This source code is provided 'as-is', without any express or implied etisserant@355: warranty. In no event will the author be held liable for any damages etisserant@355: arising from the use of this software. etisserant@355: etisserant@355: Permission is granted to anyone to use this software for any purpose, etisserant@355: including commercial applications, and to alter it and redistribute it etisserant@355: freely, subject to the following restrictions: etisserant@355: etisserant@355: 1. The origin of this source code must not be misrepresented; you must not etisserant@355: claim that you wrote the original source code. If you use this source code etisserant@355: in a product, an acknowledgment in the product documentation would be etisserant@355: appreciated but is not required. etisserant@355: etisserant@355: 2. Altered source versions must be plainly marked as such, and must not be etisserant@355: misrepresented as being the original source code. etisserant@355: etisserant@355: 3. This notice may not be removed or altered from any source distribution. etisserant@355: etisserant@355: René Nyffenegger rene.nyffenegger@adp-gmbh.ch etisserant@355: */ etisserant@355: etisserant@355: etisserant@355: #include "Socket.h" etisserant@355: #include etisserant@355: etisserant@355: using namespace std; etisserant@355: etisserant@355: int Socket::nofSockets_= 0; etisserant@355: etisserant@355: void Socket::Start() { etisserant@355: if (!nofSockets_) { etisserant@355: WSADATA info; etisserant@355: if (WSAStartup(MAKEWORD(2,0), &info)) { etisserant@355: throw "Could not start WSA"; etisserant@355: } etisserant@355: } etisserant@355: ++nofSockets_; etisserant@355: } etisserant@355: etisserant@355: void Socket::End() { etisserant@355: WSACleanup(); etisserant@355: } etisserant@355: etisserant@355: Socket::Socket() : s_(0) { etisserant@355: Start(); etisserant@355: // UDP: use SOCK_DGRAM instead of SOCK_STREAM etisserant@355: s_ = socket(AF_INET,SOCK_STREAM,0); etisserant@355: etisserant@355: if (s_ == INVALID_SOCKET) { etisserant@355: throw "INVALID_SOCKET"; etisserant@355: } etisserant@355: etisserant@355: refCounter_ = new int(1); etisserant@355: } etisserant@355: etisserant@355: Socket::Socket(SOCKET s) : s_(s) { etisserant@355: Start(); etisserant@355: refCounter_ = new int(1); etisserant@355: }; etisserant@355: etisserant@355: Socket::~Socket() { etisserant@355: if (! --(*refCounter_)) { etisserant@355: Close(); etisserant@355: delete refCounter_; etisserant@355: } etisserant@355: etisserant@355: --nofSockets_; etisserant@355: if (!nofSockets_) End(); etisserant@355: } etisserant@355: etisserant@355: Socket::Socket(const Socket& o) { etisserant@355: refCounter_=o.refCounter_; etisserant@355: (*refCounter_)++; etisserant@355: s_ =o.s_; etisserant@355: etisserant@355: nofSockets_++; etisserant@355: } etisserant@355: etisserant@355: Socket& Socket::operator=(Socket& o) { etisserant@355: (*o.refCounter_)++; etisserant@355: etisserant@355: refCounter_=o.refCounter_; etisserant@355: s_ =o.s_; etisserant@355: etisserant@355: nofSockets_++; etisserant@355: etisserant@355: return *this; etisserant@355: } etisserant@355: etisserant@355: void Socket::Close() { etisserant@355: closesocket(s_); etisserant@355: } etisserant@355: etisserant@355: std::string Socket::ReceiveBytes() { etisserant@355: std::string ret; etisserant@355: char buf[1024]; etisserant@355: etisserant@355: while (1) { etisserant@355: u_long arg = 0; etisserant@355: if (ioctlsocket(s_, FIONREAD, &arg) != 0) etisserant@355: break; etisserant@355: etisserant@355: if (arg == 0) etisserant@355: break; etisserant@355: etisserant@355: if (arg > 1024) arg = 1024; etisserant@355: etisserant@355: int rv = recv (s_, buf, arg, 0); etisserant@355: if (rv <= 0) break; etisserant@355: etisserant@355: std::string t; etisserant@355: etisserant@355: t.assign (buf, rv); etisserant@355: ret += t; etisserant@355: } etisserant@355: etisserant@355: return ret; etisserant@355: } etisserant@355: etisserant@355: std::string Socket::ReceiveLine() { etisserant@355: std::string ret; etisserant@355: while (1) { etisserant@355: char r; etisserant@355: etisserant@355: switch(recv(s_, &r, 1, 0)) { etisserant@355: case 0: // not connected anymore; etisserant@355: // ... but last line sent etisserant@355: // might not end in \n, etisserant@355: // so return ret anyway. etisserant@355: return ret; etisserant@355: case -1: etisserant@355: return ""; etisserant@355: // if (errno == EAGAIN) { etisserant@355: // return ret; etisserant@355: // } else { etisserant@355: // // not connected anymore etisserant@355: // return ""; etisserant@355: // } etisserant@355: } etisserant@355: etisserant@355: if (r == '\n') return ret; etisserant@355: ret += r; etisserant@355: } etisserant@355: } etisserant@355: etisserant@355: void Socket::SendLine(std::string s) { etisserant@355: s += '\n'; etisserant@355: send(s_,s.c_str(),s.length(),0); etisserant@355: } etisserant@355: etisserant@355: void Socket::SendBytes(const std::string& s) { etisserant@355: send(s_,s.c_str(),s.length(),0); etisserant@355: } etisserant@355: etisserant@355: SocketServer::SocketServer(int port, int connections, TypeSocket type) { etisserant@355: sockaddr_in sa; etisserant@355: etisserant@355: memset(&sa, 0, sizeof(sa)); etisserant@355: etisserant@355: sa.sin_family = PF_INET; etisserant@355: sa.sin_port = htons(port); etisserant@355: s_ = socket(AF_INET, SOCK_STREAM, 0); etisserant@355: if (s_ == INVALID_SOCKET) { etisserant@355: throw "INVALID_SOCKET"; etisserant@355: } etisserant@355: etisserant@355: if(type==NonBlockingSocket) { etisserant@355: u_long arg = 1; etisserant@355: ioctlsocket(s_, FIONBIO, &arg); etisserant@355: } etisserant@355: etisserant@355: /* bind the socket to the internet address */ etisserant@355: if (bind(s_, (sockaddr *)&sa, sizeof(sockaddr_in)) == SOCKET_ERROR) { etisserant@355: closesocket(s_); etisserant@355: throw "INVALID_SOCKET"; etisserant@355: } etisserant@355: etisserant@355: listen(s_, connections); etisserant@355: } etisserant@355: etisserant@355: Socket* SocketServer::Accept() { etisserant@355: SOCKET new_sock = accept(s_, 0, 0); etisserant@355: if (new_sock == INVALID_SOCKET) { etisserant@355: int rc = WSAGetLastError(); etisserant@355: if(rc==WSAEWOULDBLOCK) { etisserant@355: return 0; // non-blocking call, no request pending etisserant@355: } etisserant@355: else { etisserant@355: throw "Invalid Socket"; etisserant@355: } etisserant@355: } etisserant@355: etisserant@355: Socket* r = new Socket(new_sock); etisserant@355: return r; etisserant@355: } etisserant@355: etisserant@355: SocketClient::SocketClient(const std::string& host, int port) : Socket() { etisserant@355: std::string error; etisserant@355: etisserant@355: hostent *he; etisserant@355: if ((he = gethostbyname(host.c_str())) == 0) { etisserant@355: error = strerror(errno); etisserant@355: throw error; etisserant@355: } etisserant@355: etisserant@355: sockaddr_in addr; etisserant@355: addr.sin_family = AF_INET; etisserant@355: addr.sin_port = htons(port); etisserant@355: addr.sin_addr = *((in_addr *)he->h_addr); etisserant@355: memset(&(addr.sin_zero), 0, 8); etisserant@355: etisserant@355: if (::connect(s_, (sockaddr *) &addr, sizeof(sockaddr))) { etisserant@355: error = strerror(WSAGetLastError()); etisserant@355: throw error; etisserant@355: } etisserant@355: } etisserant@355: etisserant@355: SocketSelect::SocketSelect(Socket const * const s1, Socket const * const s2, TypeSocket type) { etisserant@355: FD_ZERO(&fds_); etisserant@355: FD_SET(const_cast(s1)->s_,&fds_); etisserant@355: if(s2) { etisserant@355: FD_SET(const_cast(s2)->s_,&fds_); etisserant@355: } etisserant@355: etisserant@355: TIMEVAL tval; etisserant@355: tval.tv_sec = 0; etisserant@355: tval.tv_usec = 1; etisserant@355: etisserant@355: TIMEVAL *ptval; etisserant@355: if(type==NonBlockingSocket) { etisserant@355: ptval = &tval; etisserant@355: } etisserant@355: else { etisserant@355: ptval = 0; etisserant@355: } etisserant@355: etisserant@355: if (select (0, &fds_, (fd_set*) 0, (fd_set*) 0, ptval) == SOCKET_ERROR) etisserant@355: throw "Error in select"; etisserant@355: } etisserant@355: etisserant@355: bool SocketSelect::Readable(Socket const* const s) { etisserant@355: if (FD_ISSET(s->s_,&fds_)) return true; etisserant@355: return false; etisserant@355: }