tcp.c

来自「GNet是一个简单的网络库。它是目标定向的」· C语言 代码 · 共 1,257 行 · 第 1/2 页

C
1,257
字号
 *  gnet_tcp_socket_unref *  @s: #GTcpSocket to unreference * *  Remove a reference from the #GTcpSocket.  When reference count *  reaches 0, the socket is deleted. * **/voidgnet_tcp_socket_unref(GTcpSocket* s){  g_return_if_fail(s != NULL);  --s->ref_count;  if (s->ref_count == 0)    {      if (s->accept_watch)	g_source_remove (s->accept_watch);      GNET_CLOSE_SOCKET(s->sockfd);	/* Don't care if this fails... */      if (s->iochannel)	g_io_channel_unref(s->iochannel);      g_free(s);    }}/** *  gnet_tcp_socket_get_iochannel: *  @socket: GTcpSocket to get GIOChannel from. * *  Get the #GIOChannel for the #GTcpSocket. * *  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. * *  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_tcp_socket_get_iochannel(GTcpSocket* 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_tcp_socket_get_inetaddr: *  @socket: #GTcpSocket to get address of. * *  Get the address of the socket.  If the socket is client socket, *  the address is the address of the remote host it is connected to. *  If the socket is a server socket, the address is the address of *  the local host.  (Though you should use *  gnet_inetaddr_gethostaddr() to get the #GInetAddr of the local *  host.) * *  Returns: #GInetAddr of socket; NULL on failure. * **/GInetAddr* gnet_tcp_socket_get_inetaddr(const GTcpSocket* socket){  g_return_val_if_fail (socket != NULL, NULL);  return gnet_private_inetaddr_sockaddr_new(socket->sa);}/** *  gnet_tcp_socket_get_port: *  @socket: GTcpSocket to get the port number of. * *  Get the port number the socket is bound to. * *  Returns: Port number of the socket. * **/gintgnet_tcp_socket_get_port(const GTcpSocket* socket){  g_return_val_if_fail (socket != NULL, 0);  return g_ntohs(GNET_SOCKADDR_IN(socket->sa).sin_port);}/* **************************************** *//** *  gnet_tcp_socket_set_tos: *  @socket: GTcpSocket to set the type-of-service of *  @tos: Type of service (in tcp.h) * *  Set the type-of-service of the socket.  Usually, routers don't *  honor this.  Some systems don't support this function.  If the *  operating system does not support it, the function does nothing. * **/voidgnet_tcp_socket_set_tos (GTcpSocket* socket, GNetTOS tos){  int sotos;  g_return_if_fail (socket != NULL);  /* Some systems (e.g. OpenBSD) do not have IPTOS_*.  Other systems     have some of them, but not others.  And some systems have them,     but with different names (e.g. FreeBSD has IPTOS_MINCOST).  If a     system does not have a IPTOS, or any of them, then this function     does nothing.  */  switch (tos)    {#ifdef IPTOS_LOWDELAY    case GNET_TOS_LOWDELAY:	sotos = IPTOS_LOWDELAY;		break;#endif#ifdef IPTOS_THROUGHPUT    case GNET_TOS_THROUGHPUT:	sotos = IPTOS_THROUGHPUT;	break;#endif#ifdef IPTOS_RELIABILITY    case GNET_TOS_RELIABILITY:	sotos = IPTOS_RELIABILITY;	break;#endif#ifdef IPTOS_LOWCOST    case GNET_TOS_LOWCOST:	sotos = IPTOS_LOWCOST;		break;#else#ifdef IPTOS_MINCOST	/* Called MINCOST in FreeBSD 4.0 */    case GNET_TOS_LOWCOST:	sotos = IPTOS_MINCOST;		break;#endif#endif    default: return;    }#ifdef IP_TOS  if (setsockopt(socket->sockfd, IPPROTO_IP, IP_TOS, (void*) &sotos, sizeof(sotos)) != 0)    g_warning ("Can't set TOS on TCP socket\n");#endif}/* **************************************** *//* Server stuff *//** *  gnet_tcp_socket_server_new: *  @port: Port number for the socket (0 if you don't care). * *  Create and open a new #GTcpSocket with the specified port number. *  Use this sort of socket when you are a server and you know what *  the port number should be (or pass 0 if you don't care what the *  port is).  SOCKS is used if SOCKS is enabled. * *  Returns: a new #GTcpSocket, or NULL if there was a failure. * **/GTcpSocket* gnet_tcp_socket_server_new (gint port){  GInetAddr iface;  struct sockaddr_in* sa_in;    /* Use SOCKS if enabled */  if (gnet_socks_get_enabled())    return gnet_private_socks_tcp_socket_server_new (port);  /* Set up address and port (any address, any port) */  memset (&iface, 0, sizeof(iface));  sa_in = (struct sockaddr_in*) &iface.sa;  sa_in->sin_family = AF_INET;  sa_in->sin_addr.s_addr = g_htonl(INADDR_ANY);  sa_in->sin_port = g_htons(port);  return gnet_tcp_socket_server_new_interface (&iface);}/** *  gnet_tcp_socket_server_new_interface: *  @iface: Interface to bind to * *  Create and open a new #GTcpSocket bound to the specified *  interface.  Use this sort of socket when your are a server and *  have a specific address the server must be bound to.  If the port *  number of the interface address is 0, the OS will use the *  interface specified but choose the port itself.  If the interface *  address is NULL, the OS will choose both the interface and port. *  If the interface address was created by gnet_inetaddr_new_any(), *  the OS will use all interfaces.  If the port number is also set, *  it will use that port number.  SOCKS is used if SOCKS is enabled *  and the interface is NULL. * *  Returns: a new #GTcpSocket, or NULL if there was a failure. * **/GTcpSocket* gnet_tcp_socket_server_new_interface (const GInetAddr* iface){  GTcpSocket* s;  struct sockaddr_in* sa_in;  socklen_t socklen;  /* Use SOCKS if enabled */  if (!iface && gnet_socks_get_enabled())    return gnet_private_socks_tcp_socket_server_new (0);  /* Create socket */  s = g_new0(GTcpSocket, 1);  s->ref_count = 1;  s->sockfd = socket(AF_INET, SOCK_STREAM, 0);  if (s->sockfd < 0)    goto error;  /* Set up address and port for connection */  sa_in = (struct sockaddr_in*) &s->sa;  sa_in->sin_family = AF_INET;  if (iface)    {      sa_in->sin_addr.s_addr = GNET_SOCKADDR_IN(iface->sa).sin_addr.s_addr;      sa_in->sin_port = GNET_SOCKADDR_IN(iface->sa).sin_port;    }  else    {      sa_in->sin_addr.s_addr = g_htonl(INADDR_ANY);      sa_in->sin_port = 0;    }  /* The socket is set to non-blocking mode later in the Windows     version.*/#ifndef GNET_WIN32  {    gint flags;    const int on = 1;    /* Set REUSEADDR so we can reuse the port */    if (setsockopt(s->sockfd, SOL_SOCKET, SO_REUSEADDR, 		   (void*) &on, sizeof(on)) != 0)      g_warning("Can't set reuse on tcp socket\n");    /* Get the flags (should all be 0?) */    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;  }#endif  /* Bind */  if (bind(s->sockfd, &s->sa, sizeof(s->sa)) != 0)    goto error;    /* Get the socket name */  socklen = sizeof(s->sa);  if (getsockname(s->sockfd, &s->sa, &socklen) != 0)    goto error;    /* Listen */  if (listen(s->sockfd, 10) != 0)    goto error;    return s;   error:  if (s)    		g_free(s);  return NULL;}#ifndef GNET_WIN32  /*********** Unix code ***********//** *  gnet_tcp_socket_server_accept: *  @socket: #GTcpSocket to accept connections from. * *  Accept a connection from the socket.  The socket must have been *  created using gnet_tcp_socket_server_new().  This function will *  block (use gnet_tcp_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 not block. * *  Returns: a new #GTcpSocket if there is another connect, or NULL if *  there's an error. * **/GTcpSocket* gnet_tcp_socket_server_accept (GTcpSocket* socket){  gint sockfd;  struct sockaddr sa;  socklen_t n;  fd_set fdset;  GTcpSocket* s;  g_return_val_if_fail (socket != NULL, NULL);  if (gnet_socks_get_enabled())    return gnet_private_socks_tcp_socket_server_accept(socket); 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	  errno == EINTR)	goto try_again;      return NULL;    }  s = g_new0(GTcpSocket, 1);  s->ref_count = 1;  s->sockfd = sockfd;  memcpy(&s->sa, &sa, sizeof(s->sa));  return s;}/** *  gnet_tcp_socket_server_accept_nonblock: *  @socket: GTcpSocket to accept connections from. * *  Accept a connection from the socket without blocking.  The socket *  must have been created using gnet_tcp_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 GTcpSocket if there is another connect, or NULL *  otherwise. * **/GTcpSocket* gnet_tcp_socket_server_accept_nonblock (GTcpSocket* socket){  gint sockfd;  struct sockaddr sa;  socklen_t n;  fd_set fdset;  GTcpSocket* s;  struct timeval tv = {0, 0};  g_return_val_if_fail (socket != NULL, NULL);  if (gnet_socks_get_enabled())    return gnet_private_socks_tcp_socket_server_accept(socket); 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_tcp_socket_server_accept() - it might cause a         block. */      return NULL;    }    s = g_new0(GTcpSocket, 1);  s->ref_count = 1;  s->sockfd = sockfd;  memcpy(&s->sa, &sa, sizeof(s->sa));  return s;}#else	/*********** Windows code ***********/GTcpSocket*gnet_tcp_socket_server_accept (GTcpSocket* socket){  gint sockfd;  struct sockaddr sa;  fd_set fdset;  GTcpSocket* s;  g_return_val_if_fail (socket != NULL, NULL);  if (gnet_socks_get_enabled())    return gnet_private_socks_tcp_socket_server_accept(socket);	  FD_ZERO(&fdset);  FD_SET((unsigned)socket->sockfd, &fdset);  if (select(socket->sockfd + 1, &fdset, NULL, NULL, NULL) == -1)    {      return NULL;    }  /* Don't force the socket into blocking mode */  sockfd = accept(socket->sockfd, &sa, NULL);  /* if it fails, looping isn't going to help */  if (sockfd == INVALID_SOCKET)    {      return NULL;    }  s = g_new0(GTcpSocket, 1);  s->ref_count = 1;  s->sockfd = sockfd;  memcpy(&s->sa, &sa, sizeof(s->sa));  return s;}GTcpSocket*gnet_tcp_socket_server_accept_nonblock (GTcpSocket* socket){  gint sockfd;  struct sockaddr sa;  fd_set fdset;  GTcpSocket* s;  u_long arg;  g_return_val_if_fail (socket != NULL, NULL);  if (gnet_socks_get_enabled())    return gnet_private_socks_tcp_socket_server_accept(socket);  FD_ZERO(&fdset);  FD_SET((unsigned)socket->sockfd, &fdset);  if (select(socket->sockfd + 1, &fdset, NULL, NULL, NULL) == -1)    {      return NULL;    }  /* make sure the socket is in non-blocking mode */  arg = 1;  if(ioctlsocket(socket->sockfd, FIONBIO, &arg))    return NULL;  sockfd = accept(socket->sockfd, &sa, NULL);  /* if it fails, looping isn't going to help */  if (sockfd == INVALID_SOCKET)    {      return NULL;    }  s = g_new0(GTcpSocket, 1);  s->ref_count = 1;  s->sockfd = sockfd;  memcpy(&s->sa, &sa, sizeof(s->sa));  return s;}#endif		/*********** End Windows code ***********/static gboolean tcp_socket_server_accept_async_cb (GIOChannel* iochannel, 						   GIOCondition condition, 						   gpointer data);/** *  gnet_tcp_socket_server_accept_async: *  @socket: #GTcpSocket to accept connections from. *  @accept_func: Callback function. *  @user_data: User data passed when callback function is called. * *  Accept a connection from the socket asynchronously.  The callback *  is called when a client has connection or the socket has an error. * **/voidgnet_tcp_socket_server_accept_async (GTcpSocket* socket,				     GTcpSocketAcceptFunc accept_func,				     gpointer user_data){  GIOChannel* iochannel;  g_return_if_fail (socket);  g_return_if_fail (accept_func);  g_return_if_fail (!socket->accept_func);  if (gnet_socks_get_enabled())    {      gnet_private_socks_tcp_socket_server_accept_async (socket, accept_func, user_data);      return;    }  /* Save callback */  socket->accept_func = accept_func;  socket->accept_data = user_data;  /* Add read watch */  iochannel = gnet_tcp_socket_get_iochannel (socket);  socket->accept_watch = g_io_add_watch(iochannel, 					G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, 					tcp_socket_server_accept_async_cb, socket);  g_io_channel_unref (iochannel);}static gbooleantcp_socket_server_accept_async_cb (GIOChannel* iochannel, GIOCondition condition, 				   gpointer data){  GTcpSocket* server = (GTcpSocket*) data;  g_assert (server != NULL);  if (condition & G_IO_IN)    {      GTcpSocket* client;      gboolean destroyed = FALSE;      client = gnet_tcp_socket_server_accept_nonblock (server);      if (!client) 	return TRUE;      /* Do upcall, protected by a ref */      gnet_tcp_socket_ref (server);      (server->accept_func)(server, client, server->accept_data);      if (server->ref_count == 1)	destroyed = TRUE;      gnet_tcp_socket_unref (server);      if (destroyed || !server->accept_watch)	return FALSE;    }  else    {      gnet_tcp_socket_ref (server);      (server->accept_func)(server, NULL, server->accept_data);      server->accept_watch = 0;      server->accept_func = NULL;      server->accept_data = NULL;      gnet_tcp_socket_unref (server);      return FALSE;    }  return TRUE;}/** *  gnet_tcp_socket_server_accept_async_cancel: *  @socket: #GTcpSocket accepting connections asynchronously. * *  Stops accepting connections asynchronously from the socket.  This *  does not close the socket. * **/voidgnet_tcp_socket_server_accept_async_cancel (GTcpSocket* socket){  g_return_if_fail (socket);  if (!socket->accept_watch)    return;  socket->accept_func = NULL;  socket->accept_data = NULL;  g_source_remove (socket->accept_watch);  socket->accept_watch = 0;}

⌨️ 快捷键说明

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