/*----------------------------------------------------------------------------- * udpsocket.cpp * Copyright (C) 2004, 2005 Akito Nozaki * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include NAMESPACE_TANOSHI { UDPSocket::UDPSocket(Object *parent) : DataGram(parent), d(new UDPSocketPrivate) { d->socket = 0; d->localaddress.setAddress(0); d->localport = 0; reset(); } UDPSocket::~UDPSocket() { reset(true); delete d; } void UDPSocket::setEventManager(EventManager *e) { DataGram::setEventManager(e); } void UDPSocket::reset(bool clear) { if(d->socket) { if(!clear && d->state == Connected) { // ? } ::close(d->socket); d->socket = 0; // setSafeToDelete(false); // connectionClosed(); // setSafeToDelete(true); } else { if(clear) { clearReadBuffer(); clearWriteBuffer(); } } removeFromEvent(); d->state = Idle; } int UDPSocket::socket() const { return d->socket; } void UDPSocket::setSocket(int s) { reset(true); d->state = Connected; d->socket = s; int flags = fcntl(d->socket, F_GETFL, 0); fcntl(d->socket, F_SETFL, flags | O_NONBLOCK); setFd(d->socket); setEvents(POLLIN); addToEvent(); } int UDPSocket::state() const { return d->state; } bool UDPSocket::isOpen() const { return (d->state == Connected) ? true : false; } void UDPSocket::close() { if(d->state == Idle) return; if(d->socket) { d->state = Closing; if(packetsToWrite()) reset(); } else reset(); } void UDPSocket::write(const HostAddress &ha, unsigned int port, const ByteArray &data) { if(d->state != Connected) return; if(data.size()) { UDPPacket *t = new UDPPacket; t->setHostAddress(ha); t->setPort(port); t->setData(data); appendWrite(t); setEvents(POLLIN | POLLOUT); addToEvent(); } } void UDPSocket::write(const HostAddress &ha, unsigned int port, const std::string &data) { if(d->state != Connected) return; if(data.size()) { UDPPacket *t = new UDPPacket; t->setHostAddress(ha); t->setPort(port); t->setData(ByteArray(data.c_str(), data.length())); appendWrite(t); setEvents(POLLIN | POLLOUT); addToEvent(); } } HostAddress UDPSocket::localAddress() const { return d->localaddress; } void UDPSocket::setLocalAddress(const HostAddress &_localaddress) { d->localaddress = _localaddress; } void UDPSocket::setLocalPort(unsigned short port) { d->localport = port; } unsigned short UDPSocket::localPort() const { return d->localport; } bool UDPSocket::bind(const HostAddress &ip, unsigned short port) { if(d->socket == 0) return false; d->localport = port; d->localaddress = ip; struct sockaddr_in localaddr; memset(&(localaddr.sin_zero), 0, 8); localaddr.sin_family = AF_INET; localaddr.sin_port = htons(d->localport); localaddr.sin_addr.s_addr = htonl(d->localaddress.toIPv4Address()); if(::bind(d->socket, (struct sockaddr *)&localaddr, sizeof(localaddr)) == -1) { reset(); error(ErrConnectionRefused); return false; } } bool UDPSocket::listen() { d->socket = 0; if( (d->socket = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { error(ErrSocket); return false; } int yes = 1; if(setsockopt(d->socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { d->socket = 0; error(ErrSocket); return false; } int flags = fcntl(d->socket, F_GETFL, 0); fcntl(d->socket, F_SETFL, flags | O_NONBLOCK); setFd(d->socket); setEvents(POLLIN); addToEvent(); d->state = Connected; setSleep(-1); connected(); return true; } void UDPSocket::sn_read() { struct sockaddr_in from; unsigned int slen = sizeof(from); char buffer[100000]; int read = ::recvfrom(d->socket, buffer, 10000, 0, (struct sockaddr *)&from, &slen); if(read < 0) { error(ErrSocketRead); return; } else if(read > 0) { // Generate Data Gram :) UDPPacket *t = new UDPPacket; t->setHostAddress(from.sin_addr.s_addr); t->setPort(ntohs(from.sin_port)); t->setData(ByteArray(buffer, read)); appendRead(t); readyRead(); } } void UDPSocket::sn_write() { if(d->state != Connected && d->state != Closing) return; std::auto_ptr t((UDPPacket *)takeWrite()); struct sockaddr_in peeraddr; memset(&(peeraddr.sin_zero), 0, 8); peeraddr.sin_family = AF_INET; peeraddr.sin_port = htons(t->port()); peeraddr.sin_addr.s_addr = htonl(t->hostAddress().toIPv4Address()); unsigned int slen = sizeof(peeraddr); ByteArray temp = t->data(); int written = 0; written = ::sendto(d->socket, temp.data(), temp.size(), 0, (struct sockaddr *)&peeraddr, slen); //printf("Writen: %d bytes -> %s %d\n", written, t->hostAddress().toString().c_str(), t->port()); if(written == -1) { error(ErrSocketWrite); return; } if(!packetsToWrite()) { setEvents(POLLIN); addToEvent(); } packetWritten(1); if(d->state == Closing && !packetsToWrite()) { reset(); removeFromEvent(); connectionClosed(); } } bool UDPSocket::event(Event *e) { if(e->id() != id()) return false; setSafeToDelete(false); if(d->state != Idle) { if(d->state != Idle) { if(revents() & POLLOUT) sn_write(); if(revents() & POLLIN) sn_read(); setSleep(-1); } } setSafeToDelete(true); return true; } }