📄 tclmacsock.c
字号:
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. * * Side effects: * Adds the socket to the global socket list, allocates memory. * *---------------------------------------------------------------------- */static TcpState *NewSocketInfo( StreamPtr tcpStream){ TcpState *statePtr; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); statePtr = (TcpState *) ckalloc((unsigned) sizeof(TcpState)); statePtr->tcpStream = tcpStream; statePtr->psn = applicationPSN; statePtr->flags = 0; statePtr->checkMask = 0; statePtr->watchMask = 0; statePtr->acceptProc = (Tcl_TcpAcceptProc *) NULL; statePtr->acceptProcData = (ClientData) NULL; statePtr->writeBuffer = (void *) NULL; statePtr->writeBufferSize = 0; statePtr->nextPtr = tsdPtr->socketList; tsdPtr->socketList = statePtr; return statePtr;}/* *---------------------------------------------------------------------- * * FreeSocketInfo -- * * This function deallocates a SocketInfo structure that is no * longer needed. * * Results: * None. * * Side effects: * Removes the socket from the global socket list, frees memory. * *---------------------------------------------------------------------- */static voidFreeSocketInfo( TcpState *statePtr) /* The state pointer to free. */{ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); if (statePtr == tsdPtr->socketList) { tsdPtr->socketList = statePtr->nextPtr; } else { TcpState *p; for (p = tsdPtr->socketList; p != NULL; p = p->nextPtr) { if (p->nextPtr == statePtr) { p->nextPtr = statePtr->nextPtr; break; } } } if (statePtr->writeBuffer != (void *) NULL) { ckfree(statePtr->writeBuffer); } ckfree((char *) statePtr);}/* *---------------------------------------------------------------------- * * 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. * *---------------------------------------------------------------------- */Tcl_ChannelTcl_MakeTcpClientChannel( ClientData sock) /* The socket to wrap up into a channel. */{ TcpState *statePtr; char channelName[20]; if (TclpHasSockets(NULL) != TCL_OK) { return NULL; } statePtr = NewSocketInfo((StreamPtr) sock); /* TODO: do we need to set the port??? */ sprintf(channelName, "sock%d", socketNumber++); statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName, (ClientData) statePtr, (TCL_READABLE | TCL_WRITABLE)); Tcl_SetChannelBufferSize(statePtr->channel, socketBufferSize); Tcl_SetChannelOption(NULL, statePtr->channel, "-translation", "auto crlf"); return statePtr->channel;}/* *---------------------------------------------------------------------- * * CreateSocket -- * * This function opens a new socket and initializes the * SocketInfo structure. * * Results: * Returns a new SocketInfo, or NULL with an error in interp. * * Side effects: * Adds a new socket to the socketList. * *---------------------------------------------------------------------- */static TcpState *CreateSocket( Tcl_Interp *interp, /* For error reporting; can be NULL. */ int port, /* Port number to open. */ CONST char *host, /* Name of host on which to open port. */ CONST char *myaddr, /* Optional client-side address */ int myport, /* Optional client-side port */ int server, /* 1 if socket should be a server socket, * else 0 for a client socket. */ int async) /* 1 create async, 0 do sync. */{ ip_addr macAddr; OSErr err; TCPiopb pb; StreamPtr tcpStream; TcpState *statePtr; char * buffer; /* * Figure out the ip address from the host string. */ if (host == NULL) { err = GetLocalAddress(&macAddr); } else { err = GetHostFromString(host, &macAddr); } if (err != noErr) { Tcl_SetErrno(EHOSTUNREACH); if (interp != (Tcl_Interp *) NULL) { Tcl_AppendResult(interp, "couldn't open socket: ", Tcl_PosixError(interp), (char *) NULL); } return (TcpState *) NULL; } /* * Create a MacTCP stream and create the state used for socket * transactions from here on out. */ ClearZombieSockets(); buffer = ckalloc(socketBufferSize); InitMacTCPParamBlock(&pb, TCPCreate); pb.csParam.create.rcvBuff = buffer; pb.csParam.create.rcvBuffLen = socketBufferSize; pb.csParam.create.notifyProc = nil /* notifyUPP */; err = PBControlSync((ParmBlkPtr) &pb); if (err != noErr) { Tcl_SetErrno(0); /* TODO: set to ENOSR - maybe?*/ if (interp != (Tcl_Interp *) NULL) { Tcl_AppendResult(interp, "couldn't open socket: ", Tcl_PosixError(interp), (char *) NULL); } return (TcpState *) NULL; } tcpStream = pb.tcpStream; statePtr = NewSocketInfo(tcpStream); statePtr->port = port; if (server) { /* * Set up server connection. */ InitMacTCPParamBlock(&statePtr->pb, TCPPassiveOpen); statePtr->pb.tcpStream = tcpStream; statePtr->pb.csParam.open.localPort = statePtr->port; statePtr->pb.ioCompletion = completeUPP; statePtr->pb.csParam.open.userDataPtr = (Ptr) statePtr; statePtr->pb.csParam.open.ulpTimeoutValue = 100; statePtr->pb.csParam.open.ulpTimeoutAction = 1 /* 1:abort 0:report */; statePtr->pb.csParam.open.commandTimeoutValue = 0 /* infinity */; statePtr->flags |= TCP_LISTENING; err = PBControlAsync((ParmBlkPtr) &(statePtr->pb)); /* * If this is a server on port 0 then we need to wait until * the dynamic port allocation is made by the MacTcp driver. */ if (statePtr->port == 0) { EventRecord dummy; while (statePtr->pb.csParam.open.localPort == 0) { WaitNextEvent(0, &dummy, 1, NULL); if (statePtr->pb.ioResult != 0) { break; } } statePtr->port = statePtr->pb.csParam.open.localPort; } Tcl_SetErrno(EINPROGRESS); } else { /* * Attempt to connect. The connect may fail at present with an * EINPROGRESS but at a later time it will complete. The caller * will set up a file handler on the socket if she is interested in * being informed when the connect completes. */ InitMacTCPParamBlock(&statePtr->pb, TCPActiveOpen); statePtr->pb.tcpStream = tcpStream; statePtr->pb.csParam.open.remoteHost = macAddr; statePtr->pb.csParam.open.remotePort = port; statePtr->pb.csParam.open.localHost = 0; statePtr->pb.csParam.open.localPort = myport; statePtr->pb.csParam.open.userDataPtr = (Ptr) statePtr; statePtr->pb.csParam.open.validityFlags = timeoutValue | timeoutAction; statePtr->pb.csParam.open.ulpTimeoutValue = 60 /* seconds */; statePtr->pb.csParam.open.ulpTimeoutAction = 1 /* 1:abort 0:report */; statePtr->pb.csParam.open.commandTimeoutValue = 0; statePtr->pb.ioCompletion = completeUPP; if (async) { statePtr->flags |= TCP_ASYNC_CONNECT; err = PBControlAsync((ParmBlkPtr) &(statePtr->pb)); Tcl_SetErrno(EINPROGRESS); } else { err = PBControlSync((ParmBlkPtr) &(statePtr->pb)); } } switch (err) { case noErr: if (!async) { statePtr->flags |= TCP_CONNECTED; } return statePtr; case duplicateSocket: Tcl_SetErrno(EADDRINUSE); break; case openFailed: case connectionTerminated: Tcl_SetErrno(ECONNREFUSED); break; case invalidStreamPtr: case connectionExists: default: /* * These cases should never occur. However, we will fail * gracefully and hope Tcl can resume. The alternative is to panic * which is probably a bit drastic. */ Debugger(); Tcl_SetErrno(err); } /* * We had error during the connection. Release the stream * and file handle. Also report to the interp. */ pb.ioCRefNum = driverRefNum; pb.csCode = TCPRelease; pb.tcpStream = tcpStream; pb.ioCompletion = NULL; err = PBControlSync((ParmBlkPtr) &pb); if (interp != (Tcl_Interp *) NULL) { Tcl_AppendResult(interp, "couldn't open socket: ", Tcl_PosixError(interp), (char *) NULL); } ckfree(buffer); FreeSocketInfo(statePtr); return (TcpState *) NULL;}/* *---------------------------------------------------------------------- * * Tcl_OpenTcpClient -- * * Opens a TCP client socket and creates a channel around it. * * Results: * The channel or NULL if failed. On failure, the routine also * sets the output argument errorCodePtr to the error code. * * Side effects: * Opens a client socket and creates a new channel. * *---------------------------------------------------------------------- */Tcl_ChannelTcl_OpenTcpClient( Tcl_Interp *interp, /* For error reporting; can be NULL. */ int port, /* Port number to open. */ CONST char *host, /* Host on which to open port. */ CONST char *myaddr, /* Client-side address */ int myport, /* Client-side port */ int async) /* If nonzero, attempt to do an * asynchronous connect. Otherwise * we do a blocking connect. * - currently ignored */{ TcpState *statePtr; char channelName[20]; if (TclpHasSockets(interp) != TCL_OK) { return NULL; } /* * Create a new client socket and wrap it in a channel. */ statePtr = CreateSocket(interp, port, host, myaddr, myport, 0, async); if (statePtr == NULL) { return NULL; } sprintf(channelName, "sock%d", socketNumber++); statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName, (ClientData) statePtr, (TCL_READABLE | TCL_WRITABLE)); Tcl_SetChannelBufferSize(statePtr->channel, socketBufferSize); Tcl_SetChannelOption(NULL, statePtr->channel, "-translation", "auto crlf"); return statePtr->channel;}/* *---------------------------------------------------------------------- * * Tcl_OpenTcpServer -- * * Opens a TCP server socket and creates a channel around it. *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -