/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /*************************************************************************** * tcpsocket.cc * * Thu Oct 19 10:24:25 CEST 2006 * Copyright 2006 Bent Bisballe Nyeng * deva@aasimon.org ****************************************************************************/ /* * This file is part of Artefact. * * Artefact is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Artefact 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Artefact; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include "tcpsocket.h" #include "debug.h" // for gethostbyname #include // for inet_addr #include #include #include // for connect, listen, bind and accept #include #include // For socket #include #include // For TCP #include #include #include // For inet_ntoa #include #include #include // For close #include #include #include #include // For ioctl #include // For socket and friends #include #include #include #include // For fcntl #include #include TCPSocket::TCPSocket() throw(TCPSocketException) { if((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { throw TCPSocketException(strerror(errno)); } isconnected = false; } TCPSocket::~TCPSocket() { disconnect(); } static int _listen(int sockfd, int backlog){return listen(sockfd, backlog);} void TCPSocket::listen(unsigned short int port) throw(TCPListenException) { if(sock == -1) { throw TCPListenException("Socket not initialized."); } if(isconnected) { throw TCPListenException("Socket already connected."); } struct sockaddr_in socketaddr; memset((char *) &socketaddr, sizeof(socketaddr), 0); socketaddr.sin_family = AF_INET; socketaddr.sin_port = htons(port); socketaddr.sin_addr.s_addr = htonl(INADDR_ANY); if(bind(sock, (struct sockaddr*)&socketaddr, sizeof(socketaddr)) == -1) { throw TCPListenException(std::string("bind failed - ") + strerror(errno)); } if(_listen(sock, 5) == -1) { throw TCPListenException(std::string("listen failed - ") + strerror(errno)); } isconnected = true; } static int _accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {return accept(sockfd, addr, addrlen);} TCPSocket TCPSocket::accept() throw(TCPAcceptException) { TCPSocket child; if(sock == -1) { throw TCPAcceptException("Socket not initialized."); } if(!isconnected) { throw TCPAcceptException("Socket not connected."); } // accept new connection and get its connection descriptor struct sockaddr_in ssocketaddr; int csalen = sizeof(ssocketaddr); child.disconnect(); // We need to close the existing socket child.sock = _accept(sock, (struct sockaddr*)&ssocketaddr, (socklen_t*)&csalen); if (child.sock == -1) { throw TCPAcceptException(std::string("accept failed - ") + strerror(errno)); } child.isconnected = true; return child; } static int _connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen) {return connect(sockfd, serv_addr, addrlen);} void TCPSocket::connect(std::string addr, unsigned short int port) throw(TCPConnectException) { if(isconnected) { throw TCPConnectException("Socket already connected.", "", ""); } #ifndef BYPASS_STATICALLOCATIONS // Do DNS lookup char *ip; struct in_addr **addr_list; struct hostent *he; he = gethostbyname(addr.c_str()); if(!he || !he->h_length) { char portno[32]; sprintf(portno, "%d", port); throw TCPConnectException(addr, portno, std::string("host lookup failed: ") + hstrerror(h_errno)); } addr_list = (struct in_addr **)he->h_addr_list; // Get first value. We know for sure that there are at least one. ip = inet_ntoa(*addr_list[0]); #else/*BYPASS_STATICALLOCATIONS*/ char *ip = "127.0.0.1"; #endif/*BYPASS_STATICALLOCATIONS*/ struct sockaddr_in socketaddr; memset((char *) &socketaddr, sizeof(socketaddr), 0); socketaddr.sin_family = AF_INET; socketaddr.sin_port = htons(port); socketaddr.sin_addr.s_addr = inet_addr(ip); if(_connect(sock, (struct sockaddr*)&socketaddr, sizeof(socketaddr))) { char portno[32]; sprintf(portno, "%d", port); throw TCPConnectException(addr, portno, hstrerror(h_errno)); } isconnected = true; } void TCPSocket::disconnect() { if(sock != -1) { close(sock); sock = -1; } isconnected = false; } bool TCPSocket::connected() { return sock != -1 && isconnected; } int TCPSocket::read(char *buf, int size) throw(TCPReadException) { int res = 0; if(sock == -1) { throw TCPReadException("Socket not initialized."); } if(!isconnected) { throw TCPReadException("Socket is not connected."); } /* if( (res = recv(sock, buf, size, MSG_WAITALL)) == -1 ) { throw TCPReadException(strerror(errno)); } */ // Wait until something is ready to be read ( peek'a'loop ). errno = EAGAIN; while( recv(sock, buf, 1, MSG_PEEK) == -1 && errno == EAGAIN) { usleep(10); } // Now read it if( (res = recv(sock, buf, size, MSG_DONTWAIT)) == -1 ) { throw TCPReadException(strerror(errno)); } // fwrite(buf, size, 1, stdout); fflush(stdout); return res; } int TCPSocket::write(char *data, int size) throw(TCPWriteException) { if(sock == -1) { throw TCPWriteException("Socket not initialized."); } if(!isconnected) { throw TCPWriteException("Socket is not connected."); } int res; if( (res = send(sock, data, size, MSG_WAITALL)) == -1 ) { throw TCPWriteException(strerror(errno)); } return res; } int TCPSocket::write(std::string data) throw(TCPWriteException) { return write((char*)data.c_str(), data.length()); } std::string TCPSocket::srcaddr() throw(TCPNameException) { std::string addr; #ifndef BYPASS_STATICALLOCATIONS struct sockaddr_in name; socklen_t namelen = sizeof(name); if(getpeername(sock, (sockaddr*)&name, &namelen) == -1) { throw TCPNameException(strerror(errno)); } addr = inet_ntoa(name.sin_addr); #else/*BYPASS_STATICALLOCATIONS*/ addr = "127.0.0.1"; #endif/*BYPASS_STATICALLOCATIONS*/ return addr; } std::string TCPSocket::dstaddr() throw(TCPNameException) { std::string addr; #ifndef BYPASS_STATICALLOCATIONS struct sockaddr_in name; socklen_t namelen = sizeof(name); if(getsockname(sock, (sockaddr*)&name, &namelen) == -1) { throw TCPNameException(strerror(errno)); } addr = inet_ntoa(name.sin_addr); #else/*BYPASS_STATICALLOCATIONS*/ addr = "127.0.0.1"; #endif/*BYPASS_STATICALLOCATIONS*/ return addr; } #ifdef TEST_TCPSOCKET int main() { char buf[32]; switch(fork()) { case -1: // error fprintf(stderr, "Could not fork: %s\n", strerror(errno)); return 1; case 0: // child try { TCPSocket client; sleep(1); // We need to wait for the listen socket to be created. client.connect("localhost", 12345); sprintf(buf, "hello"); client.write(buf, sizeof(buf)); printf("Sent: [%s]\n", buf); } catch( Exception &e ) { fprintf(stderr, "%s\n", e.what()); return 1; } break; default: // parent try { TCPSocket listen_sock; listen_sock.listen(12345); TCPSocket sock = listen_sock.accept(); sock.read(buf, sizeof(buf)); printf("Got: [%s]\n", buf); if(std::string(buf) != "hello") return 1; } catch( Exception &e ) { fprintf(stderr, "%s\n", e.what()); return 1; } break; } return 0; } #endif/*TEST_TCPSOCKET*/