std::streamsize Socket::send(const char_type* buffer, std::streamsize size) { if(size == 0) return 0; switch(type) { case UDP_PEER: { AddrinfoContainer info = getSocketInfoFor(hostRemote.c_str(), portRemote, false); size_t sentBytes = 0; while(sentBytes < size) { int result = ::sendto(handle, (const char*)buffer + sentBytes, size - sentBytes, 0, info->ai_addr, info->ai_addrlen); if(result == -1) throw Exception(Exception::ERROR_SEND); sentBytes += result; } return sentBytes; } case TCP_CLIENT: case TCP_SERVERS_CLIENT: { size_t sentBytes = 0; while(sentBytes < size) { int result = ::send(handle, (const char*)buffer + sentBytes, size - sentBytes, 0); if(result == -1) throw Exception(Exception::ERROR_SEND); sentBytes += result; } return sentBytes; } default: case NONE: case TCP_SERVER: throw Exception(Exception::BAD_TYPE); } }
std::streamsize Socket::showmanyc() { #ifdef WIN32 unsigned long result = 0; if(ioctlsocket(handle, FIONREAD, &result)) { #else int result = 0; if(ioctl(handle, FIONREAD, &result)) { #endif disconnect(); throw Exception(Exception::ERROR_IOCTL); }else return result; } std::streamsize Socket::advanceInputBuffer() { if(inputIntermediateSize == 0) //No input buffer return 0; std::streamsize inAvail; if(type == UDP_PEER) inAvail = 0; else{ inAvail = egptr()-gptr(); memmove(eback(), gptr(), inAvail); } try { inAvail += receive(eback()+inAvail, inputIntermediateSize-inAvail); } catch(Exception err) { } setg(eback(), eback(), eback()+inAvail); return inAvail; } std::streamsize Socket::receive(char_type* buffer, std::streamsize size) { if(type == TCP_SERVER) throw Exception(Exception::BAD_TYPE); if(status != Socket::Status::READY && status != Socket::Status::BUSY) return 0; size = std::min(size, showmanyc()); if(size == 0) return 0; switch(type) { case UDP_PEER: { struct sockaddr_storage remoteAddr; #ifdef WIN32 int addrSize = sizeof(remoteAddr); #else unsigned int addrSize = sizeof(remoteAddr); #endif int result = recvfrom(handle, (char*)buffer, size, 0, reinterpret_cast<struct sockaddr*>(&remoteAddr), &addrSize); if(result <= 0) { portRemote = 0; hostRemote = ""; throw Exception(Exception::ERROR_READ); }else readSockaddr(&remoteAddr, hostRemote, portRemote); return result; } case TCP_CLIENT: case TCP_SERVERS_CLIENT: { int result = recv(handle, (char*)buffer, size, 0); if(result <= 0) throw Exception(Exception::ERROR_READ); return result; } default: case NONE: case TCP_SERVER: throw Exception(Exception::BAD_TYPE); } } std::streamsize Socket::send(const char_type* buffer, std::streamsize size) { if(type == TCP_SERVER) throw Exception(Exception::BAD_TYPE); if(status != Socket::Status::READY || size == 0) return 0; switch(type) { case UDP_PEER: { AddrinfoContainer info = getSocketInfoFor(hostRemote.c_str(), portRemote, false); size_t sentBytes = 0; while(sentBytes < (size_t)size) { int result = ::sendto(handle, (const char*)buffer + sentBytes, size - sentBytes, 0, info->ai_addr, info->ai_addrlen); if(result <= 0) { status = BUSY; throw Exception(Exception::ERROR_SEND); } sentBytes += result; } return sentBytes; } case TCP_CLIENT: case TCP_SERVERS_CLIENT: { size_t sentBytes = 0; while(sentBytes < (size_t)size) { int result = ::send(handle, (const char*)buffer + sentBytes, size - sentBytes, 0); if(result <= 0) { status = BUSY; throw Exception(Exception::ERROR_SEND); } sentBytes += result; } return sentBytes; } default: case NONE: case TCP_SERVER: throw Exception(Exception::BAD_TYPE); } } std::streamsize Socket::redirect(const std::vector<std::shared_ptr<Socket>>& destinations) { if(type == TCP_SERVER) throw Exception(Exception::BAD_TYPE); std::streamsize size = 0; while(in_avail()) { auto length = egptr()-gptr(); for(const auto& destination : destinations) if(destination->sputn(gptr(), length) < length) throw Exception(Exception::ERROR_SEND); gbump(length); size += length; advanceInputBuffer(); } return size; } std::streamsize Socket::getInputBufferSize() { return inputIntermediateSize; } std::streamsize Socket::getOutputBufferSize() { return epptr()-pbase(); } void Socket::setInputBufferSize(std::streamsize n) { if(eback()) { delete[] eback(); setg(NULL, NULL, NULL); } if(n == 0) return; if(type == TCP_SERVER) throw Exception(Exception::BAD_TYPE); char_type* readBuffer = new char_type[n]; setg(readBuffer, readBuffer, readBuffer); inputIntermediateSize = n; } void Socket::setOutputBufferSize(std::streamsize n) { if(pbase()) { delete[] pbase(); setp(NULL, NULL); } if(n == 0) return; if(type == TCP_SERVER) throw Exception(Exception::BAD_TYPE); char_type* writeBuffer = new char_type[n]; setp(writeBuffer, writeBuffer+n); } void Socket::setBlockingMode(bool blocking) { #ifdef WIN32 unsigned long flag = !blocking; if(ioctlsocket(handle, FIONBIO, &flag) != 0) #else int flags = fcntl(handle, F_GETFL); if(blocking) flags &= ~O_NONBLOCK; else flags |= O_NONBLOCK; if(fcntl(handle, F_SETFL, flags) == -1) #endif throw Exception(Exception::ERROR_IOCTL); }
void Socket::initSocket(bool blockingConnect) { AddrinfoContainer info; if(type == TCP_CLIENT) info = getSocketInfoFor(hostRemote.c_str(), portRemote, false); else{ const char* host; if(!hostLocal.compare("") || !hostLocal.compare("*")) host = NULL; else host = hostLocal.c_str(); info = getSocketInfoFor(host, portLocal, true); } struct addrinfo* nextAddr = info.get(); while(nextAddr) { handle = socket(nextAddr->ai_family, nextAddr->ai_socktype, nextAddr->ai_protocol); if(handle == -1) { nextAddr = nextAddr->ai_next; continue; } setBlockingMode(blockingConnect); #ifdef WIN32 char flag = 1; #else int flag = 1; #endif if(setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) == -1) { disconnect(); throw Exception(Exception::ERROR_SET_SOCK_OPT); } switch(nextAddr->ai_family) { case AF_INET: ipVersion = IPv4; break; case AF_INET6: ipVersion = IPv6; break; } switch(type) { case NONE: case TCP_SERVERS_CLIENT: disconnect(); throw Exception(Exception::BAD_TYPE); case TCP_CLIENT: if(connect(handle, nextAddr->ai_addr, nextAddr->ai_addrlen) == -1 && blockingConnect) { closesocket(handle); handle = -1; }else if(blockingConnect) status = READY; else status = CONNECTING; break; case TCP_SERVER: { if(bind(handle, nextAddr->ai_addr, nextAddr->ai_addrlen) == -1) { closesocket(handle); handle = -1; } if(listen(handle, status) == -1) { disconnect(); throw Exception(Exception::ERROR_INIT); } } break; case UDP_PEER: { if(bind(handle, nextAddr->ai_addr, nextAddr->ai_addrlen) == -1) { closesocket(handle); handle = -1; } status = READY; } break; } if(handle == -1) { nextAddr = nextAddr->ai_next; continue; } if(blockingConnect) setBlockingMode(false); break; } if(handle == -1) { disconnect(); throw Exception(Exception::ERROR_INIT); } struct sockaddr_storage localAddr; #ifdef WIN32 int size = sizeof(localAddr); #else unsigned int size = sizeof(localAddr); #endif if(getsockname(handle, reinterpret_cast<struct sockaddr*>(&localAddr), &size) != 0) { disconnect(); throw Exception(Exception::ERROR_GET_SOCK_NAME); } readSockaddr(&localAddr, hostLocal, portLocal); }