A simple class to do line-based TCP communication. To be used with the NetServicesMin library-.

Dependents:   pop3demo

tcplinestream.cpp

Committer:
hlipka
Date:
2011-02-18
Revision:
1:28416a5a4ec5
Parent:
0:f2727a16072f

File content as of revision 1:28416a5a4ec5:

/*
* TcpLineStream library
* Copyright (c) 2010 Hendrik Lipka
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#include "Timer.h"

#include "tcplinestream.h"

#include "dnsresolve.h"

void TCPLineStream::onTCPSocketEvent(TCPSocketEvent e) {
//    printf("New TCPSocketEvent: %d\n",e);
    switch (e) {
        case TCPSOCKET_CONNECTED:
            _connected = true;
            _connecting=false;
            break;

        case TCPSOCKET_READABLE:
            break;
        case TCPSOCKET_WRITEABLE:
            break;

        case TCPSOCKET_CONTIMEOUT:
        case TCPSOCKET_CONRST:
        case TCPSOCKET_CONABRT:
        case TCPSOCKET_ERROR:
        case TCPSOCKET_DISCONNECTED: {
            // don't close the real socket here, as this may skip the already received data
            _connected = false;
            _connecting=false;
        }
        break;
    }
}

TCPLineStream::TCPLineStream(const char *server, int port, const char* term) {
    _server=server;
    _port=port;
    _term=term;
}

bool TCPLineStream::open() {
    _closed=false;
    _readpos=0;
    _readlen=0;

    _socket=new TCPSocket();
    _socket->setOnEvent(this,&TCPLineStream::onTCPSocketEvent);

    IpAddr addr;
    // ignore IP adress - is !null even if it is 0.0.0.0
    // so do DNS if a name is given
    if (NULL!=_server) {
//    printf("do DNS request for %s\n",_server);
        DNSResolver dr;
        addr=dr.resolveName(_server);
    }
//    printf("server=%i.%i.%i.%i/%i\n",addr[0],addr[1],addr[2],addr[3],_port);

    Host host(addr, _port);

//    printf("connect\n");
    TCPSocketErr bindErr = _socket->connect(host);

    if (bindErr != 0) {
        printf("connection bind error %i\n", bindErr);
        return false;
    }
//    printf("connecting\n");
    // wait for connection established
    _connecting=true;
    mbed::Timer tmr;
    tmr.start();
    tmr.reset();

    while (_connecting) {
        Net::poll();
        wait_us(100);
        if (tmr.read()>20) {
            printf("error: reached timeout\n");
            break;
        }
    }
    if (!_connected) {
        printf("error - could not connect (timeout)\n");
        return false;
    }
//    printf("connected\n");
    return true;
}

void TCPLineStream::sendLine(string line) {
//    printf("send request[%s]\n",line.c_str());
    _socket->send(line.append(_term).c_str(),line.length()+strlen(_term));
    Net::poll();
}

void TCPLineStream::close() {
    if (_closed)
        return;

    while (_connected) {
        Net::poll();
        // read remaining data
        int err=_socket->recv(_readbuf,BUFSIZE-1);
        if (err<=0)
            break;
    }

    while (true) {
        Net::poll();
        // read remaining data
        int err=_socket->recv(_readbuf,BUFSIZE-1);
        if (err<=0)
            break;
    }

    _socket->resetOnEvent();
    _socket->close();
    delete _socket;
    _closed=true;
}

string TCPLineStream::readLine(int strLen) {
    string r;
    r.reserve(strLen);

    int emptyCount=0;
    int lineendPos=0;
    while (true) {
        Net::poll();
        if (_readlen>_readpos) {
            while (_readbuf[_readpos]!=0 && _readpos<_readlen) {
                char c=_readbuf[_readpos++];
                //if the current char is the right one of the line terminator sequence
                if (c==_term[lineendPos]) {
                    lineendPos++;
                    if (0==_term[lineendPos]) { // last char of term sequence -> finished
                        return r;
                    }
                } else { // other characters
                    int i=0;
                    while (i<lineendPos) // add missed characters from term to string, if there are any
                        r.push_back(_term[i++]);
                    lineendPos=0;
                    r.push_back(c);
                }
            }
        } else {
            int err=_socket->recv(_readbuf,BUFSIZE-1);
            if (err < 0) {
                printf("error while receiving data: %i!\n",err);
                break;
            } else if (err>0) {
                emptyCount=0;
                _readbuf[err]=0;
                _readlen=err;
                _readpos=0;
//                printf("r=%s\n",_readbuf);
            } else {
                // when we don't receive any data, and are not connected
                // we stop because there isn't anything left
                if (emptyCount++>2)
                    if (!_connected)
                        break;
            }
        }
        wait_us(100);
    }
    return r;
}