blob: f23be8c6d171cbc1bcbaf7fe55aa853d84e3bacf [file] [log] [blame]
// Copyright (C) 2010 David Sugar, Tycho Softworks.
//
// This file is part of GNU uCommon C++.
//
// GNU uCommon C++ 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 3 of the License, or
// (at your option) any later version.
//
// GNU uCommon C++ 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with GNU uCommon C++. If not, see <http://www.gnu.org/licenses/>.
#include <ucommon-config.h>
#include <ucommon/export.h>
#include <ucommon/protocols.h>
#include <ucommon/socket.h>
#include <ucommon/timers.h>
#include <ucommon/buffer.h>
#include <ucommon/string.h>
#include <ucommon/shell.h>
using namespace UCOMMON_NAMESPACE;
TCPBuffer::TCPBuffer() :
BufferProtocol()
{
so = INVALID_SOCKET;
}
TCPBuffer::TCPBuffer(const char *host, const char *service, size_t size) :
BufferProtocol()
{
so = INVALID_SOCKET;
open(host, service, size);
}
TCPBuffer::TCPBuffer(const TCPServer *server, size_t size) :
BufferProtocol()
{
so = INVALID_SOCKET;
open(server, size);
}
TCPBuffer::~TCPBuffer()
{
TCPBuffer::close();
}
void TCPBuffer::open(const char *host, const char *service, size_t size)
{
struct addrinfo *list = Socket::query(host, service, SOCK_STREAM, 0);
if(!list)
return;
so = Socket::create(list, SOCK_STREAM, 0);
Socket::release(list);
if(so == INVALID_SOCKET)
return;
_buffer(size);
}
void TCPBuffer::open(const TCPServer *server, size_t size)
{
close();
so = server->accept();
if(so == INVALID_SOCKET)
return;
_buffer(size);
}
void TCPBuffer::close(void)
{
if(so == INVALID_SOCKET)
return;
BufferProtocol::release();
Socket::release(so);
so = INVALID_SOCKET;
}
void TCPBuffer::_buffer(size_t size)
{
unsigned iobuf = 0;
unsigned mss = size;
unsigned max = 0;
#ifdef TCP_MAXSEG
socklen_t alen = sizeof(max);
#endif
if(size < 80) {
allocate(size);
return;
}
#ifdef TCP_MAXSEG
if(mss)
setsockopt(so, IPPROTO_TCP, TCP_MAXSEG, (char *)&max, sizeof(max));
getsockopt(so, IPPROTO_TCP, TCP_MAXSEG, (char *)&max, &alen);
#endif
if(max && max < mss)
mss = max;
if(!mss) {
if(max)
mss = max;
else
mss = 536;
goto alloc;
}
if(mss < 80)
mss = 80;
if(mss * 7 < 64000u)
iobuf = mss * 7;
else if(size * 6 < 64000u)
iobuf = mss * 6;
else
iobuf = mss * 5;
Socket::sendsize(so, iobuf);
Socket::recvsize(so, iobuf);
if(mss < 512)
Socket::sendwait(so, mss * 4);
alloc:
allocate(size);
}
int TCPBuffer::_err(void) const
{
return ioerr;
}
void TCPBuffer::_clear(void)
{
ioerr = 0;
}
bool TCPBuffer::_blocking(void)
{
if(iowait)
return true;
return false;
}
size_t TCPBuffer::_push(const char *address, size_t len)
{
if(ioerr)
return 0;
ssize_t result = writeto(address, len);
if(result < 0)
result = 0;
return (size_t)result;
}
size_t TCPBuffer::_pull(char *address, size_t len)
{
ssize_t result;
result = readfrom(address, len);
if(result < 0)
result = 0;
return (size_t)result;
}
bool TCPBuffer::_pending(void)
{
if(input_pending())
return true;
if(is_input() && iowait && iowait != Timer::inf)
return Socket::wait(so, iowait);
return Socket::wait(so, 0);
}