📄 tclmacsock.c
字号:
WaitForSocketEvent( TcpState *statePtr, /* Information about this socket. */ int mask, /* Events to look for. */ int *errorCodePtr) /* Where to store errors? */{ OSErr err; TCPiopb statusPB; EventRecord dummy; /* * Loop until we get the specified condition, unless the socket is * asynchronous. */ do { statusPB.ioCRefNum = driverRefNum; statusPB.tcpStream = statePtr->tcpStream; statusPB.csCode = TCPStatus; err = PBControlSync((ParmBlkPtr) &statusPB); if (err != noErr) { statePtr->checkMask |= (TCL_READABLE | TCL_WRITABLE); return 1; } statePtr->checkMask = 0; if (statusPB.csParam.status.amtUnreadData > 0) { statePtr->checkMask |= TCL_READABLE; } if (!(statePtr->flags & TCP_WRITING) && (statusPB.csParam.status.sendWindow - statusPB.csParam.status.amtUnackedData) > 0) { statePtr->flags &= ~(TCP_ASYNC_CONNECT); statePtr->checkMask |= TCL_WRITABLE; } if (mask & statePtr->checkMask) { return 1; } /* * Call the system to let other applications run while we * are waiting for this event to occur. */ WaitNextEvent(0, &dummy, 1, NULL); } while (!(statePtr->flags & TCP_ASYNC_SOCKET)); *errorCodePtr = EWOULDBLOCK; return 0;} /* *---------------------------------------------------------------------- * * TcpAccept -- * Accept a TCP socket connection. This is called by the event * loop, and it in turns calls any registered callbacks for this * channel. * * Results: * None. * * Side effects: * Evals the Tcl script associated with the server socket. * *---------------------------------------------------------------------- */static voidTcpAccept( TcpState *statePtr){ TcpState *newStatePtr; StreamPtr tcpStream; char remoteHostname[255]; OSErr err; ip_addr remoteAddress; long remotePort; char channelName[20]; statePtr->flags &= ~TCP_LISTEN_CONNECT; statePtr->checkMask &= ~TCL_READABLE; /* * Transfer sever stream to new connection. */ tcpStream = statePtr->tcpStream; newStatePtr = NewSocketInfo(tcpStream); newStatePtr->tcpStream = tcpStream; sprintf(channelName, "sock%d", socketNumber++); newStatePtr->flags |= TCP_CONNECTED; newStatePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName, (ClientData) newStatePtr, (TCL_READABLE | TCL_WRITABLE)); Tcl_SetChannelBufferSize(newStatePtr->channel, socketBufferSize); Tcl_SetChannelOption(NULL, newStatePtr->channel, "-translation", "auto crlf"); remoteAddress = statePtr->pb.csParam.open.remoteHost; remotePort = statePtr->pb.csParam.open.remotePort; /* * Reopen passive connect. Make new tcpStream the server. */ ClearZombieSockets(); InitMacTCPParamBlock(&statePtr->pb, TCPCreate); statePtr->pb.csParam.create.rcvBuff = ckalloc(socketBufferSize); statePtr->pb.csParam.create.rcvBuffLen = socketBufferSize; err = PBControlSync((ParmBlkPtr) &statePtr->pb); if (err != noErr) { /* * Hmmm... We can't reopen the server. We'll go ahead * an continue - but we are kind of broken now... */ Debugger(); statePtr->tcpStream = -1; statePtr->flags |= TCP_SERVER_ZOMBIE; } tcpStream = statePtr->tcpStream = statePtr->pb.tcpStream; InitMacTCPParamBlock(&statePtr->pb, TCPPassiveOpen); statePtr->pb.tcpStream = tcpStream; statePtr->pb.csParam.open.localHost = 0; statePtr->pb.csParam.open.localPort = statePtr->port; statePtr->pb.ioCompletion = completeUPP; statePtr->pb.csParam.open.userDataPtr = (Ptr) statePtr; statePtr->flags |= TCP_LISTENING; err = PBControlAsync((ParmBlkPtr) &(statePtr->pb)); /* * TODO: deal with case where we can't recreate server socket... */ /* * Finally we run the accept procedure. We must do this last to make * sure we are in a nice clean state. This Tcl code can do anything * including closing the server or client sockets we've just delt with. */ if (statePtr->acceptProc != NULL) { sprintf(remoteHostname, "%d.%d.%d.%d", remoteAddress>>24, remoteAddress>>16 & 0xff, remoteAddress>>8 & 0xff, remoteAddress & 0xff); (statePtr->acceptProc)(statePtr->acceptProcData, newStatePtr->channel, remoteHostname, remotePort); }}/* *---------------------------------------------------------------------- * * Tcl_GetHostName -- * * Returns the name of the local host. * * Results: * A string containing the network name for this machine, or * an empty string if we can't figure out the name. The caller * must not modify or free this string. * * Side effects: * None. * *---------------------------------------------------------------------- */char *Tcl_GetHostName(){ static int hostnameInited = 0; static char hostname[255]; ip_addr ourAddress; Tcl_DString dString; OSErr err; if (hostnameInited) { return hostname; } if (TclHasSockets(NULL) == TCL_OK) { err = GetLocalAddress(&ourAddress); if (err == noErr) { /* * Search for the doman name and return it if found. Otherwise, * just print the IP number to a string and return that. */ Tcl_DStringInit(&dString); err = ResolveAddress(ourAddress, &dString); if (err == noErr) { strcpy(hostname, dString.string); } else { sprintf(hostname, "%d.%d.%d.%d", ourAddress>>24, ourAddress>>16 & 0xff, ourAddress>>8 & 0xff, ourAddress & 0xff); } Tcl_DStringFree(&dString); hostnameInited = 1; return hostname; } } hostname[0] = '\0'; hostnameInited = 1; return hostname;}/* *---------------------------------------------------------------------- * * ResolveAddress -- * * This function is used to resolve an ip address to it's full * domain name address. * * Results: * An os err value. * * Side effects: * Treats client data as int we set to true. * *---------------------------------------------------------------------- */static OSErr ResolveAddress( ip_addr tcpAddress, /* Address to resolve. */ Tcl_DString *dsPtr) /* Returned address in string. */{ int i; EventRecord dummy; DNRState dnrState; OSErr err; /* * Call AddrToName to resolve our ip address to our domain name. * The call is async, so we must wait for a callback to tell us * when to continue. */ for (i = 0; i < NUM_ALT_ADDRS; i++) { dnrState.hostInfo.addr[i] = 0; } dnrState.done = 0; GetCurrentProcess(&(dnrState.psn)); err = AddrToName(tcpAddress, &dnrState.hostInfo, resultUPP, (Ptr) &dnrState); if (err == cacheFault) { while (!dnrState.done) { WaitNextEvent(0, &dummy, 1, NULL); } } /* * If there is no error in finding the domain name we set the * result into the dynamic string. We also work around a bug in * MacTcp where an extranious '.' may be found at the end of the name. */ if (dnrState.hostInfo.rtnCode == noErr) { i = strlen(dnrState.hostInfo.cname) - 1; if (dnrState.hostInfo.cname[i] == '.') { dnrState.hostInfo.cname[i] = '\0'; } Tcl_DStringAppend(dsPtr, dnrState.hostInfo.cname, -1); } return dnrState.hostInfo.rtnCode;}/* *---------------------------------------------------------------------- * * DNRCompletionRoutine -- * * This function is called when the Domain Name Server is done * seviceing our request. It just sets a flag that we can poll * in functions like Tcl_GetHostName to let them know to continue. * * Results: * None. * * Side effects: * Treats client data as int we set to true. * *---------------------------------------------------------------------- */static pascal void DNRCompletionRoutine( struct hostInfo *hostinfoPtr, /* Host infor struct. */ DNRState *dnrStatePtr) /* Completetion state. */{ dnrStatePtr->done = true; WakeUpProcess(&(dnrStatePtr->psn));}/* *---------------------------------------------------------------------- * * CleanUpExitProc -- * * This procedure is invoked as an exit handler when ExitToShell * is called. It aborts any lingering socket connections. This * must be called or the Mac OS will more than likely crash. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */static pascal voidCleanUpExitProc(){ TCPiopb exitPB; TcpState *statePtr; while (socketList != NULL) { statePtr = socketList; socketList = statePtr->nextPtr; /* * Close and Release the connection. */ exitPB.ioCRefNum = driverRefNum; exitPB.csCode = TCPClose; exitPB.tcpStream = statePtr->tcpStream; exitPB.csParam.close.ulpTimeoutValue = 60 /* seconds */; exitPB.csParam.close.ulpTimeoutAction = 1 /* 1:abort 0:report */; exitPB.csParam.close.validityFlags = timeoutValue | timeoutAction; exitPB.ioCompletion = NULL; PBControlSync((ParmBlkPtr) &exitPB); exitPB.ioCRefNum = driverRefNum; exitPB.csCode = TCPRelease; exitPB.tcpStream = statePtr->tcpStream; exitPB.ioCompletion = NULL; PBControlSync((ParmBlkPtr) &exitPB); }}/* *---------------------------------------------------------------------- * * GetHostFromString -- * * Looks up the passed in domain name in the domain resolver. It * can accept strings of two types: 1) the ip number in string * format, or 2) the domain name. * * Results: * We return a ip address or 0 if there was an error or the * domain does not exist. * * Side effects: * None. * *---------------------------------------------------------------------- */static OSErrGetHostFromString( char *name, /* Host in string form. */ ip_addr *address) /* Returned IP address. */{ OSErr err; int i; EventRecord dummy; DNRState dnrState; if (TclHasSockets(NULL) != TCL_OK) { return 0; } /* * Call StrToAddr to get the ip number for the passed in domain * name. The call is async, so we must wait for a callback to * tell us when to continue. */ for (i = 0; i < NUM_ALT_ADDRS; i++) { dnrState.hostInfo.addr[i] = 0; } dnrState.done = 0; GetCurrentProcess(&(dnrState.psn)); err = StrToAddr(name, &dnrState.hostInfo, resultUPP, (Ptr) &dnrState); if (err == cacheFault) { while (!dnrState.done) { WaitNextEvent(0, &dummy, 1, NULL); } } /* * For some reason MacTcp may return a cachFault a second time via * the hostinfo block. This seems to be a bug in MacTcp. In this case * we run StrToAddr again - which seems to then work just fine. */ if (dnrState.hostInfo.rtnCode == cacheFault) { dnrState.done = 0; err = StrToAddr(name, &dnrState.hostInfo, resultUPP, (Ptr) &dnrState); if (err == cacheFault) { while (!dnrState.done) { WaitNextEvent(0, &dummy, 1, NULL); } } } if (dnrState.hostInfo.rtnCode == noErr) { *address = dnrState.hostInfo.addr[0]; } return dnrState.hostInfo.rtnCode;}/* *---------------------------------------------------------------------- * * IOCompletionRoutine -- * * This function is called when an asynchronous socket operation * completes. Since this routine runs as an interrupt handler, * it will simply set state to tell the notifier that this socket * is now ready for action. Note that this function is running at * interupt time and can't allocate memory or do much else except * set state. * * Results: * None. * * Side effects: * Sets some state in the socket state. May also wake the process * if we are not currently running. * *---------------------------------------------------------------------- */static voidIOCompletionRoutine( TCPiopb *pbPtr) /* Tcp parameter block. */{ TcpState *statePtr; if (pbPtr->csCode == TCPSend) { statePtr = (TcpState *) pbPtr->csParam.send.userDataPtr; } else { statePtr = (TcpState *) pbPtr->csParam.open.userDataPtr; } /* * Always wake the process in case it's in WaitNextEvent. * If an error has a occured - just return. We will deal * with the problem later. */ WakeUpProcess(&statePtr->psn); if (pbPtr->ioResult != noErr) { return; } if (statePtr->flags & TCP_ASYNC_CONNECT) { statePtr->flags &= ~TCP_ASYNC_CONNECT; statePtr->flags |= TCP_CONNECTED; statePtr->checkMask |= TCL_READABLE & TCL_WRITABLE; } else if (statePtr->flags & TCP_LISTENING) { if (statePtr->port == 0) { Debugger(); } statePtr->flags &= ~TCP_LISTENING; statePtr->flags |= TCP_LISTEN_CONNECT; statePtr->checkMask |= TCL_READABLE; } else if (statePtr->flags & TCP_WRITING) { statePtr->flags &= ~TCP_WRITING; statePtr->checkMask |= TCL_WRITABLE; if (!(statePtr->flags & TCP_CONNECTED)) { InitMacTCPParamBlock(&statePtr->pb, TCPClose); statePtr->pb.tcpStream = statePtr->tcpStream; statePtr->pb.ioCompletion = closeUPP;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -