tcp_listener.cpp 8.34 KB
Newer Older
Martin Sustrik's avatar
Martin Sustrik committed
1
/*
2
    Copyright (c) 2007-2010 iMatix Corporation
Martin Sustrik's avatar
Martin Sustrik committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

    This file is part of 0MQ.

    0MQ is free software; you can redistribute it and/or modify it under
    the terms of the Lesser GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    0MQ 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
    Lesser GNU General Public License for more details.

    You should have received a copy of the Lesser GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

20 21
#include <string.h>

unknown's avatar
unknown committed
22 23
#include "../bindings/c/zmq.h"

Martin Sustrik's avatar
Martin Sustrik committed
24 25 26 27 28 29
#include "tcp_listener.hpp"
#include "platform.hpp"
#include "ip.hpp"
#include "config.hpp"
#include "err.hpp"

Martin Sustrik's avatar
Martin Sustrik committed
30
#ifdef ZMQ_HAVE_WINDOWS
Martin Sustrik's avatar
Martin Sustrik committed
31

32 33 34 35
zmq::tcp_listener_t::tcp_listener_t () :
    s (retired_fd)
{
    memset (&addr, 0, sizeof (addr));
Martin Sustrik's avatar
Martin Sustrik committed
36
    addr_len = 0;
37 38 39 40 41 42 43 44
}

zmq::tcp_listener_t::~tcp_listener_t ()
{
    if (s != retired_fd)
        close ();
}

unknown's avatar
unknown committed
45
int zmq::tcp_listener_t::set_address (const char *protocol_, const char *addr_)
46
{
47 48 49 50 51 52
    //  IPC protocol is not supported on Windows platform.
    if (strcmp (protocol_, "tcp") != 0 ) {
        errno = EPROTONOSUPPORT;
        return -1;
    }

53
    //  Convert the interface into sockaddr_in structure.
Martin Sustrik's avatar
Martin Sustrik committed
54
    int rc = resolve_ip_interface (&addr, &addr_len, addr_);
55 56 57 58
    if (rc != 0)
        return rc;

    //  Create a listening socket.
59
    s = socket (addr.ss_family, SOCK_STREAM, IPPROTO_TCP);
60 61 62 63
    if (s == INVALID_SOCKET) {
        wsa_error_to_errno ();
        return -1;
    }
64 65 66 67 68 69 70 71 72 73 74 75 76

    //  Allow reusing of the address.
    int flag = 1;
    rc = setsockopt (s, SOL_SOCKET, SO_REUSEADDR,
        (const char*) &flag, sizeof (int));
    wsa_assert (rc != SOCKET_ERROR);

    //  Set the non-blocking flag.
    flag = 1;
    rc = ioctlsocket (s, FIONBIO, (u_long*) &flag);
    wsa_assert (rc != SOCKET_ERROR);

    //  Bind the socket to the network interface and port.
Martin Sustrik's avatar
Martin Sustrik committed
77
    rc = bind (s, (struct sockaddr*) &addr, addr_len);
78 79 80 81
    if (rc == SOCKET_ERROR) {
        wsa_error_to_errno ();
        return -1;
    }
82 83 84

    //  Listen for incomming connections.
    rc = listen (s, 1);
85 86 87 88
    if (rc == SOCKET_ERROR) {
        wsa_error_to_errno ();
        return -1;
    }
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132

    return 0;
}

int zmq::tcp_listener_t::close ()
{
    zmq_assert (s != retired_fd);
    int rc = closesocket (s);
    wsa_assert (rc != SOCKET_ERROR);
    s = retired_fd;
    return 0;
}

zmq::fd_t zmq::tcp_listener_t::get_fd ()
{
    return s;
}

zmq::fd_t zmq::tcp_listener_t::accept ()
{
    zmq_assert (s != retired_fd);

    //  Accept one incoming connection.
    fd_t sock = ::accept (s, NULL, NULL);
    if (sock == INVALID_SOCKET && 
          (WSAGetLastError () == WSAEWOULDBLOCK ||
          WSAGetLastError () == WSAECONNRESET))
        return retired_fd;

    zmq_assert (sock != INVALID_SOCKET);

    // Set to non-blocking mode.
    unsigned long argp = 1;
    int rc = ioctlsocket (sock, FIONBIO, &argp);
    wsa_assert (rc != SOCKET_ERROR);

    //  Disable Nagle's algorithm.
    int flag = 1;
    rc = setsockopt (sock, IPPROTO_TCP, TCP_NODELAY, (char*) &flag,
        sizeof (int));
    wsa_assert (rc != SOCKET_ERROR);

    return sock;
}
Martin Sustrik's avatar
Martin Sustrik committed
133 134 135 136 137 138 139 140 141 142

#else

#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
143
#include <sys/un.h>
Martin Sustrik's avatar
Martin Sustrik committed
144

Martin Sustrik's avatar
Martin Sustrik committed
145
zmq::tcp_listener_t::tcp_listener_t () :
Martin Sustrik's avatar
Martin Sustrik committed
146 147
    s (retired_fd)
{
148
    memset (&addr, 0, sizeof (addr));
Martin Sustrik's avatar
Martin Sustrik committed
149 150
}

Martin Sustrik's avatar
Martin Sustrik committed
151
zmq::tcp_listener_t::~tcp_listener_t ()
Martin Sustrik's avatar
Martin Sustrik committed
152 153 154 155 156
{
    if (s != retired_fd)
        close ();
}

157
int zmq::tcp_listener_t::set_address (const char *protocol_, const char *addr_)
Martin Sustrik's avatar
Martin Sustrik committed
158
{
159 160
    if (strcmp (protocol_, "tcp") == 0 ) {

161
        //  Resolve the sockaddr to bind to.
Martin Sustrik's avatar
Martin Sustrik committed
162
        int rc = resolve_ip_interface (&addr, &addr_len, addr_);
163 164 165 166
        if (rc != 0)
            return -1;

        //  Create a listening socket.
167
        s = socket (addr.ss_family, SOCK_STREAM, IPPROTO_TCP);
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
        if (s == -1)
            return -1;

        //  Allow reusing of the address.
        int flag = 1;
        rc = setsockopt (s, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof (int));
        errno_assert (rc == 0);

        //  Set the non-blocking flag.
        flag = fcntl (s, F_GETFL, 0);
        if (flag == -1) 
            flag = 0;
        rc = fcntl (s, F_SETFL, flag | O_NONBLOCK);
        errno_assert (rc != -1);

        //  Bind the socket to the network interface and port.
Martin Sustrik's avatar
Martin Sustrik committed
184
        rc = bind (s, (struct sockaddr*) &addr, addr_len);
185 186 187 188 189 190 191 192 193 194 195 196 197
        if (rc != 0) {
            close ();
            return -1;
        }

        //  Listen for incomming connections.
        rc = listen (s, tcp_connection_backlog);
        if (rc != 0) {
            close ();
            return -1;
        }

        return 0;
Martin Sustrik's avatar
Martin Sustrik committed
198
    }
199 200 201 202 203 204 205
    else if (strcmp (protocol_, "ipc") == 0) {

        //  Get rid of the file associated with the UNIX domain socket that
        //  may have been left behind by the previous run of the application.
        ::unlink (addr_);

        //  Convert the address into sockaddr_un structure.
Martin Sustrik's avatar
Martin Sustrik committed
206
        int rc = resolve_local_path (&addr, &addr_len, addr_);
207 208 209 210
        if (rc != 0)
            return -1;

        //  Create a listening socket.
Martin Sustrik's avatar
Martin Sustrik committed
211
        s = socket (AF_UNIX, SOCK_STREAM, 0);
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
        if (s == -1)
            return -1;

        //  Set the non-blocking flag.
        int flag = fcntl (s, F_GETFL, 0);
        if (flag == -1) 
            flag = 0;
        rc = fcntl (s, F_SETFL, flag | O_NONBLOCK);
        errno_assert (rc != -1);

        //  Bind the socket to the file path.
        rc = bind (s, (struct sockaddr*) &addr, sizeof (sockaddr_un));
        if (rc != 0) {
            close ();
            return -1;
        }

        //  Listen for incomming connections.
        rc = listen (s, tcp_connection_backlog);
        if (rc != 0) {
            close ();
            return -1;
        }

        return 0;
Martin Sustrik's avatar
Martin Sustrik committed
237
    }
238 239 240 241
    else {
        errno = EPROTONOSUPPORT;
        return -1;
    }    
Martin Sustrik's avatar
Martin Sustrik committed
242 243
}

Martin Sustrik's avatar
Martin Sustrik committed
244
int zmq::tcp_listener_t::close ()
Martin Sustrik's avatar
Martin Sustrik committed
245
{
Martin Sustrik's avatar
Martin Sustrik committed
246
    zmq_assert (s != retired_fd);
Martin Sustrik's avatar
Martin Sustrik committed
247 248 249 250
    int rc = ::close (s);
    if (rc != 0)
        return -1;
    s = retired_fd;
251 252 253

    //  If there's an underlying UNIX domain socket, get rid of the file it
    //  is associated with.
Martin Sustrik's avatar
Martin Sustrik committed
254 255
    struct sockaddr_un *sun = (struct sockaddr_un*) &addr;
    if (AF_UNIX == sun->sun_family) {
256 257 258 259 260
        rc = ::unlink(sun->sun_path);
        if (rc != 0)
            return -1;
    }

Martin Sustrik's avatar
Martin Sustrik committed
261 262 263
    return 0;
}

Martin Sustrik's avatar
Martin Sustrik committed
264
zmq::fd_t zmq::tcp_listener_t::get_fd ()
Martin Sustrik's avatar
Martin Sustrik committed
265 266 267 268
{
    return s;
}

Martin Sustrik's avatar
Martin Sustrik committed
269
zmq::fd_t zmq::tcp_listener_t::accept ()
Martin Sustrik's avatar
Martin Sustrik committed
270
{
Martin Sustrik's avatar
Martin Sustrik committed
271
    zmq_assert (s != retired_fd);
Martin Sustrik's avatar
Martin Sustrik committed
272 273 274 275 276 277

    //  Accept one incoming connection.
    fd_t sock = ::accept (s, NULL, NULL);

#if (defined ZMQ_HAVE_LINUX || defined ZMQ_HAVE_FREEBSD || \
     defined ZMQ_HAVE_OPENBSD || defined ZMQ_HAVE_OSX || \
Martin Lucina's avatar
Martin Lucina committed
278
     defined ZMQ_HAVE_OPENVMS || defined ZMQ_HAVE_NETBSD)
Martin Sustrik's avatar
Martin Sustrik committed
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
    if (sock == -1 && 
        (errno == EAGAIN || errno == EWOULDBLOCK || 
         errno == EINTR || errno == ECONNABORTED))
        return retired_fd;
#elif (defined ZMQ_HAVE_SOLARIS || defined ZMQ_HAVE_AIX)
    if (sock == -1 && 
        (errno == EWOULDBLOCK || errno == EINTR || 
         errno == ECONNABORTED || errno == EPROTO))
        return retired_fd;
#elif defined ZMQ_HAVE_HPUX
    if (sock == -1 && 
        (errno == EAGAIN || errno == EWOULDBLOCK || 
         errno == EINTR || errno == ECONNABORTED || errno == ENOBUFS))
        return retired_fd;
#elif defined ZMQ_HAVE_QNXNTO 
    if (sock == -1 && 
        (errno == EWOULDBLOCK || errno == EINTR || errno == ECONNABORTED))
        return retired_fd;
#endif

    errno_assert (sock != -1); 

    // Set to non-blocking mode.
    int flags = fcntl (s, F_GETFL, 0);
    if (flags == -1) 
        flags = 0;
    int rc = fcntl (sock, F_SETFL, flags | O_NONBLOCK);
    errno_assert (rc != -1);

308
    struct sockaddr *sa = (struct sockaddr*) &addr;
309
    if (AF_UNIX != sa->sa_family) {
310 311 312 313 314 315

        //  Disable Nagle's algorithm.
        int flag = 1;
        rc = setsockopt (sock, IPPROTO_TCP, TCP_NODELAY, (char*) &flag,
            sizeof (int));
        errno_assert (rc == 0);
Martin Sustrik's avatar
Martin Sustrik committed
316

Martin Sustrik's avatar
Martin Sustrik committed
317
#ifdef ZMQ_HAVE_OPENVMS
318 319 320 321 322
        //  Disable delayed acknowledgements.
        flag = 1;
        rc = setsockopt (sock, IPPROTO_TCP, TCP_NODELACK, (char*) &flag,
            sizeof (int));
        errno_assert (rc != SOCKET_ERROR);
Martin Sustrik's avatar
Martin Sustrik committed
323
#endif
324
    }
Martin Sustrik's avatar
Martin Sustrik committed
325 326 327 328 329

    return sock;
}

#endif