Set nonblocking flag in the constructor of already created socket.
[tairon.git] / src / net-core / socket.cpp
blob56cc6fd836b5bcf83f43da0f1d6add1ce320844f
1 /***************************************************************************
2 * *
3 * Copyright (C) 2006 David Brodsky *
4 * *
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. *
9 * *
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. *
14 * *
15 ***************************************************************************/
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <netdb.h>
21 #include <tairon/core/log.h>
23 #include "socket.h"
25 #include "deleter.h"
26 #include "poll.h"
27 #include "socketnotifier.h"
29 namespace Tairon
32 namespace Net
35 const size_t Socket::bufLength = 65536;
37 /* {{{ Socket::Socket(Type) */
38 Socket::Socket(Type type)
40 fd = socket(PF_INET, type, 0);
41 if (fd == -1)
42 throw SocketException("Cannot create socket", errno);
44 init();
46 /* }}} */
48 /* {{{ Socket::Socket(int sd) */
49 Socket::Socket(int sd) : fd(sd)
51 if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK) == -1)
52 throw SocketException("Cannot set nonblocking socket", errno);
54 init();
56 notifier->setReadyReadFunctor(Tairon::Core::methodFunctor(this, &Socket::readyRead));
57 notifier->setReadyWriteFunctor(Tairon::Core::methodFunctor(this, &Socket::readyWrite));
59 /* }}} */
61 /* {{{ Socket::~Socket() */
62 Socket::~Socket()
64 // deleting notifier also deletes all created functors
65 delete notifier;
66 delete [] inBuffer;
68 /* }}} */
70 /* {{{ Socket::close() */
71 void Socket::close()
73 connectedSignal.disable();
74 errorSignal.disable();
75 readyReadSignal.disable();
76 readyWriteSignal.disable();
78 // this also removes socket from polling
79 ::close(fd);
81 Poll::self()->addDeleter(createDeleter(this));
83 /* }}} */
85 /* {{{ Socket::connected() */
86 void Socket::connected()
88 DEBUG((const char *) String("Socket::connected " + String::number(fd)));
90 Poll::self()->delSocketToWrite(fd, notifier);
91 delete notifier->getReadyWriteFunctor();
92 notifier->setReadyWriteFunctor(Tairon::Core::methodFunctor(this, &Socket::readyWrite));
94 int err;
95 socklen_t len = sizeof(err);
96 getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len);
97 if (err == 0) {
98 notifier->setReadyReadFunctor(Tairon::Core::methodFunctor(this, &Socket::readyRead));
99 Poll::self()->addSocketToRead(fd, notifier);
100 connectedSignal.emit(this);
101 } else
102 errorSignal.emit(this, err);
104 /* }}} */
106 /* {{{ Socket::connect(const String &, uint16_t) */
107 void Socket::connect(const String &address, uint16_t port)
109 if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK) == -1)
110 throw SocketException("Cannot set nonblocking socket", errno);
112 struct hostent *host = gethostbyname(address);
113 if (!host)
114 throw SocketException("Cannot resolve host name", h_errno);
116 if (host->h_addrtype != AF_INET)
117 throw SocketException("Invalid address type", host->h_addrtype);
119 if (!(*host->h_addr_list))
120 throw SocketException("Zero length list of addresses", 0);
122 sockaddr_in addr;
123 addr.sin_family = AF_INET;
124 addr.sin_port = htons(port);
125 memcpy(&(addr.sin_addr.s_addr), *(host->h_addr_list), sizeof(addr.sin_addr.s_addr));
127 int ret = ::connect(fd, (sockaddr *) &addr, sizeof(addr));
128 if ((ret == -1) && (errno != EINPROGRESS))
129 throw SocketException("Error while connecting", errno);
131 notifier->setReadyWriteFunctor(Tairon::Core::methodFunctor(this, &Socket::connected));
133 Poll::self()->registerSocketToWrite(fd, notifier);
135 /* }}} */
137 /* {{{ Socket::error() */
138 void Socket::error()
140 int err;
141 socklen_t len = sizeof(err);
142 getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len);
143 Poll::self()->unregisterSocket(fd);
144 errorSignal.emit(this, err);
146 /* }}} */
148 /* {{{ Socket::init() */
149 void Socket::init()
151 inBuffer = new char[bufLength];
152 notifier = new SocketNotifier(0, 0, Tairon::Core::methodFunctor(this, &Socket::error));
154 /* }}} */
156 /* {{{ Socket::read(size_t) */
157 String Socket::read(size_t max)
159 ssize_t len = recv(fd, inBuffer, bufLength < max ? bufLength : max, MSG_DONTWAIT);
160 if (len == 0) // connection has been closed
161 throw SocketException("Connection has been closed", 0);
162 else if (len < 0)
163 if (errno != EAGAIN) {
164 throw SocketException("Error while reading from socket", errno);
165 } else
166 len = 0;
167 Poll::self()->addSocketToRead(fd, notifier);
168 return String(inBuffer, len);
170 /* }}} */
172 /* {{{ Socket::readyRead() */
173 void Socket::readyRead()
175 DEBUG((const char *) String("Socket::readyRead " + String::number((int) this)));
177 // don't make signal storm - if processing of the signals takes much time
178 // in another thread, then there can be new data and many new signals would
179 // be emitted
180 Poll::self()->delSocketToRead(fd, notifier);
181 readyReadSignal.emit(this);
183 /* }}} */
185 /* {{{ Socket::readTo(void *, size_t) */
186 size_t Socket::readTo(void *buf, size_t length)
188 size_t len = recv(fd, buf, length, MSG_DONTWAIT);
189 if (len == 0) // connection has been closed
190 throw SocketException("Connection has been closed", 0);
191 else if (len < 0)
192 if (errno != EAGAIN)
193 throw SocketException("Error while reading from socket", errno);
194 else
195 len = 0;
196 Poll::self()->addSocketToRead(fd, notifier);
197 return len;
199 /* }}} */
201 /* {{{ Socket::ready() */
202 void Socket::ready()
204 Poll::self()->registerSocketToRead(fd, notifier);
206 /* }}} */
208 /* {{{ Socket::readyWrite() */
209 void Socket::readyWrite()
211 Poll::self()->delSocketToWrite(fd, notifier);
212 readyWriteSignal.emit(this);
214 /* }}} */
216 /* {{{ Socket::write(const String &, bool) */
217 size_t Socket::write(const String &data, bool block)
219 return write(data.data(), data.length(), block);
221 /* }}} */
223 /* {{{ Socket::write(const char *, size_t, bool) */
224 size_t Socket::write(const char *data, size_t length, bool block)
226 ssize_t sent = send(fd, data, length, block ? 0 : MSG_DONTWAIT);
227 if (sent == -1) {
228 if (errno != EAGAIN)
229 throw SocketException("Error while writing to socket", errno);
230 Poll::self()->addSocketToWrite(fd, notifier);
231 return 0;
233 return sent;
235 /* }}} */
238 /* {{{ SocketException::SocketException(const String &, int) */
239 SocketException::SocketException(const String &desc, int err) : Tairon::Core::Exception(desc), errorNumber(err)
241 DEBUG((const char *) String(desc + " (" + String::number(errorNumber) + ")"));
243 /* }}} */
249 // vim: ai sw=4 ts=4 noet fdm=marker