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 + -
显示快捷键?