| 
							- // RH_TCP.cpp
 - //
 - // Copyright (C) 2014 Mike McCauley
 - // $Id: RH_TCP.cpp,v 1.5 2015/08/13 02:45:47 mikem Exp $
 - 
 - #include <RadioHead.h>
 - 
 - // This can only build on Linux and compatible systems
 - #if (RH_PLATFORM == RH_PLATFORM_UNIX) 
 - 
 - #include <RH_TCP.h>
 - #include <sys/types.h>
 - #include <errno.h>
 - #include <sys/socket.h>
 - #include <netinet/in.h>
 - #include <arpa/inet.h>
 - #include <unistd.h>
 - #include <sys/ioctl.h>
 - #include <netdb.h>
 - #include <string>
 - 
 - RH_TCP::RH_TCP(const char* server)
 -     : _server(server),
 -       _rxBufLen(0),
 -       _rxBufValid(false),
 -       _socket(-1)
 - {
 - }
 -     
 - bool RH_TCP::init()
 - {   
 -     if (!connectToServer())
 - 	return false;
 -     return sendThisAddress(_thisAddress);
 - }
 -     
 - bool RH_TCP::connectToServer()
 - {
 -     struct addrinfo hints;
 -     struct addrinfo *result, *rp;
 -     int sfd, s;
 -     struct sockaddr_storage peer_addr;
 -     socklen_t peer_addr_len;
 - 
 -     memset(&hints, 0, sizeof(struct addrinfo));
 -     hints.ai_family = AF_UNSPEC;    // Allow IPv4 or IPv6
 -     hints.ai_socktype = SOCK_STREAM; // Stream socket
 -     hints.ai_flags = AI_PASSIVE;    // For wildcard IP address 
 -     hints.ai_protocol = 0;          // Any protocol 
 -     hints.ai_canonname = NULL;
 -     hints.ai_addr = NULL;
 -     hints.ai_next = NULL;
 -     
 -     std::string server(_server);
 -     std::string port("4000");
 -     size_t indexOfSeparator = server.find_first_of(':');
 -     if (indexOfSeparator != std::string::npos)
 -     {
 - 	port = server.substr(indexOfSeparator+1);
 - 	server.erase(indexOfSeparator);
 -     }
 - 
 -     s = getaddrinfo(server.c_str(), port.c_str(), &hints, &result);
 -     if (s != 0) 
 -     {
 - 	fprintf(stderr, "RH_TCP::connect getaddrinfo failed: %s\n", gai_strerror(s));
 - 	return false;
 -     }
 - 
 -     // getaddrinfo() returns a list of address structures.
 -     // Try each address until we successfully connect(2).
 -     // If socket(2) (or connect(2)) fails, we (close the socket
 -     // and) try the next address. */
 - 
 -     for (rp = result; rp != NULL; rp = rp->ai_next) 
 -     {
 - 	_socket = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
 - 	if (_socket == -1)
 - 	    continue;
 - 	
 - 	if (connect(_socket, rp->ai_addr, rp->ai_addrlen) == 0)
 - 	    break;                  /* Success */
 - 
 - 	close(_socket);
 -     }
 - 
 -     if (rp == NULL) 
 -     {               /* No address succeeded */
 - 	fprintf(stderr, "RH_TCP::connect could not connect to %s\n", _server);
 - 	return false;
 -     }
 - 
 -     freeaddrinfo(result);           /* No longer needed */
 - 
 -     // Now make the socket non-blocking
 -     int on = 1;
 -     int rc = ioctl(_socket, FIONBIO, (char *)&on);
 -     if (rc < 0)
 -     {
 - 	fprintf(stderr,"RH_TCP::init failed to set socket non-blocking: %s\n", strerror(errno));
 - 	close(_socket);
 - 	_socket = -1;
 - 	return false;
 -     }
 -     return true;
 - }
 - 
 - void RH_TCP::clearRxBuf()
 - {
 -     _rxBufValid = false;
 -     _rxBufLen = 0;
 - }
 - 
 - void RH_TCP::checkForEvents()
 - {
 -     #define RH_TCP_SOCKETBUF_LEN 500
 -     static uint8_t socketBuf[RH_TCP_SOCKETBUF_LEN]; // Room for several messages
 -     static uint16_t socketBufLen = 0;
 - 
 -     // Read at most the amount of space we have left in the buffer
 -     ssize_t count = read(_socket, socketBuf + socketBufLen, sizeof(socketBuf) - socketBufLen);
 -     if (count < 0)
 -     {
 - 	if (errno != EAGAIN)
 - 	{
 - 	    fprintf(stderr,"RH_TCP::checkForEvents read error: %s\n", strerror(errno));
 - 	    exit(1);
 - 	}
 -     }
 -     else if (count == 0)
 -     {
 - 	// End of file
 - 	fprintf(stderr,"RH_TCP::checkForEvents unexpected end of file on read\n");
 - 	exit(1);
 -     }
 -     else
 -     {
 - 	socketBufLen += count;
 - 	while (socketBufLen >= 5)
 - 	{
 - 	    RHTcpTypeMessage* message = ((RHTcpTypeMessage*)socketBuf);
 - 	    uint32_t len = ntohl(message->length);
 - 	    uint32_t messageLen = len + sizeof(message->length);
 - 	    if (len > sizeof(socketBuf) - sizeof(message->length))
 - 	    {
 - 		// Bogus length
 - 		fprintf(stderr, "RH_TCP::checkForEvents read ridiculous length: %d. Corrupt message stream? Aborting\n", len);
 - 		exit(1);
 - 	    }
 - 	    if (socketBufLen >= len + sizeof(message->length))
 - 	    {
 - 		// Got at least all of this message
 - 		if (message->type == RH_TCP_MESSAGE_TYPE_PACKET && len >= 5)
 - 		{
 - 		    // REVISIT: need to check if we are actually receiving?
 - 		    // Its a new packet, extract the headers and payload
 - 		    RHTcpPacket* packet = ((RHTcpPacket*)socketBuf);
 - 		    _rxHeaderTo    = packet->to;
 - 		    _rxHeaderFrom  = packet->from;
 - 		    _rxHeaderId    = packet->id;
 - 		    _rxHeaderFlags = packet->flags;
 - 		    uint32_t payloadLen = len - 5;
 - 		    if (payloadLen <= sizeof(_rxBuf))
 - 		    {
 - 			// Enough room in our receiver buffer
 - 			memcpy(_rxBuf, packet->payload, payloadLen);
 - 			_rxBufLen = payloadLen;
 - 			_rxBufFull = true;
 - 		    }
 - 		}
 - 		// check for other message types here
 - 		// Now remove the used message by copying the trailing bytes (maybe start of a new message?)
 - 		// to the top of the buffer
 - 		memcpy(socketBuf, socketBuf + messageLen, sizeof(socketBuf) - messageLen);
 - 		socketBufLen -= messageLen;
 - 	    }
 - 	}
 -     }
 - }
 - 
 - void RH_TCP::validateRxBuf()
 - {
 -     // The headers have already been extracted
 -     if (_promiscuous ||
 - 	_rxHeaderTo == _thisAddress ||
 - 	_rxHeaderTo == RH_BROADCAST_ADDRESS)
 -     {
 - 	_rxGood++;
 - 	_rxBufValid = true;
 -     }
 - }
 - 
 - bool RH_TCP::available()
 - {
 -     if (_socket < 0)
 - 	return false;
 -     checkForEvents();
 -     if (_rxBufFull)
 -     {
 - 	validateRxBuf();
 - 	_rxBufFull= false;
 -     }
 -     return _rxBufValid;
 - }
 - 
 - // Block until something is available
 - void RH_TCP::waitAvailable()
 - {
 -     waitAvailableTimeout(0); // 0 = Wait forever
 - }
 - 
 - // Block until something is available or timeout expires
 - bool RH_TCP::waitAvailableTimeout(uint16_t timeout)
 - {
 -     int            max_fd;
 -     fd_set         input;
 -     int            result;
 - 
 -     FD_ZERO(&input);
 -     FD_SET(_socket, &input);
 -     max_fd = _socket + 1;
 - 
 -     if (timeout)
 -     {
 - 	struct timeval timer;
 - 	// Timeout is in milliseconds
 - 	timer.tv_sec  = timeout / 1000;
 - 	timer.tv_usec = (timeout % 1000) * 1000;
 - 	result = select(max_fd, &input, NULL, NULL, &timer);
 -     }
 -     else
 -     {
 - 	result = select(max_fd, &input, NULL, NULL, NULL);
 -     }
 -     if (result < 0)
 - 	fprintf(stderr, "RH_TCP::waitAvailableTimeout: select failed %s\n", strerror(errno));
 -     return result > 0;
 - }
 - 
 - bool RH_TCP::recv(uint8_t* buf, uint8_t* len)
 - {
 -     if (!available())
 - 	return false;
 - 
 -     if (buf && len)
 -     {
 - 	if (*len > _rxBufLen)
 - 	    *len = _rxBufLen;
 - 	memcpy(buf, _rxBuf, *len);
 -     }
 -     clearRxBuf();
 -     return true;
 - }
 - 
 - bool RH_TCP::send(const uint8_t* data, uint8_t len)
 - {
 -     bool ret = sendPacket(data, len);
 -     delay(10); // Wait for transmit to succeed. REVISIT: depends on length and speed
 -     return ret;
 - }
 - 
 - uint8_t RH_TCP::maxMessageLength()
 - {
 -     return RH_TCP_MAX_MESSAGE_LEN;
 - }
 - 
 - void RH_TCP::setThisAddress(uint8_t address)
 - {
 -     RHGenericDriver::setThisAddress(address);
 -     sendThisAddress(_thisAddress);
 - }
 - 
 - bool RH_TCP::sendThisAddress(uint8_t thisAddress)
 - {
 -     if (_socket < 0)
 - 	return false;
 -     RHTcpThisAddress m;
 -     m.length = htonl(2);
 -     m.type = RH_TCP_MESSAGE_TYPE_THISADDRESS;
 -     m.thisAddress = thisAddress;
 -     ssize_t sent = write(_socket, &m, sizeof(m));
 -     return sent > 0;
 - }
 - 
 - bool RH_TCP::sendPacket(const uint8_t* data, uint8_t len)
 - {
 -     if (_socket < 0)
 - 	return false;
 -     RHTcpPacket m;
 -     m.length = htonl(len + 4);
 -     m.type  = RH_TCP_MESSAGE_TYPE_PACKET;
 -     m.to    = _txHeaderTo;
 -     m.from  = _txHeaderFrom;
 -     m.id    = _txHeaderId;
 -     m.flags = _txHeaderFlags;
 -     memcpy(m.payload, data, len);
 -     ssize_t sent = write(_socket, &m, len + 8);
 -     return sent > 0;
 - }
 - 
 - #endif
 
 
  |