📄 tclwinsock.c
字号:
/* * tclWinSock.c -- * * This file contains Windows-specific socket related code. * * Copyright (c) 1995-1997 Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * RCS: @(#) $Id: tclWinSock.c,v 1.36 2003/01/16 19:02:00 mdejong Exp $ */#include "tclWinInt.h"/* * Make sure to remove the redirection defines set in tclWinPort.h * that is in use in other sections of the core, except for us. */#undef getservbyname#undef getsockopt#undef ntohs#undef setsockopt/* * The following variable is used to tell whether this module has been * initialized. */static int initialized = 0;static int hostnameInitialized = 0;static char hostname[255]; /* This buffer should be big enough for * hostname plus domain name. */TCL_DECLARE_MUTEX(socketMutex)/* * Mingw and Cygwin may not have LPFN_* typedefs. */#ifdef HAVE_NO_LPFN_DECLS typedef SOCKET (PASCAL FAR *LPFN_ACCEPT)(SOCKET s, struct sockaddr FAR * addr, int FAR * addrlen); typedef int (PASCAL FAR *LPFN_BIND)(SOCKET s, const struct sockaddr FAR *addr, int namelen); typedef int (PASCAL FAR *LPFN_CLOSESOCKET)(SOCKET s); typedef int (PASCAL FAR *LPFN_CONNECT)(SOCKET s, const struct sockaddr FAR *name, int namelen); typedef struct hostent FAR * (PASCAL FAR *LPFN_GETHOSTBYADDR) (const char FAR *addr, int addrlen, int addrtype); typedef struct hostent FAR * (PASCAL FAR *LPFN_GETHOSTBYNAME) (const char FAR * name); typedef int (PASCAL FAR *LPFN_GETHOSTNAME)(char FAR * name, int namelen); typedef int (PASCAL FAR *LPFN_GETPEERNAME)(SOCKET sock, struct sockaddr FAR *name, int FAR *namelen); typedef struct servent FAR * (PASCAL FAR *LPFN_GETSERVBYNAME) (const char FAR * name, const char FAR * proto); typedef int (PASCAL FAR *LPFN_GETSOCKNAME)(SOCKET sock, struct sockaddr FAR *name, int FAR *namelen); typedef int (PASCAL FAR *LPFN_GETSOCKOPT)(SOCKET s, int level, int optname, char FAR * optval, int FAR *optlen); typedef u_short (PASCAL FAR *LPFN_HTONS)(u_short hostshort); typedef unsigned long (PASCAL FAR *LPFN_INET_ADDR) (const char FAR * cp); typedef char FAR * (PASCAL FAR *LPFN_INET_NTOA) (struct in_addr in); typedef int (PASCAL FAR *LPFN_IOCTLSOCKET)(SOCKET s, long cmd, u_long FAR *argp); typedef int (PASCAL FAR *LPFN_LISTEN)(SOCKET s, int backlog); typedef u_short (PASCAL FAR *LPFN_NTOHS)(u_short netshort); typedef int (PASCAL FAR *LPFN_RECV)(SOCKET s, char FAR * buf, int len, int flags); typedef int (PASCAL FAR *LPFN_SELECT)(int nfds, fd_set FAR * readfds, fd_set FAR * writefds, fd_set FAR * exceptfds, const struct timeval FAR * timeout); typedef int (PASCAL FAR *LPFN_SEND)(SOCKET s, const char FAR * buf, int len, int flags); typedef int (PASCAL FAR *LPFN_SETSOCKOPT)(SOCKET s, int level, int optname, const char FAR * optval, int optlen); typedef SOCKET (PASCAL FAR *LPFN_SOCKET)(int af, int type, int protocol); typedef int (PASCAL FAR *LPFN_WSAASYNCSELECT)(SOCKET s, HWND hWnd, u_int wMsg, long lEvent); typedef int (PASCAL FAR *LPFN_WSACLEANUP)(void); typedef int (PASCAL FAR *LPFN_WSAGETLASTERROR)(void); typedef int (PASCAL FAR *LPFN_WSASTARTUP)(WORD wVersionRequired, LPWSADATA lpWSAData);#endif/* * The following structure contains pointers to all of the WinSock API * entry points used by Tcl. It is initialized by InitSockets. Since * we dynamically load the Winsock DLL on demand, we must use this * function table to refer to functions in the winsock API. */static struct { HMODULE hModule; /* Handle to WinSock library. */ /* Winsock 1.1 functions */ LPFN_ACCEPT accept; LPFN_BIND bind; LPFN_CLOSESOCKET closesocket; LPFN_CONNECT connect; LPFN_GETHOSTBYADDR gethostbyaddr; LPFN_GETHOSTBYNAME gethostbyname; LPFN_GETHOSTNAME gethostname; LPFN_GETPEERNAME getpeername; LPFN_GETSERVBYNAME getservbyname; LPFN_GETSOCKNAME getsockname; LPFN_GETSOCKOPT getsockopt; LPFN_HTONS htons; LPFN_INET_ADDR inet_addr; LPFN_INET_NTOA inet_ntoa; LPFN_IOCTLSOCKET ioctlsocket; LPFN_LISTEN listen; LPFN_NTOHS ntohs; LPFN_RECV recv; LPFN_SELECT select; LPFN_SEND send; LPFN_SETSOCKOPT setsockopt; LPFN_SOCKET socket; LPFN_WSAASYNCSELECT WSAAsyncSelect; LPFN_WSACLEANUP WSACleanup; LPFN_WSAGETLASTERROR WSAGetLastError; LPFN_WSASTARTUP WSAStartup;} winSock;/* * The following defines declare the messages used on socket windows. */#define SOCKET_MESSAGE WM_USER+1#define SOCKET_SELECT WM_USER+2#define SOCKET_TERMINATE WM_USER+3#define SELECT TRUE#define UNSELECT FALSE/* * The following structure is used to store the data associated with * each socket. */typedef struct SocketInfo { Tcl_Channel channel; /* Channel associated with this * socket. */ SOCKET socket; /* Windows SOCKET handle. */ int flags; /* Bit field comprised of the flags * described below. */ int watchEvents; /* OR'ed combination of FD_READ, * FD_WRITE, FD_CLOSE, FD_ACCEPT and * FD_CONNECT that indicate which * events are interesting. */ int readyEvents; /* OR'ed combination of FD_READ, * FD_WRITE, FD_CLOSE, FD_ACCEPT and * FD_CONNECT that indicate which * events have occurred. */ int selectEvents; /* OR'ed combination of FD_READ, * FD_WRITE, FD_CLOSE, FD_ACCEPT and * FD_CONNECT that indicate which * events are currently being * selected. */ int acceptEventCount; /* Count of the current number of * FD_ACCEPTs that have arrived and * not yet processed. */ Tcl_TcpAcceptProc *acceptProc; /* Proc to call on accept. */ ClientData acceptProcData; /* The data for the accept proc. */ int lastError; /* Error code from last message. */ struct SocketInfo *nextPtr; /* The next socket on the per-thread * socket list. */} SocketInfo;/* * The following structure is what is added to the Tcl event queue when * a socket event occurs. */typedef struct SocketEvent { Tcl_Event header; /* Information that is standard for * all events. */ SOCKET socket; /* Socket descriptor that is ready. Used * to find the SocketInfo structure for * the file (can't point directly to the * SocketInfo structure because it could * go away while the event is queued). */} SocketEvent;/* * This defines the minimum buffersize maintained by the kernel. */#define TCP_BUFFER_SIZE 4096/* * The following macros may be used to set the flags field of * a SocketInfo structure. */#define SOCKET_ASYNC (1<<0) /* The socket is in blocking * mode. */#define SOCKET_EOF (1<<1) /* A zero read happened on * the socket. */#define SOCKET_ASYNC_CONNECT (1<<2) /* This socket uses async * connect. */#define SOCKET_PENDING (1<<3) /* A message has been sent * for this socket */typedef struct ThreadSpecificData { HWND hwnd; /* Handle to window for socket messages. */ HANDLE socketThread; /* Thread handling the window */ Tcl_ThreadId threadId; /* Parent thread. */ HANDLE readyEvent; /* Event indicating that a socket event is * ready. Also used to indicate that the * socketThread has been initialized and has * started. */ HANDLE socketListLock; /* Win32 Event to lock the socketList */ SocketInfo *socketList; /* Every open socket in this thread has an * entry on this list. */} ThreadSpecificData;static Tcl_ThreadDataKey dataKey;static WNDCLASS windowClass;/* * Static functions defined in this file. */static SocketInfo * CreateSocket _ANSI_ARGS_((Tcl_Interp *interp, int port, CONST char *host, int server, CONST char *myaddr, int myport, int async));static int CreateSocketAddress _ANSI_ARGS_( (LPSOCKADDR_IN sockaddrPtr, CONST char *host, int port));static void InitSockets _ANSI_ARGS_((void));static SocketInfo * NewSocketInfo _ANSI_ARGS_((SOCKET socket));static Tcl_EventCheckProc SocketCheckProc;static Tcl_EventProc SocketEventProc;static void SocketExitHandler _ANSI_ARGS_(( ClientData clientData));static LRESULT CALLBACK SocketProc _ANSI_ARGS_((HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam));static Tcl_EventSetupProc SocketSetupProc;static Tcl_ExitProc SocketThreadExitHandler;static int SocketsEnabled _ANSI_ARGS_((void));static void TcpAccept _ANSI_ARGS_((SocketInfo *infoPtr));static Tcl_DriverBlockModeProc TcpBlockProc;static Tcl_DriverCloseProc TcpCloseProc;static Tcl_DriverSetOptionProc TcpSetOptionProc;static Tcl_DriverGetOptionProc TcpGetOptionProc;static Tcl_DriverInputProc TcpInputProc;static Tcl_DriverOutputProc TcpOutputProc;static Tcl_DriverWatchProc TcpWatchProc;static Tcl_DriverGetHandleProc TcpGetHandleProc;static int WaitForSocketEvent _ANSI_ARGS_(( SocketInfo *infoPtr, int events, int *errorCodePtr));static DWORD WINAPI SocketThread _ANSI_ARGS_((LPVOID arg));/* * This structure describes the channel type structure for TCP socket * based IO. */static Tcl_ChannelType tcpChannelType = { "tcp", /* Type name. */ TCL_CHANNEL_VERSION_2, /* v2 channel */ TcpCloseProc, /* Close proc. */ TcpInputProc, /* Input proc. */ TcpOutputProc, /* Output proc. */ NULL, /* Seek proc. */ TcpSetOptionProc, /* Set option proc. */ TcpGetOptionProc, /* Get option proc. */ TcpWatchProc, /* Set up notifier to watch this channel. */ TcpGetHandleProc, /* Get an OS handle from channel. */ NULL, /* close2proc. */ TcpBlockProc, /* Set socket into (non-)blocking mode. */ NULL, /* flush proc. */ NULL, /* handler proc. */};/* *---------------------------------------------------------------------- * * InitSockets -- * * Initialize the socket module. Attempts to load the wsock32.dll * library and set up the winSock function table. If successful, * registers the event window for the socket notifier code. * * Assumes Mutex is held. * * Results: * None. * * Side effects: * Dynamically loads wsock32.dll, and registers a new window * class and creates a window for use in asynchronous socket * notification. * *---------------------------------------------------------------------- */static voidInitSockets(){ DWORD id; WSADATA wsaData; DWORD err; ThreadSpecificData *tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); if (!initialized) { initialized = 1; Tcl_CreateExitHandler(SocketExitHandler, (ClientData) NULL); winSock.hModule = LoadLibraryA("wsock32.dll"); if (winSock.hModule == NULL) { return; } /* * Initialize the function table. */ winSock.accept = (LPFN_ACCEPT) GetProcAddress(winSock.hModule, "accept"); winSock.bind = (LPFN_BIND) GetProcAddress(winSock.hModule, "bind"); winSock.closesocket = (LPFN_CLOSESOCKET) GetProcAddress(winSock.hModule, "closesocket"); winSock.connect = (LPFN_CONNECT) GetProcAddress(winSock.hModule, "connect"); winSock.gethostbyaddr = (LPFN_GETHOSTBYADDR) GetProcAddress(winSock.hModule, "gethostbyaddr"); winSock.gethostbyname = (LPFN_GETHOSTBYNAME) GetProcAddress(winSock.hModule, "gethostbyname"); winSock.gethostname = (LPFN_GETHOSTNAME) GetProcAddress(winSock.hModule, "gethostname"); winSock.getpeername = (LPFN_GETPEERNAME) GetProcAddress(winSock.hModule, "getpeername"); winSock.getservbyname = (LPFN_GETSERVBYNAME) GetProcAddress(winSock.hModule, "getservbyname"); winSock.getsockname = (LPFN_GETSOCKNAME) GetProcAddress(winSock.hModule, "getsockname"); winSock.getsockopt = (LPFN_GETSOCKOPT) GetProcAddress(winSock.hModule, "getsockopt"); winSock.htons = (LPFN_HTONS) GetProcAddress(winSock.hModule, "htons"); winSock.inet_addr = (LPFN_INET_ADDR) GetProcAddress(winSock.hModule, "inet_addr"); winSock.inet_ntoa = (LPFN_INET_NTOA) GetProcAddress(winSock.hModule, "inet_ntoa"); winSock.ioctlsocket = (LPFN_IOCTLSOCKET) GetProcAddress(winSock.hModule, "ioctlsocket"); winSock.listen = (LPFN_LISTEN) GetProcAddress(winSock.hModule, "listen"); winSock.ntohs = (LPFN_NTOHS) GetProcAddress(winSock.hModule, "ntohs"); winSock.recv = (LPFN_RECV) GetProcAddress(winSock.hModule, "recv"); winSock.select = (LPFN_SELECT) GetProcAddress(winSock.hModule, "select"); winSock.send = (LPFN_SEND) GetProcAddress(winSock.hModule, "send"); winSock.setsockopt = (LPFN_SETSOCKOPT) GetProcAddress(winSock.hModule, "setsockopt"); winSock.socket = (LPFN_SOCKET) GetProcAddress(winSock.hModule, "socket"); winSock.WSAAsyncSelect = (LPFN_WSAASYNCSELECT) GetProcAddress(winSock.hModule, "WSAAsyncSelect"); winSock.WSACleanup = (LPFN_WSACLEANUP) GetProcAddress(winSock.hModule, "WSACleanup"); winSock.WSAGetLastError = (LPFN_WSAGETLASTERROR) GetProcAddress(winSock.hModule, "WSAGetLastError"); winSock.WSAStartup = (LPFN_WSASTARTUP) GetProcAddress(winSock.hModule, "WSAStartup"); /* * Now check that all fields are properly initialized. If not, * return zero to indicate that we failed to initialize * properly. */ if ((winSock.accept == NULL) || (winSock.bind == NULL) || (winSock.closesocket == NULL) || (winSock.connect == NULL) || (winSock.gethostbyname == NULL) || (winSock.gethostbyaddr == NULL) || (winSock.gethostname == NULL) || (winSock.getpeername == NULL) || (winSock.getservbyname == NULL) || (winSock.getsockname == NULL) || (winSock.getsockopt == NULL) || (winSock.htons == NULL) || (winSock.inet_addr == NULL) || (winSock.inet_ntoa == NULL) || (winSock.ioctlsocket == NULL) || (winSock.listen == NULL) || (winSock.ntohs == NULL) || (winSock.recv == NULL) || (winSock.select == NULL) || (winSock.send == NULL) || (winSock.setsockopt == NULL) || (winSock.socket == NULL) || (winSock.WSAAsyncSelect == NULL) || (winSock.WSACleanup == NULL) || (winSock.WSAGetLastError == NULL) || (winSock.WSAStartup == NULL)) { goto unloadLibrary; } /* * Create the async notification window with a new class. We * must create a new class to avoid a Windows 95 bug that causes * us to get the wrong message number for socket events if the * message window is a subclass of a static control. */ windowClass.style = 0; windowClass.cbClsExtra = 0; windowClass.cbWndExtra = 0; windowClass.hInstance = TclWinGetTclInstance(); windowClass.hbrBackground = NULL; windowClass.lpszMenuName = NULL; windowClass.lpszClassName = "TclSocket"; windowClass.lpfnWndProc = SocketProc; windowClass.hIcon = NULL; windowClass.hCursor = NULL; if (!RegisterClassA(&windowClass)) { TclWinConvertError(GetLastError()); goto unloadLibrary; } /* * Initialize the winsock library and check the interface * version actually loaded. We only ask for the 1.1 interface * and do require that it not be less than 1.1. */#define WSA_VERSION_MAJOR 1#define WSA_VERSION_MINOR 1#define WSA_VERSION_REQD MAKEWORD(WSA_VERSION_MAJOR, WSA_VERSION_MINOR) if ((err = winSock.WSAStartup(WSA_VERSION_REQD, &wsaData)) != 0) { TclWinConvertWSAError(err); goto unloadLibrary; } /* * Note the byte positions are swapped for the comparison, so
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -