📄 tclmacsock.c
字号:
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; StreamPtr tcpStream; OSErr err; TCPiopb statusPB; int toRead, dataAvail; *errorCodePtr = 0; errno = 0; tcpStream = statePtr->tcpStream; if (bufSize == 0) { return 0; } toRead = bufSize; /* * First check to see if EOF was already detected, to prevent * calling the socket stack after the first time EOF is detected. */ if (statePtr->flags & TCP_REMOTE_CLOSED) { return 0; } /* * If an asynchronous connect is in progress, attempt to wait for it * to complete before reading. */ if ((statePtr->flags & TCP_ASYNC_CONNECT) && ! WaitForSocketEvent(statePtr, TCL_READABLE, errorCodePtr)) { return -1; } /* * No EOF, and it is connected, so try to read more from the socket. * If the socket is blocking, we keep trying until there is data * available or the socket is closed. */ while (1) { statusPB.ioCRefNum = driverRefNum; statusPB.tcpStream = tcpStream; statusPB.csCode = TCPStatus; err = PBControlSync((ParmBlkPtr) &statusPB); if (err != noErr) { Debugger(); statePtr->flags |= TCP_REMOTE_CLOSED; return 0; /* EOF */ } dataAvail = statusPB.csParam.status.amtUnreadData; if (dataAvail < bufSize) { toRead = dataAvail; } else { toRead = bufSize; } if (toRead != 0) { /* * Try to read the data. */ InitMacTCPParamBlock(&statusPB, TCPRcv); statusPB.tcpStream = tcpStream; statusPB.csParam.receive.rcvBuff = buf; statusPB.csParam.receive.rcvBuffLen = toRead; err = PBControlSync((ParmBlkPtr) &statusPB); statePtr->checkMask &= ~TCL_READABLE; switch (err) { case noErr: /* * The channel remains readable only if this read succeds * and we had more data then the size of the buffer we were * trying to fill. Use the info from the call to status to * determine this. */ if (dataAvail > bufSize) { statePtr->checkMask |= TCL_READABLE; } return statusPB.csParam.receive.rcvBuffLen; case connectionClosing: *errorCodePtr = errno = ESHUTDOWN; statePtr->flags |= TCP_REMOTE_CLOSED; return 0; case connectionDoesntExist: case connectionTerminated: *errorCodePtr = errno = ENOTCONN; statePtr->flags |= TCP_REMOTE_CLOSED; return 0; case invalidStreamPtr: default: *errorCodePtr = EINVAL; return -1; } } /* * No data is available, so check the connection state to * see why this is the case. */ if (statusPB.csParam.status.connectionState == 14) { statePtr->flags |= TCP_REMOTE_CLOSED; return 0; } if (statusPB.csParam.status.connectionState != 8) { Debugger(); } statePtr->checkMask &= ~TCL_READABLE; if (statePtr->flags & TCP_ASYNC_SOCKET) { *errorCodePtr = EWOULDBLOCK; return -1; } /* * In the blocking case, wait until the file becomes readable * or closed and try again. */ if (!WaitForSocketEvent(statePtr, TCL_READABLE, errorCodePtr)) { return -1; } }}/* *---------------------------------------------------------------------- * * TcpGetHandle -- * * Called from Tcl_GetChannelFile to retrieve handles from inside * a file based channel. * * Results: * The appropriate handle or NULL if not present. * * Side effects: * None. * *---------------------------------------------------------------------- */static intTcpGetHandle( ClientData instanceData, /* The file state. */ int direction, /* Which handle to retrieve? */ ClientData *handlePtr){ TcpState *statePtr = (TcpState *) instanceData; *handlePtr = (ClientData) statePtr->tcpStream; return TCL_OK;}/* *---------------------------------------------------------------------- * * TcpOutput-- * * Writes the given output on the IO channel. Returns count of how * many characters were actually written, and an error indication. * * Results: * A count of how many characters were written is returned and an * error indication is returned in an output argument. * * Side effects: * Writes output on the actual channel. * *---------------------------------------------------------------------- */static intTcpOutput( ClientData instanceData, /* Channel state. */ char *buf, /* The data buffer. */ int toWrite, /* How many bytes to write? */ int *errorCodePtr) /* Where to store error code. */{ TcpState *statePtr = (TcpState *) instanceData; StreamPtr tcpStream; OSErr err; int amount; TCPiopb statusPB; *errorCodePtr = 0; tcpStream = statePtr->tcpStream; /* * If an asynchronous connect is in progress, attempt to wait for it * to complete before writing. */ if ((statePtr->flags & TCP_ASYNC_CONNECT) && ! WaitForSocketEvent(statePtr, TCL_WRITABLE, errorCodePtr)) { return -1; } /* * Loop until we have written some data, or an error occurs. */ while (1) { statusPB.ioCRefNum = driverRefNum; statusPB.tcpStream = tcpStream; statusPB.csCode = TCPStatus; err = PBControlSync((ParmBlkPtr) &statusPB); if ((err == connectionDoesntExist) || ((err == noErr) && (statusPB.csParam.status.connectionState == 14))) { /* * The remote connection is gone away. Report an error * and don't write anything. */ *errorCodePtr = errno = EPIPE; return -1; } else if (err != noErr) { return -1; } amount = statusPB.csParam.status.sendWindow - statusPB.csParam.status.amtUnackedData; /* * Attempt to write the data to the socket if a background * write isn't in progress and there is room in the output buffers. */ if (!(statePtr->flags & TCP_WRITING) && amount > 0) { if (toWrite < amount) { amount = toWrite; } statePtr->dataSegment[0].length = amount; statePtr->dataSegment[0].ptr = buf; statePtr->dataSegment[1].length = 0; InitMacTCPParamBlock(&statePtr->pb, TCPSend); statePtr->pb.ioCompletion = completeUPP; statePtr->pb.tcpStream = tcpStream; statePtr->pb.csParam.send.wdsPtr = (Ptr) statePtr->dataSegment; statePtr->pb.csParam.send.pushFlag = 1; statePtr->pb.csParam.send.userDataPtr = (Ptr) statePtr; statePtr->flags |= TCP_WRITING; err = PBControlAsync((ParmBlkPtr) &(statePtr->pb)); switch (err) { case noErr: return amount; case connectionClosing: *errorCodePtr = errno = ESHUTDOWN; statePtr->flags |= TCP_REMOTE_CLOSED; return -1; case connectionDoesntExist: case connectionTerminated: *errorCodePtr = errno = ENOTCONN; statePtr->flags |= TCP_REMOTE_CLOSED; return -1; case invalidStreamPtr: default: return -1; } } /* * The socket wasn't writable. In the non-blocking case, return * immediately, otherwise wait until the file becomes writable * or closed and try again. */ if (statePtr->flags & TCP_ASYNC_SOCKET) { statePtr->checkMask &= ~TCL_WRITABLE; *errorCodePtr = EWOULDBLOCK; return -1; } else if (!WaitForSocketEvent(statePtr, TCL_WRITABLE, errorCodePtr)) { return -1; } }}/* *---------------------------------------------------------------------- * * 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. * * Side effects: * None. * *---------------------------------------------------------------------- */static intTcpGetOptionProc( 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; int doPeerName = false, doSockName = false, doAll = false; ip_addr tcpAddress; char buffer[128]; OSErr err; Tcl_DString dString; TCPiopb statusPB; int errorCode; /* * If an asynchronous connect is in progress, attempt to wait for it * to complete before accessing the socket state. */ if ((statePtr->flags & TCP_ASYNC_CONNECT) && ! WaitForSocketEvent(statePtr, TCL_WRITABLE, &errorCode)) { if (interp) { /* * fix the error message. */ Tcl_AppendResult(interp, "connect is in progress and can't wait", NULL); } return TCL_ERROR; } /* * Determine which options we need to do. Do all of them * if optionName is NULL. */ if (optionName == (char *) NULL || optionName[0] == '\0') { doAll = true; } else { if (!strcmp(optionName, "-peername")) { doPeerName = true; } else if (!strcmp(optionName, "-sockname")) { doSockName = true; } else { return Tcl_BadChannelOption(interp, optionName, "peername sockname"); } } /* * Get status on the stream. Make sure to use a new pb struct because * the struct in the statePtr may be part of an asyncronous call. */ statusPB.ioCRefNum = driverRefNum; statusPB.tcpStream = statePtr->tcpStream; statusPB.csCode = TCPStatus; err = PBControlSync((ParmBlkPtr) &statusPB); if ((err == connectionDoesntExist) || ((err == noErr) && (statusPB.csParam.status.connectionState == 14))) { /* * The socket was probably closed on the other side of the connection. */ if (interp) { Tcl_AppendResult(interp, "can't access socket info: ", "connection reset by peer", NULL); } return TCL_ERROR; } else if (err != noErr) { if (interp) { Tcl_AppendResult(interp, "unknown socket error", NULL); } Debugger(); return TCL_ERROR; } /* * Get the sockname for the socket. */ Tcl_DStringInit(&dString); if (doAll || doSockName) { if (doAll) { Tcl_DStringAppendElement(dsPtr, "-sockname"); Tcl_DStringStartSublist(dsPtr); } tcpAddress = statusPB.csParam.status.localHost; sprintf(buffer, "%d.%d.%d.%d", tcpAddress>>24, tcpAddress>>16 & 0xff, tcpAddress>>8 & 0xff, tcpAddress & 0xff); Tcl_DStringAppendElement(dsPtr, buffer); if (ResolveAddress(tcpAddress, &dString) == noErr) { Tcl_DStringAppendElement(dsPtr, dString.string); } else { Tcl_DStringAppendElement(dsPtr, "<unknown>"); } sprintf(buffer, "%d", statusPB.csParam.status.localPort); Tcl_DStringAppendElement(dsPtr, buffer); if (doAll) { Tcl_DStringEndSublist(dsPtr); } } /* * Get the peername for the socket. */ if ((doAll || doPeerName) && (statePtr->flags & TCP_CONNECTED)) { if (doAll) { Tcl_DStringAppendElement(dsPtr, "-peername"); Tcl_DStringStartSublist(dsPtr); } tcpAddress = statusPB.csParam.status.remoteHost; sprintf(buffer, "%d.%d.%d.%d", tcpAddress>>24, tcpAddress>>16 & 0xff, tcpAddress>>8 & 0xff, tcpAddress & 0xff); Tcl_DStringAppendElement(dsPtr, buffer); Tcl_DStringSetLength(&dString, 0); if (ResolveAddress(tcpAddress, &dString) == noErr) { Tcl_DStringAppendElement(dsPtr, dString.string); } else { Tcl_DStringAppendElement(dsPtr, "<unknown>"); } sprintf(buffer, "%d", statusPB.csParam.status.remotePort); Tcl_DStringAppendElement(dsPtr, buffer); if (doAll) { Tcl_DStringEndSublist(dsPtr); } } Tcl_DStringFree(&dString); return TCL_OK;}/* *---------------------------------------------------------------------- * * TcpWatch -- * * Initialize the notifier to watch this channel. * * Results: * None. * * Side effects: * Sets the watchMask for the channel. * *---------------------------------------------------------------------- */static voidTcpWatch(instanceData, mask) ClientData instanceData; /* The file state. */ int mask; /* Events of interest; an OR-ed * combination of TCL_READABLE, * TCL_WRITABLE and TCL_EXCEPTION. */{ TcpState *statePtr = (TcpState *) instanceData; statePtr->watchMask = mask;}/* *---------------------------------------------------------------------- * * NewSocketInfo -- * * This function allocates and initializes a new SocketInfo * structure. * * Results: * Returns a newly allocated SocketInfo. *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -