1 /***************************************************************************
3 * Copyright (C) 2006 David Brodsky *
5 * This library is free software; you can redistribute it and/or *
6 * modify it under the terms of the GNU Library General Public *
7 * License as published by the Free Software Foundation and appearing *
8 * in the file LICENSE.LGPL included in the packaging of this file. *
10 * This library is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
13 * Library General Public License for more details. *
15 ***************************************************************************/
22 #include <tairon/core/log.h>
28 #include "socketnotifier.h"
36 const size_t Socket::bufLength
= 65536;
38 /* {{{ Socket::Socket(Family, Type) */
39 Socket::Socket(Family family
, Type type
)
41 fd
= socket(family
, type
, 0);
43 throw SocketException("Cannot create socket", errno
);
49 /* {{{ Socket::Socket(int sd) */
50 Socket::Socket(int sd
) : fd(sd
)
52 if (fcntl(fd
, F_SETFL
, fcntl(fd
, F_GETFL
, 0) | O_NONBLOCK
) == -1)
53 throw SocketException("Cannot set nonblocking socket", errno
);
57 notifier
->setReadyReadFunctor(Tairon::Core::methodFunctor(this, &Socket::readyRead
));
58 notifier
->setReadyWriteFunctor(Tairon::Core::methodFunctor(this, &Socket::readyWrite
));
62 /* {{{ Socket::~Socket() */
65 // deleting notifier also deletes all created functors
71 /* {{{ Socket::close() */
74 connectedSignal
.disable();
75 errorSignal
.disable();
76 readyReadSignal
.disable();
77 readyWriteSignal
.disable();
79 // this also removes socket from polling
82 Poll::self()->addDeleter(createDeleter(this));
86 /* {{{ Socket::connected() */
87 void Socket::connected()
89 DEBUG((const char *) String("Socket::connected " + String::number(fd
)));
91 Poll::self()->unregisterSocket(fd
);
92 delete notifier
->getReadyWriteFunctor();
93 notifier
->setReadyWriteFunctor(Tairon::Core::methodFunctor(this, &Socket::readyWrite
));
96 socklen_t len
= sizeof(err
);
97 getsockopt(fd
, SOL_SOCKET
, SO_ERROR
, &err
, &len
);
99 notifier
->setReadyReadFunctor(Tairon::Core::methodFunctor(this, &Socket::readyRead
));
100 // don't register socket for reading, wait for the ready method
101 connectedSignal
.emit(this);
103 errorSignal
.emit(this, err
);
107 /* {{{ Socket::connect(const String &) */
108 void Socket::connect(const String
&path
)
110 if (path
.length() >= 108) // length of struct socaddr_un.sun_path field
111 throw SocketException("Path for unix socket is too long", 0);
113 if (fcntl(fd
, F_SETFL
, fcntl(fd
, F_GETFL
, 0) | O_NONBLOCK
) == -1)
114 throw SocketException("Cannot set nonblocking socket", errno
);
117 addr
.sun_family
= AF_UNIX
;
118 memcpy(addr
.sun_path
, path
.data(), path
.length());
119 addr
.sun_path
[path
.length()] = 0;
121 int ret
= ::connect(fd
, (sockaddr
*) &addr
, sizeof(addr
));
122 if ((ret
== -1) && (errno
!= EINPROGRESS
))
123 throw SocketException("Error while connecting", errno
);
125 notifier
->setReadyWriteFunctor(Tairon::Core::methodFunctor(this, &Socket::connected
));
127 Poll::self()->registerSocketToWrite(fd
, notifier
);
131 /* {{{ Socket::connect(const String &, uint16_t) */
132 void Socket::connect(const String
&address
, uint16_t port
)
134 if (fcntl(fd
, F_SETFL
, fcntl(fd
, F_GETFL
, 0) | O_NONBLOCK
) == -1)
135 throw SocketException("Cannot set nonblocking socket", errno
);
137 struct hostent
*host
= gethostbyname(address
);
139 throw SocketException("Cannot resolve host name", h_errno
);
141 if (host
->h_addrtype
!= AF_INET
)
142 throw SocketException("Invalid address type", host
->h_addrtype
);
144 if (!(*host
->h_addr_list
))
145 throw SocketException("Zero length list of addresses", 0);
148 addr
.sin_family
= AF_INET
;
149 addr
.sin_port
= htons(port
);
150 memcpy(&(addr
.sin_addr
.s_addr
), *(host
->h_addr_list
), sizeof(addr
.sin_addr
.s_addr
));
152 int ret
= ::connect(fd
, (sockaddr
*) &addr
, sizeof(addr
));
153 if ((ret
== -1) && (errno
!= EINPROGRESS
))
154 throw SocketException("Error while connecting", errno
);
156 notifier
->setReadyWriteFunctor(Tairon::Core::methodFunctor(this, &Socket::connected
));
158 Poll::self()->registerSocketToWrite(fd
, notifier
);
162 /* {{{ Socket::error() */
166 socklen_t len
= sizeof(err
);
167 getsockopt(fd
, SOL_SOCKET
, SO_ERROR
, &err
, &len
);
168 Poll::self()->unregisterSocket(fd
);
169 errorSignal
.emit(this, err
);
173 /* {{{ Socket::init() */
176 inBuffer
= new char[bufLength
];
177 notifier
= new SocketNotifier(0, 0, Tairon::Core::methodFunctor(this, &Socket::error
));
181 /* {{{ Socket::read(size_t) */
182 String
Socket::read(size_t max
)
184 ssize_t len
= recv(fd
, inBuffer
, bufLength
< max
? bufLength
: max
, MSG_DONTWAIT
);
185 if (len
== 0) // connection has been closed
186 throw SocketException("Connection has been closed", 0);
188 if (errno
!= EAGAIN
) {
189 throw SocketException("Error while reading from socket", errno
);
193 Poll::self()->addSocketToRead(fd
, notifier
);
194 } catch (const PollException
&) { // do nothing
196 return String(inBuffer
, len
);
200 /* {{{ Socket::readyRead() */
201 void Socket::readyRead()
203 DEBUG((const char *) String("Socket::readyRead " + String::number((int) this)));
205 // don't make signal storm - if processing of the signals takes much time
206 // in another thread, then there can be new data and many new signals would
209 Poll::self()->delSocketToRead(fd
, notifier
);
210 } catch (const PollException
&) {
212 readyReadSignal
.emit(this);
216 /* {{{ Socket::readTo(void *, size_t) */
217 size_t Socket::readTo(void *buf
, size_t length
)
219 ssize_t len
= recv(fd
, buf
, length
, MSG_DONTWAIT
);
220 if (len
== 0) // connection has been closed
221 throw SocketException("Connection has been closed", 0);
224 throw SocketException("Error while reading from socket", errno
);
228 Poll::self()->addSocketToRead(fd
, notifier
);
229 } catch (const PollException
&) { // do nothing
235 /* {{{ Socket::ready() */
238 Poll::self()->registerSocketToRead(fd
, notifier
);
242 /* {{{ Socket::readyWrite() */
243 void Socket::readyWrite()
245 Poll::self()->delSocketToWrite(fd
, notifier
);
246 readyWriteSignal
.emit(this);
250 /* {{{ Socket::write(const String &, bool) */
251 size_t Socket::write(const String
&data
, bool block
)
253 return write(data
.data(), data
.length(), block
);
257 /* {{{ Socket::write(const char *, size_t, bool) */
258 size_t Socket::write(const char *data
, size_t length
, bool block
)
260 ssize_t sent
= send(fd
, data
, length
, block
? 0 : MSG_DONTWAIT
);
263 throw SocketException("Error while writing to socket", errno
);
265 Poll::self()->addSocketToWrite(fd
, notifier
);
266 } catch (const PollException
&) { // do nothing
275 /* {{{ SocketException::SocketException(const String &, int) */
276 SocketException::SocketException(const String
&desc
, int err
) : Tairon::Core::Exception(desc
), errorNumber(err
)
278 DEBUG((const char *) String(desc
+ " (" + String::number(errorNumber
) + ")"));
286 // vim: ai sw=4 ts=4 noet fdm=marker