📄 tclwinsock.c
字号:
} /* * Create a new client socket and wrap it in a channel. */ infoPtr = CreateSocket(interp, port, host, 0, myaddr, myport, async); if (infoPtr == NULL) { return NULL; } wsprintfA(channelName, "sock%d", infoPtr->socket); infoPtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName, (ClientData) infoPtr, (TCL_READABLE | TCL_WRITABLE)); if (Tcl_SetChannelOption(interp, infoPtr->channel, "-translation", "auto crlf") == TCL_ERROR) { Tcl_Close((Tcl_Interp *) NULL, infoPtr->channel); return (Tcl_Channel) NULL; } if (Tcl_SetChannelOption(NULL, infoPtr->channel, "-eofchar", "") == TCL_ERROR) { Tcl_Close((Tcl_Interp *) NULL, infoPtr->channel); return (Tcl_Channel) NULL; } return infoPtr->channel;}/* *---------------------------------------------------------------------- * * Tcl_MakeTcpClientChannel -- * * Creates a Tcl_Channel from an existing client TCP socket. * * Results: * The Tcl_Channel wrapped around the preexisting TCP socket. * * Side effects: * None. * * NOTE: Code contributed by Mark Diekhans (markd@grizzly.com) * *---------------------------------------------------------------------- */Tcl_ChannelTcl_MakeTcpClientChannel(sock) ClientData sock; /* The socket to wrap up into a channel. */{ SocketInfo *infoPtr; char channelName[16 + TCL_INTEGER_SPACE]; ThreadSpecificData *tsdPtr; if (TclpHasSockets(NULL) != TCL_OK) { return NULL; } tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); /* * Set kernel space buffering and non-blocking. */ TclSockMinimumBuffers((SOCKET) sock, TCP_BUFFER_SIZE); infoPtr = NewSocketInfo((SOCKET) sock); /* * Start watching for read/write events on the socket. */ infoPtr->selectEvents = FD_READ | FD_CLOSE | FD_WRITE; SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) SELECT, (LPARAM) infoPtr); wsprintfA(channelName, "sock%d", infoPtr->socket); infoPtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName, (ClientData) infoPtr, (TCL_READABLE | TCL_WRITABLE)); Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto crlf"); return infoPtr->channel;}/* *---------------------------------------------------------------------- * * Tcl_OpenTcpServer -- * * Opens a TCP server socket and creates a channel around it. * * Results: * The channel or NULL if failed. An error message is returned * in the interpreter on failure. * * Side effects: * Opens a server socket and creates a new channel. * *---------------------------------------------------------------------- */Tcl_ChannelTcl_OpenTcpServer(interp, port, host, acceptProc, acceptProcData) Tcl_Interp *interp; /* For error reporting - may be * NULL. */ int port; /* Port number to open. */ CONST char *host; /* Name of local host. */ Tcl_TcpAcceptProc *acceptProc; /* Callback for accepting connections * from new clients. */ ClientData acceptProcData; /* Data for the callback. */{ SocketInfo *infoPtr; char channelName[16 + TCL_INTEGER_SPACE]; if (TclpHasSockets(interp) != TCL_OK) { return NULL; } /* * Create a new client socket and wrap it in a channel. */ infoPtr = CreateSocket(interp, port, host, 1, NULL, 0, 0); if (infoPtr == NULL) { return NULL; } infoPtr->acceptProc = acceptProc; infoPtr->acceptProcData = acceptProcData; wsprintfA(channelName, "sock%d", infoPtr->socket); infoPtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName, (ClientData) infoPtr, 0); if (Tcl_SetChannelOption(interp, infoPtr->channel, "-eofchar", "") == TCL_ERROR) { Tcl_Close((Tcl_Interp *) NULL, infoPtr->channel); return (Tcl_Channel) NULL; } return infoPtr->channel;}/* *---------------------------------------------------------------------- * * TcpAccept -- * Accept a TCP socket connection. This is called by * SocketEventProc and it in turns calls the registered accept * procedure. * * Results: * None. * * Side effects: * Invokes the accept proc which may invoke arbitrary Tcl code. * *---------------------------------------------------------------------- */static voidTcpAccept(infoPtr) SocketInfo *infoPtr; /* Socket to accept. */{ SOCKET newSocket; SocketInfo *newInfoPtr; struct sockaddr_in addr; int len; char channelName[16 + TCL_INTEGER_SPACE]; ThreadSpecificData *tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); /* * Accept the incoming connection request. */ len = sizeof(struct sockaddr_in); newSocket = (*winSock.accept)(infoPtr->socket, (struct sockaddr *)&addr, &len); /* * Clear the ready mask so we can detect the next connection request. * Note that connection requests are level triggered, so if there is * a request already pending, a new event will be generated. */ if (newSocket == INVALID_SOCKET) { infoPtr->acceptEventCount = 0; infoPtr->readyEvents &= ~(FD_ACCEPT); return; } /* * It is possible that more than one FD_ACCEPT has been sent, so an extra * count must be kept. Decrement the count, and reset the readyEvent bit * if the count is no longer > 0. */ infoPtr->acceptEventCount--; if (infoPtr->acceptEventCount <= 0) { infoPtr->readyEvents &= ~(FD_ACCEPT); } /* * Win-NT has a misfeature that sockets are inherited in child * processes by default. Turn off the inherit bit. */ SetHandleInformation( (HANDLE) newSocket, HANDLE_FLAG_INHERIT, 0 ); /* * Add this socket to the global list of sockets. */ newInfoPtr = NewSocketInfo(newSocket); /* * Select on read/write events and create the channel. */ newInfoPtr->selectEvents = (FD_READ | FD_WRITE | FD_CLOSE); SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) SELECT, (LPARAM) newInfoPtr); wsprintfA(channelName, "sock%d", newInfoPtr->socket); newInfoPtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName, (ClientData) newInfoPtr, (TCL_READABLE | TCL_WRITABLE)); if (Tcl_SetChannelOption(NULL, newInfoPtr->channel, "-translation", "auto crlf") == TCL_ERROR) { Tcl_Close((Tcl_Interp *) NULL, newInfoPtr->channel); return; } if (Tcl_SetChannelOption(NULL, newInfoPtr->channel, "-eofchar", "") == TCL_ERROR) { Tcl_Close((Tcl_Interp *) NULL, newInfoPtr->channel); return; } /* * Invoke the accept callback procedure. */ if (infoPtr->acceptProc != NULL) { (infoPtr->acceptProc) (infoPtr->acceptProcData, newInfoPtr->channel, (*winSock.inet_ntoa)(addr.sin_addr), (*winSock.ntohs)(addr.sin_port)); }}/* *---------------------------------------------------------------------- * * TcpInputProc -- * * This procedure is called by the generic IO level to read data from * a socket based channel. * * Results: * The number of bytes read or -1 on error. * * Side effects: * Consumes input from the socket. * *---------------------------------------------------------------------- */static intTcpInputProc(instanceData, buf, toRead, errorCodePtr) ClientData instanceData; /* The socket state. */ char *buf; /* Where to store data. */ int toRead; /* Maximum number of bytes to read. */ int *errorCodePtr; /* Where to store error codes. */{ SocketInfo *infoPtr = (SocketInfo *) instanceData; int bytesRead; int error; ThreadSpecificData *tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); *errorCodePtr = 0; /* * Check that WinSock is initialized; do not call it if not, to * prevent system crashes. This can happen at exit time if the exit * handler for WinSock ran before other exit handlers that want to * use sockets. */ if (!SocketsEnabled()) { *errorCodePtr = EFAULT; return -1; } /* * First check to see if EOF was already detected, to prevent * calling the socket stack after the first time EOF is detected. */ if (infoPtr->flags & SOCKET_EOF) { return 0; } /* * Check to see if the socket is connected before trying to read. */ if ((infoPtr->flags & SOCKET_ASYNC_CONNECT) && ! WaitForSocketEvent(infoPtr, FD_CONNECT, errorCodePtr)) { return -1; } /* * No EOF, and it is connected, so try to read more from the socket. * Note that we clear the FD_READ bit because read events are level * triggered so a new event will be generated if there is still data * available to be read. We have to simulate blocking behavior here * since we are always using non-blocking sockets. */ while (1) { SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) UNSELECT, (LPARAM) infoPtr); bytesRead = (*winSock.recv)(infoPtr->socket, buf, toRead, 0); infoPtr->readyEvents &= ~(FD_READ); /* * Check for end-of-file condition or successful read. */ if (bytesRead == 0) { infoPtr->flags |= SOCKET_EOF; } if (bytesRead != SOCKET_ERROR) { break; } /* * If an error occurs after the FD_CLOSE has arrived, * then ignore the error and report an EOF. */ if (infoPtr->readyEvents & FD_CLOSE) { infoPtr->flags |= SOCKET_EOF; bytesRead = 0; break; } /* * Check for error condition or underflow in non-blocking case. */ error = (*winSock.WSAGetLastError)(); if ((infoPtr->flags & SOCKET_ASYNC) || (error != WSAEWOULDBLOCK)) { TclWinConvertWSAError(error); *errorCodePtr = Tcl_GetErrno(); bytesRead = -1; break; } /* * In the blocking case, wait until the file becomes readable * or closed and try again. */ if (!WaitForSocketEvent(infoPtr, FD_READ|FD_CLOSE, errorCodePtr)) { bytesRead = -1; break; } } SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) SELECT, (LPARAM) infoPtr); return bytesRead;}/* *---------------------------------------------------------------------- * * TcpOutputProc -- * * This procedure is called by the generic IO level to write data * to a socket based channel. * * Results: * The number of bytes written or -1 on failure. * * Side effects: * Produces output on the socket. * *---------------------------------------------------------------------- */static intTcpOutputProc(instanceData, buf, toWrite, errorCodePtr) ClientData instanceData; /* The socket state. */ CONST char *buf; /* Where to get data. */ int toWrite; /* Maximum number of bytes to write. */ int *errorCodePtr; /* Where to store error codes. */{ SocketInfo *infoPtr = (SocketInfo *) instanceData; int bytesWritten; int error; ThreadSpecificData *tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); *errorCodePtr = 0; /* * Check that WinSock is initialized; do not call it if not, to * prevent system crashes. This can happen at exit time if the exit * handler for WinSock ran before other exit handlers that want to * use sockets. */ if (!SocketsEnabled()) { *errorCodePtr = EFAULT; return -1; } /* * Check to see if the socket is connected before trying to write. */ if ((infoPtr->flags & SOCKET_ASYNC_CONNECT) && ! WaitForSocketEvent(infoPtr, FD_CONNECT, errorCodePtr)) { return -1; } while (1) { SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) UNSELECT, (LPARAM) infoPtr); bytesWritten = (*winSock.send)(infoPtr->socket, buf, toWrite, 0); if (bytesWritten != SOCKET_ERROR) { /* * Since Windows won't generate a new write event until we hit * an overflow condition, we need to force the event loop to * poll until the condition changes. */ if (infoPtr->watchEvents & FD_WRITE) { Tcl_Time blockTime = { 0, 0 }; Tcl_SetMaxBlockTime(&blockTime); } break; } /* * Check for error condition or overflow. In the event of overflow, we * need to clear the FD_WRITE flag so we can detect the next writable * event. Note that Windows only sends a new writable event after a * send fails with WSAEWOULDBLOCK.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -