📄 socket.c
字号:
/* * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2000-2003 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. *//* $Id: socket.c,v 1.5.2.13.2.13 2004/09/01 04:32:18 marka Exp $ *//* This code has been rewritten to take advantage of Windows Sockets * I/O Completion Ports and Events. I/O Completion Ports is ONLY * available on Windows NT, Windows 2000 and Windows XP series of * the Windows Operating Systems. In CANNOT run on Windows 95, Windows 98 * or the follow-ons to those Systems. * * This code is by nature multithreaded and takes advantage of various * features to pass on information through the completion port for * when I/O is completed. All sends and receives are completed through * the completion port. Due to an implementation bug in Windows 2000, * Service Pack 2 must installed on the system for this code to run correctly. * For details on this problem see Knowledge base article Q263823. * The code checks for this. The number of Completion Port Worker threads * used is the total number of CPU's + 1. This increases the likelihood that * a Worker Thread is available for processing a completed request. * * All accepts and connects are accomplished through the WSAEventSelect() * function and the event_wait loop. Events are added to and deleted from * each event_wait thread via a common event_update stack owned by the socket * manager. If the event_wait thread runs out of array space in the events * array it will look for another event_wait thread to add the event. If it * fails to find another one it will create a new thread to handle the * outstanding event. * * A future enhancement is to use AcceptEx to take avantage of Overlapped * I/O which allows for enhanced performance of TCP connections. * This will also reduce the number of events that are waited on by the * event_wait threads to just the connect sockets and reduce the number * additional threads required. * * XXXPDM 5 August, 2002 */#define MAKE_EXTERNAL 1#include <config.h>#include <sys/types.h>#ifndef _WINSOCKAPI_#define _WINSOCKAPI_ /* Prevent inclusion of winsock.h in windows.h */#endif#include <errno.h>#include <stddef.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <io.h>#include <fcntl.h>#include <process.h>#include <isc/buffer.h>#include <isc/bufferlist.h>#include <isc/condition.h>#include <isc/list.h>#include <isc/log.h>#include <isc/mem.h>#include <isc/msgs.h>#include <isc/mutex.h>#include <isc/net.h>#include <isc/os.h>#include <isc/platform.h>#include <isc/print.h>#include <isc/region.h>#include <isc/socket.h>#include <isc/strerror.h>#include <isc/syslog.h>#include <isc/task.h>#include <isc/thread.h>#include <isc/util.h>#include <isc/win32os.h>#include "errno2result.h"/* * Define this macro to control the behavior of connection * resets on UDP sockets. See Microsoft KnowledgeBase Article Q263823 * for details. * NOTE: This requires that Windows 2000 systems install Service Pack 2 * or later. */#ifndef SIO_UDP_CONNRESET #define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12) #endif/* * Some systems define the socket length argument as an int, some as size_t, * some as socklen_t. This is here so it can be easily changed if needed. */#ifndef ISC_SOCKADDR_LEN_T#define ISC_SOCKADDR_LEN_T unsigned int#endif/* * Define what the possible "soft" errors can be. These are non-fatal returns * of various network related functions, like recv() and so on. * * For some reason, BSDI (and perhaps others) will sometimes return <0 * from recv() but will have errno==0. This is broken, but we have to * work around it here. */#define SOFT_ERROR(e) ((e) == WSAEINTR || \ (e) == WSA_IO_PENDING || \ (e) == WSAEWOULDBLOCK || \ (e) == EWOULDBLOCK || \ (e) == EINTR || \ (e) == EAGAIN || \ (e) == 0)#define DLVL(x) ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_SOCKET, ISC_LOG_DEBUG(x)/* * DLVL(90) -- Function entry/exit and other tracing. * DLVL(70) -- Socket "correctness" -- including returning of events, etc. * DLVL(60) -- Socket data send/receive * DLVL(50) -- Event tracing, including receiving/sending completion events. * DLVL(20) -- Socket creation/destruction. */#define TRACE_LEVEL 90#define CORRECTNESS_LEVEL 70#define IOEVENT_LEVEL 60#define EVENT_LEVEL 50#define CREATION_LEVEL 20#define TRACE DLVL(TRACE_LEVEL)#define CORRECTNESS DLVL(CORRECTNESS_LEVEL)#define IOEVENT DLVL(IOEVENT_LEVEL)#define EVENT DLVL(EVENT_LEVEL)#define CREATION DLVL(CREATION_LEVEL)typedef isc_event_t intev_t;#define SOCKET_MAGIC ISC_MAGIC('I', 'O', 'i', 'o')#define VALID_SOCKET(t) ISC_MAGIC_VALID(t, SOCKET_MAGIC)/* * IPv6 control information. If the socket is an IPv6 socket we want * to collect the destination address and interface so the client can * set them on outgoing packets. */#ifdef ISC_PLATFORM_HAVEIPV6#ifndef USE_CMSG#define USE_CMSG 1#endif#endif/* * NetBSD and FreeBSD can timestamp packets. XXXMLG Should we have * a setsockopt() like interface to request timestamps, and if the OS * doesn't do it for us, call gettimeofday() on every UDP receive? *//* * We really don't want to try and use these control messages. Win32 * doesn't have this mechanism */#undef USE_CMSG/* * Message header for recvmsg and sendmsg calls. * Used value-result for recvmsg, value only for sendmsg. */struct msghdr { void *msg_name; /* optional address */ u_int msg_namelen; /* size of address */ WSABUF *msg_iov; /* scatter/gather array */ u_int msg_iovlen; /* # elements in msg_iov */ void *msg_control; /* ancillary data, see below */ u_int msg_controllen; /* ancillary data buffer len */ int msg_flags; /* flags on received message */} msghdr; /* * The number of times a send operation is repeated if the result is EINTR. */#define NRETRIES 10struct isc_socket { /* Not locked. */ unsigned int magic; isc_socketmgr_t *manager; isc_mutex_t lock; isc_sockettype_t type; OVERLAPPED overlapped; /* Pointers to scatter/gather buffers */ WSABUF iov[ISC_SOCKET_MAXSCATTERGATHER]; size_t totalBytes; WSAEVENT hEvent; /* Event Handle */ long wait_type; /* Events to wait on */ WSAEVENT hAlert; /* Alert Event Handle */ DWORD evthread_id; /* Event Thread Id for socket */ /* Locked by socket lock. */ ISC_LINK(isc_socket_t) link; unsigned int references; SOCKET fd; int pf; ISC_LIST(isc_socketevent_t) send_list; ISC_LIST(isc_socketevent_t) recv_list; ISC_LIST(isc_socket_newconnev_t) accept_list; isc_socket_connev_t *connect_ev; /* * Internal events. Posted when a descriptor is readable or * writable. These are statically allocated and never freed. * They will be set to non-purgable before use. */ intev_t readable_ev; intev_t writable_ev; isc_sockaddr_t address; /* remote address */ unsigned int pending_close : 1, pending_accept : 1, iocp : 1, /* I/O Completion Port */ listener : 1, /* listener socket */ connected : 1, connecting : 1, /* connect pending */ bound : 1, /* bound to local addr */ pending_free: 1; unsigned int pending_recv; unsigned int pending_send;};/* * I/O Completion ports Info structures */static HANDLE hHeapHandle = NULL;static int iocp_total = 0;typedef struct IoCompletionInfo { OVERLAPPED overlapped; isc_socketevent_t *dev; int request_type; struct msghdr messagehdr;} IoCompletionInfo;/* * Define a maximum number of I/O Completion Port worker threads * to handle the load on the Completion Port. The actual number * used is the number of CPU's + 1. */#define MAX_IOCPTHREADS 20/* * event_change structure to handle adds and deletes from the list of * events in the Wait */typedef struct event_change event_change_t;struct event_change { isc_socket_t *sock; WSAEVENT hEvent; DWORD evthread_id; SOCKET fd; unsigned int action; ISC_LINK(event_change_t) link;};/* * Note: We are using an array here since *WaitForMultiple* wants an array * WARNING: This value may not be greater than 64 since the * WSAWaitForMultipleEvents function is limited to 64 events. */#define MAX_EVENTS 64/* * List of events being waited on and their associated sockets */typedef struct sock_event_list { int max_event; int total_events; isc_socket_t *aSockList[MAX_EVENTS]; WSAEVENT aEventList[MAX_EVENTS];} sock_event_list;/* * Thread Event structure for managing the threads handling events */typedef struct events_thread events_thread_t;struct events_thread { isc_thread_t thread_handle; /* Thread's handle */ DWORD thread_id; /* Thread's id */ sock_event_list sockev_list; isc_socketmgr_t *manager; ISC_LINK(events_thread_t) link;};#define SOCKET_MANAGER_MAGIC ISC_MAGIC('I', 'O', 'm', 'g')#define VALID_MANAGER(m) ISC_MAGIC_VALID(m, SOCKET_MANAGER_MAGIC)struct isc_socketmgr { /* Not locked. */ unsigned int magic; isc_mem_t *mctx; isc_mutex_t lock; /* Locked by manager lock. */ ISC_LIST(event_change_t) event_updates; ISC_LIST(isc_socket_t) socklist; int event_written; WSAEVENT prime_alert; isc_boolean_t bShutdown; ISC_LIST(events_thread_t) ev_threads; isc_condition_t shutdown_ok; HANDLE hIoCompletionPort; int maxIOCPThreads; HANDLE hIOCPThreads[MAX_IOCPTHREADS]; DWORD dwIOCPThreadIds[MAX_IOCPTHREADS];};#define CLOSED 0 /* this one must be zero */#define MANAGED 1#define CLOSE_PENDING 2/* * send() and recv() iovec counts */#define MAXSCATTERGATHER_SEND (ISC_SOCKET_MAXSCATTERGATHER)#define MAXSCATTERGATHER_RECV (ISC_SOCKET_MAXSCATTERGATHER)static isc_threadresult_t WINAPI event_wait(void *uap);static isc_threadresult_t WINAPI SocketIoThread(LPVOID ThreadContext);static void free_socket(isc_socket_t **);enum { SOCKET_RECV, SOCKET_SEND,};enum { EVENT_ADD, EVENT_DELETE};#if defined(ISC_SOCKET_DEBUG)/* * This is used to dump the contents of the sock structure * You should make sure that the sock is locked before * dumping it. Since the code uses simple printf() statements * it should only be used interactively. */voidsock_dump(isc_socket_t *sock) { isc_socketevent_t *ldev; isc_socket_newconnev_t *ndev; isc_sockaddr_t addr; char socktext[256]; isc_socket_getpeername(sock, &addr); isc_sockaddr_format(&addr, socktext, sizeof(socktext)); printf("Remote Socket: %s\n", socktext); isc_socket_getsockname(sock, &addr); isc_sockaddr_format(&addr, socktext, sizeof(socktext)); printf("This Socket: %s\n", socktext); printf("\n\t\tSock Dump\n"); printf("\t\tfd: %u\n", sock->fd); printf("\t\treferences: %d\n", sock->references); printf("\t\tpending_accept: %d\n", sock->pending_accept); printf("\t\tpending_close: %d\n", sock->pending_close); printf("\t\tconnecting: %d\n", sock->connecting); printf("\t\tconnected: %d\n", sock->connected); printf("\t\tbound: %d\n", sock->bound); printf("\t\tiocp: %d\n", sock->iocp); printf("\t\tsocket type: %d\n", sock->type); printf("\n\t\tSock Recv List\n"); ldev = ISC_LIST_HEAD(sock->recv_list); while (ldev != NULL) { printf("\t\tdev: %p\n", ldev); ldev = ISC_LIST_NEXT(ldev, ev_link); } printf("\n\t\tSock Send List\n"); ldev = ISC_LIST_HEAD(sock->send_list); while (ldev != NULL) { printf("\t\tdev: %p\n", ldev); ldev = ISC_LIST_NEXT(ldev, ev_link); } printf("\n\t\tSock Accept List\n"); ndev = ISC_LIST_HEAD(sock->accept_list); while (ndev != NULL) { printf("\t\tdev: %p\n", ldev); ndev = ISC_LIST_NEXT(ndev, ev_link); }}#endif/* This function will add an entry to the I/O completion port * that will signal the I/O thread to exit (gracefully) */static voidsignal_iocompletionport_exit(isc_socketmgr_t *manager) { int i; int errval; char strbuf[ISC_STRERRORSIZE]; REQUIRE(VALID_MANAGER(manager)); for (i = 0; i < manager->maxIOCPThreads; i++) { if (!PostQueuedCompletionStatus(manager->hIoCompletionPort, 0, 0, 0)) { errval = GetLastError(); isc__strerror(errval, strbuf, sizeof(strbuf)); FATAL_ERROR(__FILE__, __LINE__, isc_msgcat_get(isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_FAILED, "Can't request service thread to exit: %s"), strbuf); } }}/* * Create the worker threads for the I/O Completion Port */voidiocompletionport_createthreads(int total_threads, isc_socketmgr_t *manager) { int errval; char strbuf[ISC_STRERRORSIZE]; int i; INSIST(total_threads > 0); REQUIRE(VALID_MANAGER(manager)); /* * We need at least one */ for (i = 0; i < total_threads; i++) { manager->hIOCPThreads[i] = CreateThread(NULL, 0, SocketIoThread, manager, 0, &manager->dwIOCPThreadIds[i]); if(manager->hIOCPThreads[i] == NULL) { errval = GetLastError(); isc__strerror(errval, strbuf, sizeof(strbuf)); FATAL_ERROR(__FILE__, __LINE__, isc_msgcat_get(isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_FAILED, "Can't create IOCP thread: %s"), strbuf); } }}/* * Create/initialise the I/O completion port */voidiocompletionport_init(isc_socketmgr_t *manager) { int errval; char strbuf[ISC_STRERRORSIZE]; REQUIRE(VALID_MANAGER(manager)); /* * Create a private heap to handle the socket overlapped structure * The miniumum number of structures is 10, there is no maximum */ hHeapHandle = HeapCreate(0, 10*sizeof(IoCompletionInfo), 0); manager->maxIOCPThreads = min(isc_os_ncpus() + 1, MAX_IOCPTHREADS); /* Now Create the Completion Port */ manager->hIoCompletionPort = CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, 0, manager->maxIOCPThreads); if (manager->hIoCompletionPort == NULL) { errval = GetLastError(); isc__strerror(errval, strbuf, sizeof(strbuf)); FATAL_ERROR(__FILE__, __LINE__, isc_msgcat_get(isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_FAILED, "CreateIoCompletionPort() failed " "during initialization: %s"), strbuf); exit(1); } /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -