diff options
author | Luo Jinghua <sunmoon1997@gmail.com> | 2009-11-08 10:40:51 +0800 |
---|---|---|
committer | Luo Jinghua <sunmoon1997@gmail.com> | 2009-11-08 10:40:51 +0800 |
commit | 3de77c9d1c5e103e0d63e8aa130ee9ec4255349e (patch) | |
tree | 5e4acaa5b66edb81004897e82b193ce5c3e96b6c | |
parent | 7e776646d3887d89633e00f669386de5f0814e5f (diff) |
milkway: add a socket wrapper
-rw-r--r-- | milkway/Makefile.am | 8 | ||||
-rw-r--r-- | milkway/mw-inet-address.h | 2 | ||||
-rw-r--r-- | milkway/mw-inet-socket-address.h | 1 | ||||
-rw-r--r-- | milkway/mw-net-types.h (renamed from milkway/mw-socket-family.h) | 4 | ||||
-rw-r--r-- | milkway/mw-socket-address.h | 2 | ||||
-rw-r--r-- | milkway/mw-socket.c | 581 | ||||
-rw-r--r-- | milkway/mw-socket.h | 114 |
7 files changed, 704 insertions, 8 deletions
diff --git a/milkway/Makefile.am b/milkway/Makefile.am index 0059fe7..55bd417 100644 --- a/milkway/Makefile.am +++ b/milkway/Makefile.am @@ -35,10 +35,11 @@ milkway_headers = \ mw-main-loop.h \ mw-timeout-source.h \ mw-idle-source.h \ - mw-socket-family.h \ + mw-net-types.h \ mw-socket-address.h \ mw-inet-address.h \ - mw-inet-socket-address.h + mw-inet-socket-address.h \ + mw-socket.h libmilkway_la_LDFLAGS = -version-info $(LT_VERSION_INFO) -no-undefined libmilkway_la_LIBADD = @DEP_LIBS@ $(PTHREAD_LIBS) @@ -92,7 +93,8 @@ libmilkway_la_SOURCES = \ mw-network-private.h \ mw-socket-address.c \ mw-inet-address.c \ - mw-inet-socket-address.c + mw-inet-socket-address.c \ + mw-socket.c milkwayincludedir = $(includedir)/milkway/milkway milkwayinclude_HEADERS = $(milkway_headers) diff --git a/milkway/mw-inet-address.h b/milkway/mw-inet-address.h index ca5b3b2..8080499 100644 --- a/milkway/mw-inet-address.h +++ b/milkway/mw-inet-address.h @@ -21,7 +21,7 @@ #define MW_INET_ADDRESS_H #include <milkway/mw-object.h> -#include <milkway/mw-socket-family.h> +#include <milkway/mw-net-types.h> typedef struct _mw_inet_address_type mw_inet_address_type_t; typedef struct _mw_inet_address mw_inet_address_t; diff --git a/milkway/mw-inet-socket-address.h b/milkway/mw-inet-socket-address.h index 1c942fe..b06cd08 100644 --- a/milkway/mw-inet-socket-address.h +++ b/milkway/mw-inet-socket-address.h @@ -21,7 +21,6 @@ #define MW_INET_SOCKET_ADDRESS_H #include <milkway/mw-socket-address.h> -#include <milkway/mw-socket-family.h> #include <milkway/mw-inet-address.h> typedef struct _mw_inet_socket_address_type mw_inet_socket_address_type_t; diff --git a/milkway/mw-socket-family.h b/milkway/mw-net-types.h index 8ced9d3..ad0d8b9 100644 --- a/milkway/mw-socket-family.h +++ b/milkway/mw-net-types.h @@ -17,8 +17,8 @@ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ -#ifndef MW_SOCKET_FAMILY_H -#define MW_SOCKET_FAMILY_H +#ifndef MW_NET_TYPES_H +#define MW_NET_TYPES_H typedef enum { MW_SOCKET_FAMILY_INVALID, diff --git a/milkway/mw-socket-address.h b/milkway/mw-socket-address.h index d809bf6..ce0907e 100644 --- a/milkway/mw-socket-address.h +++ b/milkway/mw-socket-address.h @@ -20,7 +20,7 @@ #ifndef MW_SOCKET_ADDRESS_H #define MW_SOCKET_ADDRESS_H -#include <milkway/mw-socket-family.h> +#include <milkway/mw-net-types.h> #include <milkway/mw-object.h> #include <milkway/mw-error.h> diff --git a/milkway/mw-socket.c b/milkway/mw-socket.c new file mode 100644 index 0000000..ad5bf1f --- /dev/null +++ b/milkway/mw-socket.c @@ -0,0 +1,581 @@ +/* Milkway + * + * Copyright (C) 2008- Luo Jinghua <sunmoon1997@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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 "milkwayint.h" +#include "milkway/mw-socket.h" +#include "milkway/mw-network-private.h" + +#include <errno.h> +#include <signal.h> + +#ifndef MW_OS_WINDOWS +#include <unistd.h> +#endif + +struct _mw_socket_priv { + unsigned int closed : 1; + unsigned int connected : 1; + unsigned int keepalive : 1; + unsigned int listening : 1; + unsigned int blocking : 1; + + int backlog; + + int fd; + mw_socket_family_t family; + mw_socket_type_t type; + mw_socket_proto_t proto; +}; + +#ifndef MW_OS_WINDOWS +#define closesocket(fd) close(fd) +#endif + +static int +get_socket_errno (void) +{ +#ifndef MW_OS_WINDOWS + return errno; +#else + return WSAGetLastError (); +#endif +} + +static int +get_socket_error (int err) +{ +#ifndef MW_OS_WINDOWS + switch(err) { + case EADDRINUSE: + return MW_ADDRESS_IN_USE; + case EWOULDBLOCK: + return MW_WOULD_BLOCK; + case EBADF: + return MW_BADF; + case ENOTSOCK: + return MW_INVALID; + case EOPNOTSUPP: + case EPFNOSUPPORT: + case EAFNOSUPPORT: + return MW_NOT_SUPPORTED; + default: + return MW_INVALID; + } +#else + switch (err) { + case WSAEADDRINUSE: + return MW_ADDRESS_IN_USE; + case WSAEWOULDBLOCK: + return MW_WOULD_BLOCK; + case WSAEACCES: + return MW_PERMISSION_DENIED; + case WSA_INVALID_HANDLE: + case WSA_INVALID_PARAMETER: + case WSAEBADF: + case WSAENOTSOCK: + return MW_INVALID_ARGUMENT; + case WSAEPROTONOSUPPORT: + return MW_NOT_SUPPORTED; + case WSAECANCELLED: + return MW_CANCELLED; + case WSAESOCKTNOSUPPORT: + case WSAEOPNOTSUPP: + case WSAEPFNOSUPPORT: + case WSAEAFNOSUPPORT: + return MW_NOT_SUPPORTED; + default: + return MW_UNKNOWN; + } +#endif +} + +static mw_socket_t* +mw_socket_init(mw_socket_t *self) +{ + mw_socket_priv_t *priv; + + if (!mw_object_init(&self->base)) + return NULL; + + priv = MW_GET_PRIV(self, MW_SOCKET_TYPE); + priv->closed = MW_FALSE; + priv->connected = MW_FALSE; + priv->keepalive = MW_FALSE; + priv->listening = MW_FALSE; + priv->blocking = MW_FALSE; + priv->backlog = 10; + priv->fd = -1; + priv->family = MW_SOCKET_FAMILY_INVALID; + priv->type = MW_SOCKET_FAMILY_INVALID; + priv->proto = MW_SOCKET_PROTO_UNKNOWN; + return self; +} + +static mw_bool_t +mw_socket_init_socket(mw_socket_t *self, + mw_error_t **reterr) +{ + int family, type, proto; + + switch (self->priv->family) { + case MW_SOCKET_FAMILY_INET: + family = AF_INET; + break; + case MW_SOCKET_FAMILY_INET6: + family = AF_INET6; + break; + case MW_SOCKET_FAMILY_UNIX: +#ifdef AF_UNIX + family = AF_UNIX; + break; +#endif + case MW_SOCKET_FAMILY_INVALID: + mw_error_set(reterr, MW_ERROR_SOCKET, MW_NOT_SUPPORTED, + "Unsupported socket familly"); + return MW_FALSE; + } + + switch (self->priv->type) { + case MW_SOCKET_TYPE_STREAM: + type = SOCK_STREAM; + break; + case MW_SOCKET_TYPE_DATAGRAM: + type = SOCK_DGRAM; + break; + case MW_SOCKET_TYPE_SEQPACKET: + type = SOCK_SEQPACKET; + break; + case MW_SOCKET_TYPE_INVALID: + mw_error_set(reterr, MW_ERROR_SOCKET, MW_NOT_SUPPORTED, + "Unsupported socket type"); + return MW_FALSE; + } + proto = self->priv->proto; + + self->priv->fd = socket(family, type, proto); + if (self->priv->fd < 0) { + mw_error_set(reterr, MW_ERROR_SOCKET, + get_socket_error(get_socket_errno()), + "Failed to create a socket"); + return MW_FALSE; + } + + return MW_TRUE; +} + +static mw_bool_t +mw_socket_init_info(mw_socket_t *self, + mw_error_t **reterr) +{ + struct sockaddr_storage address; + int fd; + unsigned int addrlen; + unsigned int optlen; + int value; +#ifdef MW_OS_WINDOWS + BOOL bool_val; +#else + int bool_val; +#endif + + fd = self->priv->fd; + optlen = sizeof(value); + if (getsockopt (fd, SOL_SOCKET, SO_TYPE, (void *)&value, &optlen) != 0) { + mw_error_set(reterr, MW_ERROR_SOCKET, + get_socket_error(get_socket_errno()), + "Invalid socket"); + return MW_FALSE; + } + + switch (value) { + case SOCK_STREAM: + self->priv->type = MW_SOCKET_TYPE_STREAM; + break; + case SOCK_DGRAM: + self->priv->type = MW_SOCKET_TYPE_DATAGRAM; + break; + case SOCK_SEQPACKET: + self->priv->type = MW_SOCKET_TYPE_SEQPACKET; + break; + default: + self->priv->type = MW_SOCKET_TYPE_INVALID; + break; + } + + addrlen = sizeof(address); + if (getsockname(fd, (struct sockaddr *) &address, &addrlen) != 0) { + mw_error_set(reterr, MW_ERROR_SOCKET, + get_socket_error(get_socket_errno()), + "Invalid socket, failed to socket address"); + return MW_FALSE; + } + + switch (address.ss_family) { + case AF_INET: + self->priv->family = MW_SOCKET_FAMILY_INET; + case AF_INET6: + self->priv->family = MW_SOCKET_FAMILY_INET6; +#ifdef AF_UNIX + case AF_UNIX: + self->priv->family = MW_SOCKET_FAMILY_UNIX; + break; +#endif + default: + self->priv->family = MW_SOCKET_FAMILY_INVALID; + break; + } + + if (self->priv->family != MW_SOCKET_FAMILY_INVALID) { + addrlen = sizeof(address); + if (getpeername (fd, (struct sockaddr *) &address, &addrlen) >= 0) + self->priv->connected = MW_TRUE; + } + + optlen = sizeof(bool_val); + if (getsockopt (fd, SOL_SOCKET, SO_KEEPALIVE, + (void *)&bool_val, &optlen) == 0) + self->priv->keepalive = !!bool_val; + else + self->priv->keepalive = MW_FALSE; + + return MW_TRUE; +} + +mw_socket_t* +mw_socket_new(mw_socket_family_t family, + mw_socket_type_t type, + mw_socket_proto_t proto, + mw_error_t **reterr) +{ + mw_socket_t *self; + + self = mw_object_alloc(MW_SOCKET_TYPE); + if (!mw_socket_init(self)) { + mw_error_set(reterr, MW_ERROR_SOCKET, MW_OUT_OF_MEM, + "Failed to construct a socket object"); + return NULL; + } + + self->priv->family = family; + self->priv->type = type; + self->priv->proto = proto; + if (!mw_socket_init_socket(self, reterr)) { + MW_SUPER_FINALIZE(self, MW_SOCKET_TYPE)((mw_object_t*)self); + return NULL; + } + return self; +} + +mw_socket_t* +mw_socket_new_from_fd(int fd, + mw_error_t **reterr) +{ + mw_socket_t *self; + + self = mw_object_alloc(MW_SOCKET_TYPE); + if (!mw_socket_init(self)) { + mw_error_set(reterr, MW_ERROR_SOCKET, MW_OUT_OF_MEM, + "Failed to construct a socket object"); + return NULL; + } + + self->priv->fd = fd; + if (!mw_socket_init_info(self, reterr)) { + MW_SUPER_FINALIZE(self, MW_SOCKET_TYPE)((mw_object_t*)self); + return NULL; + } + return self; +} + +int +mw_socket_get_fd(mw_socket_t *self) +{ + return self->priv->fd; +} + +mw_socket_family_t +mw_socket_get_family(mw_socket_t *self) +{ + return self->priv->family; +} + +mw_socket_type_t +mw_socket_get_type(mw_socket_t *self) +{ + return self->priv->type; +} + +mw_socket_proto_t +mw_socket_get_proto(mw_socket_t *self) +{ + return self->priv->proto; +} + +mw_bool_t +mw_socket_is_connected(mw_socket_t *self) +{ + return self->priv->connected; +} + +mw_bool_t +mw_socket_bind(mw_socket_t *self, + mw_socket_address_t *address, + mw_error_t **reterr) +{ + struct sockaddr_storage addr; + int fd = self->priv->fd; + + if (!mw_socket_address_to_native(address, &addr, + sizeof(addr), reterr)) + return MW_FALSE; + if (bind (fd, (const struct sockaddr*)&addr, + mw_socket_address_get_native_size(address)) < 0) { + mw_error_set(reterr, MW_ERROR_SOCKET, + get_socket_error(get_socket_errno()), + "Failed to bind socket to a address"); + return MW_FALSE; + } + + return MW_TRUE; +} + +mw_bool_t +mw_socket_listen(mw_socket_t *self, + int backlog, + mw_error_t **reterr) +{ + int fd = self->priv->fd; + + if (listen (fd, self->priv->backlog) < 0) { + mw_error_set(reterr, MW_ERROR_SOCKET, + get_socket_error(get_socket_errno()), + "Failed to listen"); + return MW_FALSE; + } + + return MW_TRUE; +} + +mw_bool_t +mw_socket_connect(mw_socket_t *self, + mw_socket_address_t *address, + mw_error_t **reterr) +{ + struct sockaddr_storage addr; + int fd = self->priv->fd; + + if (!mw_socket_address_to_native(address, &addr, + sizeof(addr), reterr)) + return MW_FALSE; + if (connect (fd, (const struct sockaddr*)&addr, + mw_socket_address_get_native_size(address)) < 0) { + mw_error_set(reterr, MW_ERROR_SOCKET, + get_socket_error(get_socket_errno()), + "Failed to connect to peer"); + return MW_FALSE; + } + + return MW_TRUE; +} + +mw_socket_t* +mw_socket_accept(mw_socket_t *self, + mw_error_t **reterr) +{ + int fd = self->priv->fd; + mw_socket_t *client; + int ret; + +#ifndef MW_OS_WINDOWS + do { + ret = accept (fd, NULL, 0); + } while (ret < 0 && errno != EINTR); +#else + ret = accept (fd, NULL, 0); +#endif + if (ret < 0) { + mw_error_set(reterr, MW_ERROR_SOCKET, + get_socket_error(get_socket_errno()), + "Failed to connect to peer"); + return MW_FALSE; + } + +#ifdef MW_OS_WINDOWS + /* The socket inherits the accepting sockets event mask and even object, + we need to remove that */ + WSAEventSelect (ret, NULL, 0); +#endif + + client = mw_socket_new_from_fd (ret, reterr); + if (!client) + closesocket (ret); + else + client->priv->proto = self->priv->proto; + + return client; +} + +int +mw_socket_recv(mw_socket_t *self, + void *buffer, + size_t size, + mw_error_t **reterr) +{ + int fd = self->priv->fd; + int ret; + + do { + ret = recv(fd, buffer, size, 0); + if (ret < 0) { + int error = get_socket_errno (); + + if (error == EINTR) + continue; + + mw_error_set (reterr, MW_ERROR_SOCKET, + get_socket_error (error), + "Failed to send data"); + return -1; + } + } while (0); + + return ret; +} + +int +mw_socket_send(mw_socket_t *self, + const void *buffer, + size_t size, + mw_error_t **reterr) +{ + int fd = self->priv->fd; + int ret; + + do { + ret = send(fd, buffer, size, 0); + if (ret < 0) { + int error = get_socket_errno (); + + if (error == EINTR) + continue; + + mw_error_set (reterr, MW_ERROR_SOCKET, + get_socket_error (error), + "Failed to send data"); + return -1; + } + } while (0); + + return ret; +} + +mw_bool_t +mw_socket_shutdown(mw_socket_t *self, + mw_bool_t shutdown_read, + mw_bool_t shutdown_write, + mw_error_t **reterr) +{ + int how; + + if (!shutdown_read && !shutdown_write) + return MW_TRUE; + +#ifndef MW_OS_WINDOWS + if (shutdown_read && shutdown_write) + how = SHUT_RDWR; + else if (shutdown_read) + how = SHUT_RD; + else + how = SHUT_WR; +#else + if (shutdown_read && shutdown_write) + how = SD_BOTH; + else if (shutdown_read) + how = SD_RECEIVE; + else + how = SD_SEND; +#endif + + if (shutdown (self->priv->fd, how) != 0) { + mw_error_set (reterr, MW_ERROR_SOCKET, + get_socket_error (get_socket_errno()), + "Failed to shutdown socket"); + return MW_FALSE; + } + + if (shutdown_read && shutdown_write) + self->priv->connected = MW_FALSE; + + return MW_TRUE; +} + +mw_bool_t +mw_socket_close(mw_socket_t *self, + mw_error_t **reterr) +{ + mw_socket_priv_t *priv = self->priv; + int ret; + + if (priv->closed) + return MW_TRUE; + + for (;;) { +#ifdef MW_OS_WINDOWS + ret = closesocket (priv->fd); +#else + do { + ret = close (priv->fd); + } while (ret < 0 && errno != EINTR); +#endif + if (ret < 0) { + mw_error_set(reterr, MW_ERROR_SOCKET, + get_socket_error(get_socket_errno()), + "Failed to close socket"); + return MW_FALSE; + } + } + + priv->closed = MW_TRUE; + priv->connected = MW_FALSE; + return MW_TRUE; +} + +static void +mw_socket_finalize(mw_object_t *super) +{ + mw_socket_t *self = (mw_socket_t*)super; + + if (self->priv->fd > 0) + mw_socket_close(self, NULL); + MW_SUPER_FINALIZE(super, MW_SOCKET_TYPE)(super); +} + +static void +mw_socket__type_init(mw_socket__type_t *self) +{ +#ifdef SIGPIPE + signal (SIGPIPE, SIG_IGN); +#endif + + self->base.finalize = mw_socket_finalize; +} + +typedef mw_socket_t mw_socket__t; +MW_DEFINE_GET_TYPE(mw_socket_, mw_socket__type_t, + MW_OBJECT_TYPE, "MWSocket", sizeof (mw_socket_priv_t)); diff --git a/milkway/mw-socket.h b/milkway/mw-socket.h new file mode 100644 index 0000000..17171e4 --- /dev/null +++ b/milkway/mw-socket.h @@ -0,0 +1,114 @@ +/* Milkway + * + * Copyright (C) 2008- Luo Jinghua <sunmoon1997@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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. + */ +#ifndef MW_SOCKET_H +#define MW_SOCKET_H + +#include <milkway/mw-object.h> +#include <milkway/mw-socket-address.h> +#include <milkway/mw-net-types.h> + +typedef struct _mw_socket__type mw_socket__type_t; +typedef struct _mw_socket mw_socket_t; +typedef struct _mw_socket_priv mw_socket_priv_t; + +#define MW_SOCKET_TYPE mw_socket__get_type() +#define MW_ERROR_SOCKET "MWSocketError" + +struct _mw_socket__type { + mw_object_type_t base; +}; + +struct _mw_socket { + mw_object_t base; + + mw_socket_priv_t *priv; +}; + +mw_public mw_socket__type_t* +mw_socket__get_type(void); + +mw_public mw_socket_t* +mw_socket_new(mw_socket_family_t family, + mw_socket_type_t type, + mw_socket_proto_t proto, + mw_error_t **reterr); + +mw_public mw_socket_t* +mw_socket_new_from_fd(int fd, + mw_error_t **reterr); + +mw_public int +mw_socket_get_fd(mw_socket_t *self); + +mw_public mw_socket_family_t +mw_socket_get_family(mw_socket_t *self); + +mw_public mw_socket_type_t +mw_socket_get_type(mw_socket_t *self); + +mw_public mw_socket_proto_t +mw_socket_get_proto(mw_socket_t *self); + +mw_public mw_bool_t +mw_socket_is_connected(mw_socket_t *self); + +mw_public mw_bool_t +mw_socket_bind(mw_socket_t *self, + mw_socket_address_t *address, + mw_error_t **reterr); + +mw_public mw_bool_t +mw_socket_listen(mw_socket_t *self, + int backlog, + mw_error_t **reterr); + +mw_public mw_bool_t +mw_socket_connect(mw_socket_t *self, + mw_socket_address_t *address, + mw_error_t **reterr); + +mw_socket_t* +mw_socket_accept(mw_socket_t *self, + mw_error_t **reterr); + +mw_public int +mw_socket_recv(mw_socket_t *self, + void *buffer, + size_t size, + mw_error_t **reterrr); + +mw_public int +mw_socket_send(mw_socket_t *self, + const void *buffer, + size_t size, + mw_error_t **reterrr); + +mw_public mw_bool_t +mw_socket_shutdown(mw_socket_t *self, + mw_bool_t read, + mw_bool_t write, + mw_error_t **reterr); + +mw_public mw_bool_t +mw_socket_close(mw_socket_t *self, + mw_error_t **reterr); + + +#endif |