📄 tclunixchan.c
字号:
fsPtr->validMask = mode | TCL_EXCEPTION; fsPtr->channel = Tcl_CreateChannel(channelTypePtr, channelName, (ClientData) fsPtr, mode); return fsPtr->channel;}/* *---------------------------------------------------------------------- * * TcpBlockModeProc -- * * This procedure is invoked by the generic IO level to set blocking * and nonblocking mode on a TCP socket based channel. * * Results: * 0 if successful, errno when failed. * * Side effects: * Sets the device into blocking or nonblocking mode. * *---------------------------------------------------------------------- */ /* ARGSUSED */static intTcpBlockModeProc(instanceData, mode) ClientData instanceData; /* Socket state. */ int mode; /* The mode to set. Can be one of * TCL_MODE_BLOCKING or * TCL_MODE_NONBLOCKING. */{ TcpState *statePtr = (TcpState *) instanceData; int setting;#ifndef USE_FIONBIO setting = fcntl(statePtr->fd, F_GETFL); if (mode == TCL_MODE_BLOCKING) { statePtr->flags &= (~(TCP_ASYNC_SOCKET)); setting &= (~(O_NONBLOCK)); } else { statePtr->flags |= TCP_ASYNC_SOCKET; setting |= O_NONBLOCK; } if (fcntl(statePtr->fd, F_SETFL, setting) < 0) { return errno; }#else /* USE_FIONBIO */ if (mode == TCL_MODE_BLOCKING) { statePtr->flags &= (~(TCP_ASYNC_SOCKET)); setting = 0; if (ioctl(statePtr->fd, (int) FIONBIO, &setting) == -1) { return errno; } } else { statePtr->flags |= TCP_ASYNC_SOCKET; setting = 1; if (ioctl(statePtr->fd, (int) FIONBIO, &setting) == -1) { return errno; } }#endif /* !USE_FIONBIO */ return 0;}/* *---------------------------------------------------------------------- * * WaitForConnect -- * * Waits for a connection on an asynchronously opened socket to * be completed. * * Results: * None. * * Side effects: * The socket is connected after this function returns. * *---------------------------------------------------------------------- */static intWaitForConnect(statePtr, errorCodePtr) TcpState *statePtr; /* State of the socket. */ int *errorCodePtr; /* Where to store errors? */{ int timeOut; /* How long to wait. */ int state; /* Of calling TclWaitForFile. */ int flags; /* fcntl flags for the socket. */ /* * If an asynchronous connect is in progress, attempt to wait for it * to complete before reading. */ if (statePtr->flags & TCP_ASYNC_CONNECT) { if (statePtr->flags & TCP_ASYNC_SOCKET) { timeOut = 0; } else { timeOut = -1; } errno = 0; state = TclUnixWaitForFile(statePtr->fd, TCL_WRITABLE | TCL_EXCEPTION, timeOut); if (!(statePtr->flags & TCP_ASYNC_SOCKET)) {#ifndef USE_FIONBIO flags = fcntl(statePtr->fd, F_GETFL); flags &= (~(O_NONBLOCK)); (void) fcntl(statePtr->fd, F_SETFL, flags);#else /* USE_FIONBIO */ flags = 0; (void) ioctl(statePtr->fd, FIONBIO, &flags);#endif /* !USE_FIONBIO */ } if (state & TCL_EXCEPTION) { return -1; } if (state & TCL_WRITABLE) { statePtr->flags &= (~(TCP_ASYNC_CONNECT)); } else if (timeOut == 0) { *errorCodePtr = errno = EWOULDBLOCK; return -1; } } return 0;}/* *---------------------------------------------------------------------- * * TcpInputProc -- * * This procedure is invoked by the generic IO level to read input * from a TCP socket based channel. * * NOTE: We cannot share code with FilePipeInputProc because here * we must use recv to obtain the input from the channel, not read. * * Results: * The number of bytes read is returned or -1 on error. An output * argument contains the POSIX error code on error, or zero if no * error occurred. * * Side effects: * Reads input from the input device of the channel. * *---------------------------------------------------------------------- */ /* ARGSUSED */static intTcpInputProc(instanceData, buf, bufSize, errorCodePtr) ClientData instanceData; /* Socket state. */ char *buf; /* Where to store data read. */ int bufSize; /* How much space is available * in the buffer? */ int *errorCodePtr; /* Where to store error code. */{ TcpState *statePtr = (TcpState *) instanceData; int bytesRead, state; *errorCodePtr = 0; state = WaitForConnect(statePtr, errorCodePtr); if (state != 0) { return -1; } bytesRead = recv(statePtr->fd, buf, (size_t) bufSize, 0); if (bytesRead > -1) { return bytesRead; } if (errno == ECONNRESET) { /* * Turn ECONNRESET into a soft EOF condition. */ return 0; } *errorCodePtr = errno; return -1;}/* *---------------------------------------------------------------------- * * TcpOutputProc -- * * This procedure is invoked by the generic IO level to write output * to a TCP socket based channel. * * NOTE: We cannot share code with FilePipeOutputProc because here * we must use send, not write, to get reliable error reporting. * * Results: * The number of bytes written is returned. An output argument is * set to a POSIX error code if an error occurred, or zero. * * Side effects: * Writes output on the output device of the channel. * *---------------------------------------------------------------------- */static intTcpOutputProc(instanceData, buf, toWrite, errorCodePtr) ClientData instanceData; /* Socket state. */ CONST char *buf; /* The data buffer. */ int toWrite; /* How many bytes to write? */ int *errorCodePtr; /* Where to store error code. */{ TcpState *statePtr = (TcpState *) instanceData; int written; int state; /* Of waiting for connection. */ *errorCodePtr = 0; state = WaitForConnect(statePtr, errorCodePtr); if (state != 0) { return -1; } written = send(statePtr->fd, buf, (size_t) toWrite, 0); if (written > -1) { return written; } *errorCodePtr = errno; return -1;}/* *---------------------------------------------------------------------- * * TcpCloseProc -- * * This procedure is invoked by the generic IO level to perform * channel-type-specific cleanup when a TCP socket based channel * is closed. * * Results: * 0 if successful, the value of errno if failed. * * Side effects: * Closes the socket of the channel. * *---------------------------------------------------------------------- */ /* ARGSUSED */static intTcpCloseProc(instanceData, interp) ClientData instanceData; /* The socket to close. */ Tcl_Interp *interp; /* For error reporting - unused. */{ TcpState *statePtr = (TcpState *) instanceData; int errorCode = 0; /* * Delete a file handler that may be active for this socket if this * is a server socket - the file handler was created automatically * by Tcl as part of the mechanism to accept new client connections. * Channel handlers are already deleted in the generic IO channel * closing code that called this function, so we do not have to * delete them here. */ Tcl_DeleteFileHandler(statePtr->fd); if (close(statePtr->fd) < 0) { errorCode = errno; } ckfree((char *) statePtr); return errorCode;}/* *---------------------------------------------------------------------- * * TcpGetOptionProc -- * * Computes an option value for a TCP socket based channel, or a * list of all options and their values. * * Note: This code is based on code contributed by John Haxby. * * Results: * A standard Tcl result. The value of the specified option or a * list of all options and their values is returned in the * supplied DString. Sets Error message if needed. * * Side effects: * None. * *---------------------------------------------------------------------- */static intTcpGetOptionProc(instanceData, interp, optionName, dsPtr) ClientData instanceData; /* Socket state. */ Tcl_Interp *interp; /* For error reporting - can be NULL. */ CONST char *optionName; /* Name of the option to * retrieve the value for, or * NULL to get all options and * their values. */ Tcl_DString *dsPtr; /* Where to store the computed * value; initialized by caller. */{ TcpState *statePtr = (TcpState *) instanceData; struct sockaddr_in sockname; struct sockaddr_in peername; struct hostent *hostEntPtr; socklen_t size = sizeof(struct sockaddr_in); size_t len = 0; char buf[TCL_INTEGER_SPACE]; if (optionName != (char *) NULL) { len = strlen(optionName); } if ((len > 1) && (optionName[1] == 'e') && (strncmp(optionName, "-error", len) == 0)) { socklen_t optlen = sizeof(int); int err, ret; ret = getsockopt(statePtr->fd, SOL_SOCKET, SO_ERROR, (char *)&err, &optlen); if (ret < 0) { err = errno; } if (err != 0) { Tcl_DStringAppend(dsPtr, Tcl_ErrnoMsg(err), -1); } return TCL_OK; } if ((len == 0) || ((len > 1) && (optionName[1] == 'p') && (strncmp(optionName, "-peername", len) == 0))) { if (getpeername(statePtr->fd, (struct sockaddr *) &peername, &size) >= 0) { if (len == 0) { Tcl_DStringAppendElement(dsPtr, "-peername"); Tcl_DStringStartSublist(dsPtr); } Tcl_DStringAppendElement(dsPtr, inet_ntoa(peername.sin_addr)); hostEntPtr = gethostbyaddr( /* INTL: Native. */ (char *) &peername.sin_addr, sizeof(peername.sin_addr), AF_INET); if (hostEntPtr != NULL) { Tcl_DString ds; Tcl_ExternalToUtfDString(NULL, hostEntPtr->h_name, -1, &ds); Tcl_DStringAppendElement(dsPtr, Tcl_DStringValue(&ds)); } else { Tcl_DStringAppendElement(dsPtr, inet_ntoa(peername.sin_addr)); } TclFormatInt(buf, ntohs(peername.sin_port)); Tcl_DStringAppendElement(dsPtr, buf); if (len == 0) { Tcl_DStringEndSublist(dsPtr); } else { return TCL_OK; } } else { /* * getpeername failed - but if we were asked for all the options * (len==0), don't flag an error at that point because it could * be an fconfigure request on a server socket. (which have * no peer). same must be done on win&mac. */ if (len) { if (interp) { Tcl_AppendResult(interp, "can't get peername: ", Tcl_PosixError(interp), (char *) NULL); } return TCL_ERROR; } } } if ((len == 0) || ((len > 1) && (optionName[1] == 's') && (strncmp(optionName, "-sockname", len) == 0))) { if (getsockname(statePtr->fd, (struct sockaddr *) &sockname, &size) >= 0) { if (len == 0) { Tcl_DStringAppendElement(dsPtr, "-sockname"); Tcl_DStringStartSublist(dsPtr); } Tcl_DStringAppendElement(dsPtr, inet_ntoa(sockname.sin_addr)); hostEntPtr = gethostbyaddr( /* INTL: Native. */ (char *) &sockname.sin_addr, sizeof(sockname.sin_addr), AF_INET); if (hostEntPtr != (struct hostent *) NULL) { Tcl_DString ds; Tcl_ExternalToUtfDString(NULL, hostEntPtr->h_name, -1, &ds); Tcl_DStringAppendElement(dsPtr, Tcl_DStringValue(&ds)); } else { Tcl_DStringAppendElement(dsPtr, inet_ntoa(sockname.sin_addr)); } TclFormatInt(buf, ntohs(sockname.sin_port)); Tcl_DStringAppendElement(dsPtr, buf); if (len == 0) { Tcl_DStringEndSublist(dsPtr); } else { return TCL_OK; } } else { if (interp) { Tcl_AppendResult(interp, "can't get sockname: ", Tcl_PosixError(interp), (char *) NULL); } return TCL_ERROR; } } if (len > 0) { return Tcl_BadChannelOption(interp, optionName, "peername sockname"); } return TCL_OK;}/* *---------------------------------------------------------------------- * * TcpWatchProc -- * * Initialize the notifier to watch the fd from this channel. * * Results: * None. * * Side effects: * Sets up the notifier so that a future event on the channel will * be seen by Tcl. * *---------------------------------------------------------------------- */static voidTcpWatchProc(instanceData, mask) ClientData instanceData; /* The socket state. */ int mask; /* Events of interest; an OR-ed * combination of TCL_READABLE, * TCL_WRITABLE and TCL_EXCEPTION. */{ TcpState *statePtr = (TcpState *) instanceData; /* * Make sure we don't mess with server sockets since they will never * be readable or writable at the Tcl level. This keeps Tcl scripts * from interfering with the -accept behavior. */ if (!statePtr->acceptProc) { if (mask) { Tcl_CreateFileHandler(statePtr->fd, mask, (Tcl_FileProc *) Tcl_NotifyChannel, (ClientData) statePtr->channel); } else { Tcl_DeleteFileHandler(statePtr->fd); } }}/* *---------------------------------------------------------------------- * * TcpGetHandleProc -- * * Called from Tcl_GetChannelHandle to retrieve OS handles from inside * a TCP socket based channel. * * Results: * Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if * there is no handle for the specified direction. * * Side effects: * None. * *----------------------------------------------------------------------
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -