/* -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /*************************************************************************** * File: socket.cc * This file belongs to the Bifrost project. * Socket wrapper class implementation. * Date: Wed Sep 2 09:02:38 CEST 2009 * Author: Bent Bisballe Nyeng * Copyright: 2009 * Email: deva@aasimon.org ****************************************************************************/ #include "socket.h" #include #include #ifdef WIN32 #include #include typedef signed int ssize_t; #else #include #include #include #endif #define ADDR_IS_MULTICAST(a) IN_MULTICAST(htonl(a)) struct socket_private_t { bool close_socket; socket_t sock; struct sockaddr_in name; struct ip_mreq mreq; }; static void leave_group(socket_t sock, struct ip_mreq mreq) { int ret; ret = setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, (const char*)&mreq, sizeof(mreq)); if (ret < 0) { /* fprintf(stderr, "Failed to leave multicast group."); perror(""); */ } } #ifdef WIN32 static void wsastartup() { WORD wVersionRequested = MAKEWORD(2, 0); WSADATA wsaData; int ret = WSAStartup(wVersionRequested, &wsaData); if(ret != 0) throw Socket::WSAException(); } #endif Socket::Socket() _throw(WSAException) { prv = new struct socket_private_t(); #ifdef WIN32 wsastartup(); #endif prv->close_socket = false; } Socket::~Socket() { if(prv->close_socket) { if (ADDR_IS_MULTICAST(prv->name.sin_addr.s_addr)) { leave_group(prv->sock, prv->mreq); } #ifdef WIN32 closesocket(prv->sock); WSACleanup(); #else close(prv->sock); #endif } if(prv) delete prv; } void Socket::open(std::string address, port_t port) _throw(AddressParseException, OpenException) { prv->name.sin_family = AF_INET; prv->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if(prv->sock < 0) throw OpenException(); prv->name.sin_port = htons(port); #ifdef WIN32 prv->name.sin_addr.S_un.S_addr = inet_addr(address.c_str()); if(INADDR_NONE == prv->name.sin_addr.S_un.S_addr) throw AddressParseException(); #else prv->name.sin_addr.s_addr = inet_addr(address.c_str()); if(INADDR_NONE == prv->name.sin_addr.s_addr) throw AddressParseException(); #endif prv->close_socket = true; } void Socket::setSocketFiledescriptor(socket_t sock, std::string address, port_t port) _throw(AddressParseException) { prv->name.sin_family = AF_INET; prv->sock = sock; if(prv->sock < 0) throw OpenException(); prv->name.sin_port = htons(port); #ifdef WIN32 prv->name.sin_addr.S_un.S_addr = inet_addr(address.c_str()); if(INADDR_NONE == prv->name.sin_addr.S_un.S_addr) throw AddressParseException(); #else prv->name.sin_addr.s_addr = inet_addr(address.c_str()); if(INADDR_NONE == prv->name.sin_addr.s_addr) throw AddressParseException(); #endif prv->close_socket = false; } void Socket::setSend(unsigned char ttl) _throw(MulticastTTLException, MulticastGroupJoinException) { int ret; if (ADDR_IS_MULTICAST(prv->name.sin_addr.s_addr)) { ret = setsockopt(prv->sock, IPPROTO_IP, IP_MULTICAST_TTL, (const char*)&ttl, sizeof(ttl)); if (ret < 0) throw MulticastTTLException(); prv->mreq.imr_multiaddr.s_addr = prv->name.sin_addr.s_addr; prv->mreq.imr_interface.s_addr = htonl(INADDR_ANY); ret = setsockopt(prv->sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char*)&prv->mreq, sizeof(prv->mreq)); if (ret < 0) throw MulticastGroupJoinException(); } } void Socket::setRecv() _throw(BindException) { #ifdef WIN32 if(bind(prv->sock, (SOCKADDR*)&prv->name, sizeof(prv->name)) < 0) { // int err = WSAGetLastError(); if(prv->close_socket) closesocket(prv->sock); throw BindException(); } #else if(bind(prv->sock, (struct sockaddr *)&prv->name, sizeof(prv->name)) < 0) { if(prv->close_socket) close(prv->sock); if(ADDR_IS_MULTICAST(prv->name.sin_addr.s_addr)) { leave_group(prv->sock, prv->mreq); } throw BindException(); } #endif } size_t Socket::sendTo(void *data, size_t size) _throw(SendException) { ssize_t ret = sendto(prv->sock, (const char*) data, size, 0, (struct sockaddr *)&prv->name, sizeof(struct sockaddr_in)); if(ret == -1 || (size_t)ret != size) throw SendException(); return ret; } size_t Socket::recvFrom(void *data, size_t size) _throw(ReceiveException) { ssize_t ret; #ifdef WIN32 // unsigned long int pending; #endif // do { ret = recv(prv->sock, (char*) data, size, 0); if(ret == -1) throw ReceiveException(); #ifdef WIN32 // ioctlsocket(prv->sock, FIONREAD, &pending); // } while(pending > 0); #else // } while(recv(prv->sock, (char*) data, size, MSG_PEEK | MSG_DONTWAIT) > 0); #endif return ret; }