mbed socket API
Dependents: EthernetInterface EthernetInterface_RSF EthernetInterface EthernetInterface ... more
Deprecated
This is an mbed 2 sockets library. For mbed 5, network sockets have been revised to better support additional network stacks and thread safety here.
Diff: TCPSocket.cpp
- Revision:
- 0:1f77255a22f5
- Child:
- 1:8080965f5d76
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TCPSocket.cpp Tue Jun 26 13:16:26 2012 +0000 @@ -0,0 +1,280 @@ +/* Copyright (C) 2012 mbed.org, MIT License + * + * 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 "TCPSocket.h" + +#include "bsd_socket.h" + +#include <cstring> + +using std::memset; + +TCPSocket::TCPSocket() : m_sock(-1) +{ +} + +TCPSocket::~TCPSocket() +{ + close(); //Don't want to leak +} + +int TCPSocket::connect(char* host, int port, int timeout) +{ + int ret = init(); + if( ret < 0 ) + { + return -1; + } + + //Populate m_remoteHost + std::memset(&m_remoteHost, 0, sizeof(struct sockaddr_in)); + + //Resolve DNS address or populate hard-coded IP address + struct hostent *server = ::gethostbyname(host); + if(server == NULL) + { + return -1; //Could not resolve address + } + std::memcpy((char*)&m_remoteHost.sin_addr.s_addr, (char*)server->h_addr_list[0], server->h_length); + + m_remoteHost.sin_family = AF_INET; + m_remoteHost.sin_port = htons(port); + + ret = ::connect(m_sock, (const struct sockaddr *)&m_remoteHost, sizeof(m_remoteHost)); + if (ret < 0) + { + close(); + return -1; + } + + return 0; +} + +int TCPSocket::bind(int port) +{ + int ret = init(); + if( ret < 0 ) + { + return -1; + } + + struct sockaddr_in localHost; + std::memset(&localHost, 0, sizeof(localHost)); + + localHost.sin_family = AF_INET; + localHost.sin_port = htons(port); + localHost.sin_addr.s_addr = INADDR_ANY; + + ret = ::bind(m_sock, (const struct sockaddr *)&localHost, sizeof(localHost)); + if (ret < 0) + { + close(); + return -1; + } + + return 0; +} + +int TCPSocket::listen(int max) +{ + if( m_sock < 0 ) + { + return -1; + } + + int ret = ::listen(m_sock, max); + if (ret < 0) + { + close(); + return -1; + } + + return 0; +} + +int TCPSocket::accept(TCPSocket& socket, char** host, int* port, int timeout) +{ + if( m_sock < 0 ) + { + return -1; + } + + //Populate m_remoteHost + std::memset(&socket.m_remoteHost, 0, sizeof(struct sockaddr_in)); + + struct timeval t_val; //t_val will be decremented on each call to select() + t_val.tv_sec = timeout / 1000; + t_val.tv_usec = (timeout - (t_val.tv_sec * 1000)) * 1000; + //Wait for socket to get some connection request (i.e. to be "readable") + //Creating FS set + fd_set socksSet; + FD_ZERO(&socksSet); + FD_SET(m_sock, &socksSet); + int ret = ::select(FD_SETSIZE, &socksSet, NULL, NULL, &t_val); + if(ret <= 0 || !FD_ISSET(m_sock, &socksSet)) + { + return -1; //Timeout + } + + socklen_t newSockRemoteHostLen = sizeof(socket.m_remoteHost); + + ret = ::accept(m_sock, (struct sockaddr*)&socket.m_remoteHost, &newSockRemoteHostLen); + if( ret < 0 ) + { + return -1; //Accept failed + } + + socket.m_sock = ret; //ret is the new socket's fd + socket.m_closedByRemoteHost = false; + + static char hostBuf[16]; + inet_ntoa_r(socket.m_remoteHost.sin_addr, hostBuf, sizeof(hostBuf)); + + *host = hostBuf; + *port = ntohs(socket.m_remoteHost.sin_port); + + return 0; +} + +// -1 if unsuccessful, else number of bytes written +int TCPSocket::send(uint8_t* data, int length, int timeout) +{ + if( m_sock < 0 ) + { + return -1; + } + + if( m_closedByRemoteHost ) + { + return 0; + } + + size_t writtenLen = 0; + struct timeval t_val; //t_val will be decremented on each call to select() + t_val.tv_sec = timeout / 1000; + t_val.tv_usec = (timeout - (t_val.tv_sec * 1000)) * 1000; + while(writtenLen < length) + { + //Wait for socket to be writeable + //Creating FS set + fd_set socksSet; + FD_ZERO(&socksSet); + FD_SET(m_sock, &socksSet); + + int ret = ::select(FD_SETSIZE, NULL, &socksSet, NULL, &t_val); + if(ret <= 0 || !FD_ISSET(m_sock, &socksSet)) + { + return writtenLen; //Timeout -- FIXME should we return -1 or writtenLength ? + } + + ret = ::send(m_sock, data + writtenLen, length - writtenLen, 0); + if( ret > 0) + { + writtenLen += ret; + continue; + } + else if( ret == 0 ) + { + m_closedByRemoteHost = true; + return writtenLen; //Connection was closed by remote host -- FIXME how do we signal that the connection was closed ? + } + else + { + return -1; //Connnection error + } + } + + return writtenLen; +} + +// -1 if unsuccessful, else number of bytes received +int TCPSocket::receive(uint8_t* data, int length, int timeout) +{ + if( m_sock < 0 ) + { + return -1; + } + + if( m_closedByRemoteHost ) + { + return 0; + } + + size_t readLen = 0; + struct timeval t_val; //t_val will be decremented on each call to select() + t_val.tv_sec = timeout / 1000; + t_val.tv_usec = (timeout - (t_val.tv_sec * 1000)) * 1000; + while(readLen < length) + { + //Wait for socket to be readable + //Creating FS set + fd_set socksSet; + FD_ZERO(&socksSet); + FD_SET(m_sock, &socksSet); + int ret = ::select(FD_SETSIZE, &socksSet, NULL, NULL, &t_val); + if(ret <= 0 || !FD_ISSET(m_sock, &socksSet)) + { + return readLen; //Timeout -- FIXME should we return -1 or writtenLength ? + } + + ret = ::recv(m_sock, data + readLen, length - readLen, 0); + if( ret > 0) + { + readLen += ret; + continue; + } + else if( ret == 0 ) + { + m_closedByRemoteHost = true; + return readLen; //Connection was closed by remote host -- FIXME how do we signal that the connection was closed ? + } + else + { + return -1; //Connnection error + } + } + return readLen; +} + +int TCPSocket::close() +{ + if( m_sock < 0 ) + { + return -1; + } + + ::close(m_sock); + m_sock = -1; + + return 0; +} + +int TCPSocket::init() +{ + if( m_sock != -1 ) + { + return -1; + } + m_sock = ::socket(AF_INET, SOCK_STREAM, 0); //TCP socket + if (m_sock < 0) + { + return -1; //Could not create socket (Out of memory / available descriptors) + } + m_closedByRemoteHost = false; + return 0; +}