unix.c

来自「GNet是一个简单的网络库。它是目标定向的」· C语言 代码 · 共 388 行

C
388
字号
/* GNet - Networking library * Copyright (C) 2000  David Helder * Copyright (C) 2001  Mark Ferlatte * * 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 "gnet-private.h"#include "unix.h"#define PATH(S) (((struct sockaddr_un *) (&(S)->sa))->sun_path)gboolean gnet_unix_socket_unlink (const gchar *path);/** *  gnet_unix_socket_connect: *  @path: Path to socket to connect to * *  A quick and easy #GUnixSocket constructor.  This connects to the *  specified path.  This function does block.  Use this function when *  you are a client connecting to a server and you don't mind *  blocking. * *  Returns: A new #GUnixSocket, or NULL if there was a failure.   * **/GUnixSocket *gnet_unix_socket_connect(const gchar *path){  return gnet_unix_socket_new(path);}	/** *  gnet_unix_socket_new: *  @path: Path to connect to. * *  Connect to a specified address.  Use this sort of socket when *  you're a client connecting to a server.  This function will block *  to connect. * *  Returns: A new #GUnixSocket, or NULL if there was a failure. * **/GUnixSocket *gnet_unix_socket_new(const gchar *path){  GUnixSocket *s = g_new0(GUnixSocket, 1);  struct sockaddr_un *sa_un;	  g_return_val_if_fail(path != NULL, NULL);  sa_un = (struct sockaddr_un *) &s->sa;  /* Create socket */  s->ref_count = 1;  s->server = FALSE;  s->sockfd = socket(AF_UNIX, SOCK_STREAM, 0);  if (s->sockfd < 0) {    g_free(s);    return NULL;  }  memcpy(sa_un->sun_path, path, strlen(path));  sa_un->sun_family = AF_UNIX;  if (connect(s->sockfd, &s->sa, sizeof(s->sa)) != 0) {    g_free(s);    return NULL;  }  return s;}/** *  gnet_unix_socket_delete: *  @s: UnixSocket to delete. * *  Close and delete a #GUnixSocket, regardless of reference count. * **/voidgnet_unix_socket_delete(GUnixSocket *s){  g_return_if_fail(s != NULL);    GNET_CLOSE_SOCKET(s->sockfd); /* Don't care if this fails. */  if (s->iochannel)    g_io_channel_unref(s->iochannel);  if (s->server)    gnet_unix_socket_unlink(PATH(s));  g_free(s);}/** *  gnet_unix_socket_ref *  @s: GUnixSocket to reference * *  Increment the reference counter of a #GUnixSocket. * **/voidgnet_unix_socket_ref(GUnixSocket *s){  g_return_if_fail(s != NULL);  ++s->ref_count;}/** *  gnet_unix_socket_unref *  @s: GUnixSocket to unreference * *  Remove a reference from the #GUnixSocket.  When the reference count *  reaches 0, the socket is deleted.   * **/voidgnet_unix_socket_unref(GUnixSocket *s){  g_return_if_fail(s != NULL);  --s->ref_count;  if (s->ref_count == 0)     gnet_unix_socket_delete(s);}    /** *  gnet_unix_socket_get_iochannel *  @socket: GUnixSocket to get GIOChannel from. * *  Get the #GIOChannel for the #GUnixSocket. * *  For a client socket, the #GIOChannel represents the data stream. *  Use it like you would any other #GIOChannel. * *  For a server socket, however, the #GIOChannel represents incoming *  connections.  If you can read from it, there's a connection *  waiting to be accepted. * *  There is one channel for every socket.  This function refs the *  channel before returning it.  You should unref the channel when *  you are done with it.  However, you should not close the channel - *  this is done when you delete the socket. * *  Returns: A #GIOChannel; NULL on failure.   * **/GIOChannel*gnet_unix_socket_get_iochannel(GUnixSocket *socket){  g_return_val_if_fail(socket != NULL, NULL);  if (socket->iochannel == NULL)    socket->iochannel = gnet_private_iochannel_new(socket->sockfd);  g_io_channel_ref(socket->iochannel);  return socket->iochannel;}/** *  gnet_unix_socket_get_path: *  @socket: GUnixSocket to get the path of. * *  Get the path of the socket. * *  Returns: Caller-owned path; NULL on failure. * **/gchar*gnet_unix_socket_get_path(const GUnixSocket* socket){  g_return_val_if_fail(socket != NULL, NULL);  return g_strdup (PATH(socket));}/** *  gnet_unix_socket_server_new: *  @path: Path for the socket. * *  Create and open a new #GUnixSocket with the specified path.  Use *  this sort of socket when you are a server. * *  Returns: a new #GUnixSocket, or NULL if there was a failure.   * **/GUnixSocket*gnet_unix_socket_server_new (const gchar *path){  struct sockaddr_un *sa_un;  GUnixSocket *s;  gint flags;  socklen_t n;  g_return_val_if_fail(path != NULL, NULL);	  s = g_new0(GUnixSocket, 1);  sa_un = (struct sockaddr_un *) &s->sa;  sa_un->sun_family = AF_UNIX;  memcpy(sa_un->sun_path, path, strlen(path));  s->ref_count = 1;  s->server = TRUE;    if (! gnet_unix_socket_unlink(PATH(s)))    goto error;    s->sockfd = socket(AF_UNIX, SOCK_STREAM, 0);  if (s->sockfd < 0)    goto error;  flags = fcntl(s->sockfd, F_GETFL, 0);  if (flags == -1)    goto error;  /* Make the socket non-blocking */  if (fcntl(s->sockfd, F_SETFL, flags | O_NONBLOCK) == -1)    goto error;  if (bind(s->sockfd, &s->sa, sizeof(s->sa)) != 0)    goto error;  n = sizeof(s->sa);  /* Get the socket name FIXME: why? */  if (getsockname(s->sockfd, &s->sa, &n) != 0)    goto error;  if (listen(s->sockfd, 10) != 0)    goto error;  return s;	 error:  if (s)    gnet_unix_socket_delete(s);  return NULL;}/** *  gnet_unix_socket_server_accept: *  @socket: GUnixSocket to accept connections from * *  Accept connections from the socket.  The socket must have been *  created using gnet_unix_socket_server_new(). This function will *  block (use gnet_unix_socket_server_accept_nonblock() if you don't *  want to block).  If the socket's #GIOChannel is readable, it DOES *  NOT mean that this function will block. * *  Returns: a new #GUnixSocket if there is another connect, or NULL if *  there's an error.   * **/GUnixSocket*gnet_unix_socket_server_accept (const GUnixSocket *socket){  gint sockfd;  struct sockaddr sa;  fd_set fdset;  socklen_t n;  GUnixSocket *s;  g_return_val_if_fail(socket != NULL, NULL); try_again:  FD_ZERO(&fdset);  FD_SET(socket->sockfd, &fdset);  if (select(socket->sockfd + 1, &fdset, NULL, NULL, NULL) == -1) {    if (errno == EINTR)      goto try_again;    return NULL;  }  n = sizeof(s->sa);  if ((sockfd = accept(socket->sockfd, &sa, &n)) == -1) {    if (errno == EWOULDBLOCK ||	errno == ECONNABORTED ||#ifdef EPROTO		/* OpenBSD does not have EPROTO */	errno == EPROTO ||#endif /* EPROTO */	errno == EINTR)      goto try_again;    return NULL;  }  s = g_new0(GUnixSocket, 1);  s->ref_count = 1;  s->sockfd = sockfd;  memcpy(&s->sa, &sa, sizeof(s->sa));  return s;}/** *  gnet_unix_socket_server_accept_nonblock: *  @socket: GUnixSocket to accept connections from. * *  Accept a connection from the socket without blocking.  The socket *  must have been created using gnet_unix_socket_server_new().  This *  function is best used with the socket's #GIOChannel.  If the channel *  is readable, then you PROBABLY have a connection.  It is possible *  for the connection to close by the time you call this, so it may *  return NULL even if the channel was readable. * *  Returns: a new #GUnixSocket if there was another connect, or NULL *  otherwise.  * **/GUnixSocket*gnet_unix_socket_server_accept_nonblock (const GUnixSocket *socket){  gint sockfd;  struct sockaddr sa;  fd_set fdset;  socklen_t n;  GUnixSocket *s;  struct timeval tv = {0, 0};  g_return_val_if_fail (socket != NULL, NULL); try_again:  FD_ZERO(&fdset);  FD_SET(socket->sockfd, &fdset);  if(select(socket->sockfd + 1, &fdset, NULL, NULL, &tv) == -1) {    if (errno == EINTR)      goto try_again;    return NULL;  }  n = sizeof(sa);	  if ((sockfd = accept(socket->sockfd, &sa, &n)) == -1) {    /* If we get an error, return.  We don't want to try again       as we do in gnet_unix_socket_server_accept() - it might       cause a block. */    return NULL;  }  s = g_new0(GUnixSocket, 1);  s->ref_count = 1;  s->sockfd = sockfd;  memcpy(&s->sa, &sa, sizeof(s->sa));  return s;}/** *  gnet_unix_socket_unlink: *  @path: Path to socket to unlink. * *  Verifies that the file at the end of the path is a socket, and then *  unlinks it. * *  Returns: TRUE on success, FALSE on failure. **/gbooleangnet_unix_socket_unlink(const gchar *path){	struct stat stbuf;	int r;	g_return_val_if_fail(path != NULL, FALSE);	r = stat(path, &stbuf);	if (r == 0) {		if (S_ISSOCK(stbuf.st_mode)) {			if (unlink(path) == 0) {				return TRUE;			} else {				/* Can't unlink */				return FALSE;			}		} else {			/* path is not a socket */			return FALSE;		}	} else if (errno == ENOENT) {		/* File doesn't exist, so we're okay */		return TRUE;	}	return FALSE;}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?