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
)
54 notifier
->setReadyReadFunctor(Tairon::Core::methodFunctor(this, &Socket::readyRead
));
55 notifier
->setReadyWriteFunctor(Tairon::Core::methodFunctor(this, &Socket::readyWrite
));
59 /* {{{ Socket::~Socket() */
62 // deleting notifier also deletes all created functors
68 /* {{{ Socket::close() */
71 connectedSignal
.disable();
72 errorSignal
.disable();
73 readyReadSignal
.disable();
74 readyWriteSignal
.disable();
76 // this also removes socket from polling
79 Poll::self()->addDeleter(createDeleter(this));
83 /* {{{ Socket::connected() */
84 void Socket::connected()
86 DEBUG((const char *) String("Socket::connected " + String::number(fd
)));
88 fcntl(fd
, F_SETFL
, fcntl(fd
, F_GETFL
, 0) & (!O_NONBLOCK
));
90 Poll::self()->unregisterSocket(fd
);
91 delete notifier
->getReadyWriteFunctor();
92 notifier
->setReadyWriteFunctor(Tairon::Core::methodFunctor(this, &Socket::readyWrite
));
95 socklen_t len
= sizeof(err
);
96 getsockopt(fd
, SOL_SOCKET
, SO_ERROR
, &err
, &len
);
98 notifier
->setReadyReadFunctor(Tairon::Core::methodFunctor(this, &Socket::readyRead
));
99 // don't register socket for reading, wait for the ready method
100 connectedSignal
.emit(this);
102 errorSignal
.emit(this, err
);
106 /* {{{ Socket::connect(const String &) */
107 void Socket::connect(const String
&path
)
109 if (path
.length() >= 108) // length of struct socaddr_un.sun_path field
110 throw SocketException("Path for unix socket is too long", 0);
112 if (fcntl(fd
, F_SETFL
, fcntl(fd
, F_GETFL
, 0) | O_NONBLOCK
) == -1)
113 throw SocketException("Cannot set nonblocking socket", errno
);
116 addr
.sun_family
= AF_UNIX
;
117 memcpy(addr
.sun_path
, path
.data(), path
.length());
118 addr
.sun_path
[path
.length()] = 0;
120 int ret
= ::connect(fd
, (sockaddr
*) &addr
, sizeof(addr
));
121 if ((ret
== -1) && (errno
!= EINPROGRESS
))
122 throw SocketException("Error while connecting", errno
);
124 notifier
->setReadyWriteFunctor(Tairon::Core::methodFunctor(this, &Socket::connected
));
126 Poll::self()->registerSocketToWrite(fd
, notifier
);
130 /* {{{ Socket::connect(const String &, uint16_t) */
131 void Socket::connect(const String
&address
, uint16_t port
)
133 if (fcntl(fd
, F_SETFL
, fcntl(fd
, F_GETFL
, 0) | O_NONBLOCK
) == -1)
134 throw SocketException("Cannot set nonblocking socket", errno
);
136 struct hostent
*host
= gethostbyname(address
);
138 throw SocketException("Cannot resolve host name", h_errno
);
140 if (host
->h_addrtype
!= AF_INET
)
141 throw SocketException("Invalid address type", host
->h_addrtype
);
143 if (!(*host
->h_addr_list
))
144 throw SocketException("Zero length list of addresses", 0);
147 addr
.sin_family
= AF_INET
;
148 addr
.sin_port
= htons(port
);
149 memcpy(&(addr
.sin_addr
.s_addr
), *(host
->h_addr_list
), sizeof(addr
.sin_addr
.s_addr
));
151 int ret
= ::connect(fd
, (sockaddr
*) &addr
, sizeof(addr
));
152 if ((ret
== -1) && (errno
!= EINPROGRESS
))
153 throw SocketException("Error while connecting", errno
);
155 notifier
->setReadyWriteFunctor(Tairon::Core::methodFunctor(this, &Socket::connected
));
157 Poll::self()->registerSocketToWrite(fd
, notifier
);
161 /* {{{ Socket::error() */
165 socklen_t len
= sizeof(err
);
166 getsockopt(fd
, SOL_SOCKET
, SO_ERROR
, &err
, &len
);
167 Poll::self()->unregisterSocket(fd
);
168 errorSignal
.emit(this, err
);
172 /* {{{ Socket::init() */
175 inBuffer
= new char[bufLength
];
176 notifier
= new SocketNotifier(0, 0, Tairon::Core::methodFunctor(this, &Socket::error
));
180 /* {{{ Socket::read(size_t) */
181 String
Socket::read(size_t max
)
183 ssize_t len
= recv(fd
, inBuffer
, bufLength
< max
? bufLength
: max
, MSG_DONTWAIT
);
184 if (len
== 0) // connection has been closed
185 throw SocketException("Connection has been closed", 0);
187 if ((errno
!= EAGAIN
) && (errno
!= EINTR
)) {
188 throw SocketException("Error while reading from socket", errno
);
192 Poll::self()->addSocketToRead(fd
, notifier
);
193 } catch (const PollException
&) { // do nothing
195 return String(inBuffer
, len
);
199 /* {{{ Socket::readyRead() */
200 void Socket::readyRead()
202 DEBUG((const char *) String("Socket::readyRead " + String::number((int) this)));
204 // don't make signal storm - if processing of the signals takes much time
205 // in another thread, then there can be new data and many new signals would
208 Poll::self()->delSocketToRead(fd
, notifier
);
209 } catch (const PollException
&) {
211 readyReadSignal
.emit(this);
215 /* {{{ Socket::readTo(void *, size_t) */
216 size_t Socket::readTo(void *buf
, size_t length
)
218 ssize_t len
= recv(fd
, buf
, length
, MSG_DONTWAIT
);
219 if (len
== 0) // connection has been closed
220 throw SocketException("Connection has been closed", 0);
222 if ((errno
!= EAGAIN
) && (errno
!= EINTR
))
223 throw SocketException("Error while reading from socket", errno
);
227 Poll::self()->addSocketToRead(fd
, notifier
);
228 } catch (const PollException
&) { // do nothing
234 /* {{{ Socket::ready() */
237 Poll::self()->registerSocketToRead(fd
, notifier
);
241 /* {{{ Socket::readyWrite() */
242 void Socket::readyWrite()
244 Poll::self()->delSocketToWrite(fd
, notifier
);
245 readyWriteSignal
.emit(this);
249 /* {{{ Socket::write(const String &, bool) */
250 size_t Socket::write(const String
&data
, bool block
)
252 return write(data
.data(), data
.length(), block
);
256 /* {{{ Socket::write(const char *, size_t, bool) */
257 size_t Socket::write(const char *data
, size_t length
, bool block
)
259 ssize_t sent
= send(fd
, data
, length
, block
? 0 : MSG_DONTWAIT
);
261 if ((errno
!= EAGAIN
) && (errno
!= EINTR
))
262 throw SocketException("Error while writing to socket", errno
);
264 Poll::self()->addSocketToWrite(fd
, notifier
);
265 } catch (const PollException
&) { // do nothing
274 /* {{{ SocketException::SocketException(const String &, int) */
275 SocketException::SocketException(const String
&desc
, int err
) : Tairon::Core::Exception(desc
), errorNumber(err
)
277 DEBUG((const char *) String(desc
+ " (" + String::number(errorNumber
) + ")"));
285 // vim: ai sw=4 ts=4 noet fdm=marker