📄 tclunixchan.c
字号:
* *---------------------------------------------------------------------- */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);#endif#ifdef USE_FIONBIO flags = 0; (void) ioctl(statePtr->fd, FIONBIO, &flags);#endif } 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, 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. */ 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, 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. */ 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; int size = sizeof(struct sockaddr_in); size_t len = 0; char buf[128]; if (optionName != (char *) NULL) { len = strlen(optionName); } 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((char *) &(peername.sin_addr), sizeof(peername.sin_addr), AF_INET); if (hostEntPtr != (struct hostent *) NULL) { Tcl_DStringAppendElement(dsPtr, hostEntPtr->h_name); } else { Tcl_DStringAppendElement(dsPtr, inet_ntoa(peername.sin_addr)); } sprintf(buf, "%d", 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((char *) &(sockname.sin_addr), sizeof(sockname.sin_addr), AF_INET); if (hostEntPtr != (struct hostent *) NULL) { Tcl_DStringAppendElement(dsPtr, hostEntPtr->h_name); } else { Tcl_DStringAppendElement(dsPtr, inet_ntoa(sockname.sin_addr)); } sprintf(buf, "%d", 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_GetChannelFile 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. * *---------------------------------------------------------------------- */ /* ARGSUSED */static intTcpGetHandleProc(instanceData, direction, handlePtr) ClientData instanceData; /* The socket state. */ int direction; /* Not used. */ ClientData *handlePtr; /* Where to store the handle. */{ TcpState *statePtr = (TcpState *) instanceData; *handlePtr = (ClientData)statePtr->fd; return TCL_OK;}/* *---------------------------------------------------------------------- * * CreateSocket -- * * This function opens a new socket in client or server mode * and initializes the TcpState structure. * * Results: * Returns a new TcpState, or NULL with an error in interp->result, * if interp is not NULL. * * Side effects: * Opens a socket. * *---------------------------------------------------------------------- */static TcpState *CreateSocket(interp, port, host, server, myaddr, myport, async) Tcl_Interp *interp; /* For error reporting; can be NULL. */ int port; /* Port number to open. */ char *host; /* Name of host on which to open port. * NULL implies INADDR_ANY */ int server; /* 1 if socket should be a server socket, * else 0 for a client socket. */ char *myaddr; /* Optional client-side address */ int myport; /* Optional client-side port */ int async; /* If nonzero and creating a client socket, * attempt to do an async connect. Otherwise * do a synchronous connect or bind. */{ int status, sock, asyncConnect, curState, origState; struct sockaddr_in sockaddr; /* socket address */ struct sockaddr_in mysockaddr; /* Socket address for client */ TcpState *statePtr; sock = -1; origState = 0; if (! CreateSocketAddress(&sockaddr, host, port)) { goto addressError; } if ((myaddr != NULL || myport != 0) && ! CreateSocketAddress(&mysockaddr, myaddr, myport)) { goto addressError; } sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { goto addressError; } /* * Set the close-on-exec flag so that the socket will not get * inherited by child processes. */ fcntl(sock, F_SETFD, FD_CLOEXEC); /* * Set kernel space buffering */ TclSockMinimumBuffers(sock, SOCKET_BUFSIZE); asyncConnect = 0; status = 0; if (server) { /* * Set up to reuse server addresses automatically and bind to the * specified port. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -