tcp.c

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

C
1,257
字号
/* GNet - Networking library * Copyright (C) 2000  David Helder * Copyright (C) 2000  Andrew Lanoix * * 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 "socks-private.h"#include "tcp.h"/** *  gnet_tcp_socket_connect: *  @hostname: Name of host to connect to. *  @port: Port to connect to. * *  A quick and easy #GTcpSocket constructor.  This connects to the *  specified address and port.  This function does block *  (gnet_tcp_socket_connect_async() does not).  Use this function *  when you're a client connecting to a server and you don't mind *  blocking and don't want to mess with #GInetAddr's.  You can get *  the InetAddr of the socket by calling *  gnet_tcp_socket_get_inetaddr(). * *  Returns: A new #GTcpSocket, or NULL if there was a failure. **/GTcpSocket*gnet_tcp_socket_connect (const gchar* hostname, gint port){  GInetAddr* ia;  GTcpSocket* socket;  ia = gnet_inetaddr_new(hostname, port);  if (ia == NULL)    return NULL;  socket = gnet_tcp_socket_new(ia);  gnet_inetaddr_delete(ia);  return socket;}/** *  gnet_tcp_socket_connect_async: *  @hostname: Name of host to connect to *  @port: Port to connect to *  @func: Callback function *  @data: User data passed when callback function is called. * *  A quick and easy asynchronous #GTcpSocket constructor.  This *  connects to the specified address and port and then calls the *  callback with the data.  Use this function when you're a client *  connecting to a server and you don't want to block or mess with *  #GInetAddr's.  It will call the callback if there is a failure. *  It will never call the callback before the function returns. * *  Returns: ID of the connection which can be used with *  gnet_tcp_socket_connect_async_cancel() to cancel it; NULL on *  failure. * **/GTcpSocketConnectAsyncIDgnet_tcp_socket_connect_async (const gchar* hostname, gint port, 			       GTcpSocketConnectAsyncFunc func, 			       gpointer data){  GTcpSocketConnectState* state;  g_return_val_if_fail(hostname != NULL, NULL);  g_return_val_if_fail(func != NULL, NULL);  state = g_new0(GTcpSocketConnectState, 1);  state->func = func;  state->data = data;  state->inetaddr_id =     gnet_inetaddr_new_async (hostname, port,  			     gnet_tcp_socket_connect_inetaddr_cb, 			     state);  /* On failure, gnet_inetaddr_new_async() returns NULL.  It will not     call the callback before it returns. */  if (state->inetaddr_id == NULL)    {      g_free (state);      return NULL;    }  return state;}voidgnet_tcp_socket_connect_inetaddr_cb (GInetAddr* inetaddr, 				     GInetAddrAsyncStatus status, 				     gpointer data){  GTcpSocketConnectState* state = (GTcpSocketConnectState*) data;  if (status == GINETADDR_ASYNC_STATUS_OK)    {      gpointer tcp_id;      state->ia = gnet_inetaddr_clone (inetaddr);      state->inetaddr_id = NULL;      tcp_id = gnet_tcp_socket_new_async (inetaddr, 					  gnet_tcp_socket_connect_tcp_cb, 					  state);      /* gnet_tcp_socket_new_async() may call the callback before it	 returns.  state may have been deleted. */      if (tcp_id)	state->tcp_id = tcp_id;    }  else    {      (*state->func)(NULL, NULL, 		     GTCP_SOCKET_CONNECT_ASYNC_STATUS_INETADDR_ERROR, 		     state->data);      g_free(state);    }}void gnet_tcp_socket_connect_tcp_cb (GTcpSocket* socket, 				GTcpSocketConnectAsyncStatus status, 				gpointer data){  GTcpSocketConnectState* state = (GTcpSocketConnectState*) data;  if (status == GTCP_SOCKET_NEW_ASYNC_STATUS_OK)    {      (*state->func)(socket, state->ia, 		     GTCP_SOCKET_CONNECT_ASYNC_STATUS_OK, state->data);    }  else    {      (*state->func)(NULL, NULL, 		     GTCP_SOCKET_CONNECT_ASYNC_STATUS_TCP_ERROR, state->data);      gnet_inetaddr_delete (state->ia);    }  g_free (state);}/** *  gnet_tcp_socket_connect_async_cancel: *  @id: ID of the connection. * *  Cancel an asynchronous connection that was started with *  gnet_tcp_socket_connect_async(). *  */voidgnet_tcp_socket_connect_async_cancel (GTcpSocketConnectAsyncID id){  GTcpSocketConnectState* state = (GTcpSocketConnectState*) id;  g_return_if_fail (state != NULL);  if (state->inetaddr_id)    {      gnet_inetaddr_new_async_cancel(state->inetaddr_id);    }  else if (state->tcp_id)    {      gnet_inetaddr_delete (state->ia);      gnet_tcp_socket_new_async_cancel (state->tcp_id);    }  else    g_assert_not_reached();  g_free (state);}/* **************************************** *//** *  gnet_tcp_socket_new: *  @addr: Address 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.  SOCKS is used if SOCKS is enabled. * *  Returns a new #GTcpSocket, or NULL if there was a failure. * **/GTcpSocket* gnet_tcp_socket_new (const GInetAddr* addr){  g_return_val_if_fail (addr != NULL, NULL);  /* Use SOCKS if enabled */  if (gnet_socks_get_enabled())    return gnet_private_socks_tcp_socket_new (addr);  /* Otherwise, connect directly to the address */  return gnet_tcp_socket_new_direct (addr);}/** *  gnet_tcp_socket_new_direct: *  @addr: Address to connect to. * *  Connect directly to a specified address and do not use SOCKS even *  if SOCKS is enabled.  Most users should use gnet_tcp_socket_new(). *  This is used internally to implement SOCKS. * *  Returns a new #GTcpSocket, or NULL if there was a failure. * **/GTcpSocket* gnet_tcp_socket_new_direct (const GInetAddr* addr){  int 			sockfd;  GTcpSocket* 		s;  struct sockaddr_in* 	sa_in;  int			rv;  g_return_val_if_fail (addr != NULL, NULL);  /* Create socket */  sockfd = socket (AF_INET, SOCK_STREAM, 0);  if (sockfd < 0)    return NULL;  /* Create GTcpSocket */  s = g_new0 (GTcpSocket, 1);  s->sockfd = sockfd;  s->ref_count = 1;  /* Set up address and port for connection */  memcpy(&s->sa, &addr->sa, sizeof(s->sa));  sa_in = (struct sockaddr_in*) &s->sa;  sa_in->sin_family = AF_INET;  /* Connect */  rv = connect(sockfd, &s->sa, sizeof(s->sa));  if (rv != 0)    {      GNET_CLOSE_SOCKET(s->sockfd);      g_free (s);      return NULL;    }  return s;}/* **************************************** *//** *  gnet_tcp_socket_new_async: *  @addr: Address to connect to. *  @func: Callback function. *  @data: User data passed when callback function is called. * *  Connect to a specifed address asynchronously.  When the connection *  is complete or there is an error, it will call the callback.  It *  may call the callback before the function returns.  It will call *  the callback if there is a failure.  SOCKS is used if SOCKS is *  enabled.  The SOCKS negotiation will block. * *  Returns: ID of the connection which can be used with *  gnet_tcp_socket_connect_async_cancel() to cancel it; NULL on *  failure. * **/GTcpSocketNewAsyncIDgnet_tcp_socket_new_async (const GInetAddr* addr, 			   GTcpSocketNewAsyncFunc func,			   gpointer data){  g_return_val_if_fail (addr != NULL, NULL);  g_return_val_if_fail (func != NULL, NULL);  /* Use SOCKS if enabled */  if (gnet_socks_get_enabled())    return gnet_private_socks_tcp_socket_new_async (addr, func, data);  /* Otherwise, connect directly to the address */  return gnet_tcp_socket_new_async_direct (addr, func, data);}#ifndef GNET_WIN32  /*********** Unix code ***********//** *  gnet_tcp_socket_new_async_direct: *  @addr: Address to connect to. * *  Connect directly to a specified address asynchronously and do not *  use SOCKS even if SOCKS is enabled.  Most users should use *  gnet_tcp_socket_new_async().  This is used internally to implement *  SOCKS. * *  Returns a new #GTcpSocket, or NULL if there was a failure. * **/GTcpSocketNewAsyncIDgnet_tcp_socket_new_async_direct (const GInetAddr* addr, 				  GTcpSocketNewAsyncFunc func,				  gpointer data){  gint 			sockfd;  gint 			flags;  GTcpSocket* 		s;  struct sockaddr	sa;  struct sockaddr_in* 	sa_in;  GTcpSocketAsyncState* state;  g_return_val_if_fail(addr != NULL, NULL);  g_return_val_if_fail(func != NULL, NULL);  /* Create socket */  sockfd = socket(AF_INET, SOCK_STREAM, 0);  if (sockfd < 0)    {      (func)(NULL, GTCP_SOCKET_NEW_ASYNC_STATUS_ERROR, data);      return NULL;    }  /* Get the flags (should all be 0?) */  flags = fcntl(sockfd, F_GETFL, 0);  if (flags == -1)    {      (func)(NULL, GTCP_SOCKET_NEW_ASYNC_STATUS_ERROR, data);      return NULL;    }  if (fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) == -1)    {      (func)(NULL, GTCP_SOCKET_NEW_ASYNC_STATUS_ERROR, data);      return NULL;    }  /* Create our structure */  s = g_new0(GTcpSocket, 1);  s->ref_count = 1;  s->sockfd = sockfd;  /* Set up address and port for connection */  memcpy(&sa, &addr->sa, sizeof(sa));  sa_in = (struct sockaddr_in*) &sa;  sa_in->sin_family = AF_INET;  /* Connect (but non-blocking!) */  if (connect(s->sockfd, &sa, sizeof(s->sa)) < 0)    {      if (errno != EINPROGRESS)	{	  (func)(NULL, GTCP_SOCKET_NEW_ASYNC_STATUS_ERROR, data);	  g_free(s);	  return NULL;	}    }  /* Save address */   memcpy(&s->sa, &addr->sa, sizeof(s->sa));  sa_in = (struct sockaddr_in*) &sa;  sa_in->sin_family = AF_INET;  /* Note that if connect returns 0, then we're already connected and     we could call the call back immediately.  But, it would probably     make things too complicated for the user if we could call the     callback before we returned from this function.  */  /* Wait for the connection */  state = g_new0(GTcpSocketAsyncState, 1);  state->socket = s;  state->func = func;  state->data = data;  state->flags = flags;  state->iochannel = gnet_private_iochannel_new(s->sockfd);  state->connect_watch = g_io_add_watch(state->iochannel,					GNET_ANY_IO_CONDITION,					gnet_tcp_socket_new_async_cb, 					state);  return state;}gboolean gnet_tcp_socket_new_async_cb (GIOChannel* iochannel, 			      GIOCondition condition, 			      gpointer data){  GTcpSocketAsyncState* state = (GTcpSocketAsyncState*) data;  gint error, len;  g_source_remove (state->connect_watch);  state->connect_watch = 0;  g_io_channel_unref (state->iochannel);  state->iochannel = NULL;  errno = 0;  if (!((condition & G_IO_IN) || (condition & G_IO_OUT)))    goto error;  len = sizeof(error);  /* Get the error option */  if (getsockopt(state->socket->sockfd, SOL_SOCKET, SO_ERROR, (void*) &error, &len) < 0)    goto error;  /* Check if there is an error */  if (error)    goto error;  /* Reset the flags */  if (fcntl(state->socket->sockfd, F_SETFL, state->flags) != 0)    goto error;  /* Success */  (*state->func)(state->socket, GTCP_SOCKET_NEW_ASYNC_STATUS_OK, state->data);  g_free(state);  return FALSE;  /* Error */ error:  (*state->func)(NULL, GTCP_SOCKET_NEW_ASYNC_STATUS_ERROR, state->data);  gnet_tcp_socket_delete (state->socket);  g_free(state);  return FALSE;}/** *  gnet_tcp_socket_new_async_cancel: *  @id: ID of the connection. * *  Cancel an asynchronous connection that was started with *  gnet_tcp_socket_new_async(). * **/voidgnet_tcp_socket_new_async_cancel (GTcpSocketNewAsyncID id){  GTcpSocketAsyncState* state = (GTcpSocketAsyncState*) id;  if (state->connect_watch)    g_source_remove(state->connect_watch);  if (state->iochannel)    g_io_channel_unref (state->iochannel);  gnet_tcp_socket_delete (state->socket);  g_free (state);}#else	/*********** Windows code ***********/GTcpSocketNewAsyncIDgnet_tcp_socket_new_async_direct (const GInetAddr* addr,				  GTcpSocketNewAsyncFunc func,				  gpointer data){  gint sockfd;  gint status;  GTcpSocket* s;  struct sockaddr sa;  struct sockaddr_in* sa_in;  GTcpSocketAsyncState* state;  u_long arg;  g_return_val_if_fail(addr != NULL, NULL);  g_return_val_if_fail(func != NULL, NULL);  /* Create socket */  sockfd = socket(AF_INET, SOCK_STREAM, 0);  if (sockfd == INVALID_SOCKET)    {      (func)(NULL, GTCP_SOCKET_NEW_ASYNC_STATUS_ERROR, data);      return NULL;    }	  /* Create our structure */  s = g_new0(GTcpSocket, 1);  s->ref_count = 1;  s->sockfd = sockfd;  /* Set up address and port for connection */  memcpy(&sa, &addr->sa, sizeof(sa));  sa_in = (struct sockaddr_in*) &sa;  sa_in->sin_family = AF_INET;  /* Force the socket into non-blocking mode */  arg = 1;  ioctlsocket(sockfd, FIONBIO, &arg);  status = connect(s->sockfd, &sa, sizeof(s->sa));  if (status == SOCKET_ERROR) /* Returning an error is ok, unless.. */    {      status = WSAGetLastError();      if (status != WSAEWOULDBLOCK)	{	  (func)(NULL, GTCP_SOCKET_NEW_ASYNC_STATUS_ERROR, data);	  g_free(s);	  return NULL;	}    }  /* Save address */   memcpy(&s->sa, &addr->sa, sizeof(s->sa));  sa_in = (struct sockaddr_in*) &sa;  sa_in->sin_family = AF_INET;  /* Wait for the connection */  state = g_new0(GTcpSocketAsyncState, 1);  state->socket = s;  state->func = func;  state->data = data;  state->socket->sockfd = sockfd;  state->connect_watch =     g_io_add_watch(gnet_private_iochannel_new(s->sockfd),		   G_IO_IN | G_IO_ERR,		   gnet_tcp_socket_new_async_cb, 		   state);  if (state->connect_watch <= 0)    {      (func)(NULL, GTCP_SOCKET_NEW_ASYNC_STATUS_ERROR, data);      return NULL;    }  return state;}gbooleangnet_tcp_socket_new_async_cb (GIOChannel* iochannel,			      GIOCondition condition,			      gpointer data){  GTcpSocketAsyncState* state = (GTcpSocketAsyncState*) data;  if (condition & G_IO_ERR)    goto error;  (*state->func)(state->socket, GTCP_SOCKET_NEW_ASYNC_STATUS_OK, state->data);  g_free (state);  return FALSE; error:  (*state->func)(NULL, GTCP_SOCKET_NEW_ASYNC_STATUS_ERROR, state->data);  gnet_tcp_socket_delete (state->socket);  g_free (state);  return FALSE;}voidgnet_tcp_socket_new_async_cancel (GTcpSocketNewAsyncID id){  GTcpSocketAsyncState* state = (GTcpSocketAsyncState*) id;	  g_source_remove(state->connect_watch);  gnet_tcp_socket_delete(state->socket);  g_free (state);}#endif		/*********** End Windows code ***********//** *  gnet_tcp_socket_delete: *  @s: TcpSocket to delete. * *  Close and delete a #GTcpSocket. * **/voidgnet_tcp_socket_delete(GTcpSocket* s){  if (s != NULL)    gnet_tcp_socket_unref(s);}/** *  gnet_tcp_socket_ref *  @s: #GTcpSocket to reference * *  Increment the reference counter of the GTcpSocket. * **/voidgnet_tcp_socket_ref(GTcpSocket* s){  g_return_if_fail(s != NULL);  ++s->ref_count;}/**

⌨️ 快捷键说明

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