📄 tclmacsock.c
字号:
* Side effects: * Adds the socket to the global socket list, allocates memory. * *---------------------------------------------------------------------- */static TcpState *NewSocketInfo( StreamPtr tcpStream){ TcpState *statePtr; 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->nextPtr = socketList; 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. */{ if (statePtr == socketList) { socketList = statePtr->nextPtr; } else { TcpState *p; for (p = socketList; p != NULL; p = p->nextPtr) { if (p->nextPtr == statePtr) { p->nextPtr = statePtr->nextPtr; break; } } } 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 (TclHasSockets(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. */ char *host, /* Name of host on which to open port. */ 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; 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->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.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. */ char *host, /* Host on which to open port. */ 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 (TclHasSockets(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. * * Results: * The channel or NULL if failed. * * Side effects: * Opens a server socket and creates a new channel. * *---------------------------------------------------------------------- */Tcl_ChannelTcl_OpenTcpServer( Tcl_Interp *interp, /* For error reporting - may be * NULL. */ int port, /* Port number to open. */ char *host, /* Name of local host. */ Tcl_TcpAcceptProc *acceptProc, /* Callback for accepting connections * from new clients. */ ClientData acceptProcData) /* Data for the callback. */{ TcpState *statePtr; char channelName[20]; if (TclHasSockets(interp) != TCL_OK) { return NULL; } /* * Create a new client socket and wrap it in a channel. */ statePtr = CreateSocket(interp, port, host, NULL, 0, 1, 1); if (statePtr == NULL) { return NULL; } statePtr->acceptProc = acceptProc; statePtr->acceptProcData = acceptProcData; sprintf(channelName, "sock%d", socketNumber++); statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName, (ClientData) statePtr, 0); Tcl_SetChannelBufferSize(statePtr->channel, socketBufferSize); Tcl_SetChannelOption(NULL, statePtr->channel, "-translation", "auto crlf"); return statePtr->channel;}/* *---------------------------------------------------------------------- * * SocketEventProc -- * * This procedure is called by Tcl_ServiceEvent when a socket event * reaches the front of the event queue. This procedure is * responsible for notifying the generic channel code. * * Results: * Returns 1 if the event was handled, meaning it should be removed * from the queue. Returns 0 if the event was not handled, meaning * it should stay on the queue. The only time the event isn't * handled is if the TCL_FILE_EVENTS flag bit isn't set. * * Side effects: * Whatever the channel callback procedures do. * *---------------------------------------------------------------------- */static intSocketEventProc( Tcl_Event *evPtr, /* Event to service. */ int flags) /* Flags that indicate what events to * handle, such as TCL_FILE_EVENTS. */{ TcpState *statePtr; SocketEvent *eventPtr = (SocketEvent *) evPtr; int mask = 0; if (!(flags & TCL_FILE_EVENTS)) { return 0; } /* * Find the specified socket on the socket list. */ for (statePtr = socketList; statePtr != NULL; statePtr = statePtr->nextPtr) { if ((statePtr == eventPtr->statePtr) && (statePtr->tcpStream == eventPtr->tcpStream)) { break; } } /* * Discard events that have gone stale. */ if (!statePtr) { return 1; } statePtr->flags &= ~(TCP_PENDING); if (statePtr->flags & TCP_RELEASE) { SocketFreeProc(statePtr); return 1; } /* * Handle connection requests directly. */ if (statePtr->flags & TCP_LISTEN_CONNECT) { if (statePtr->checkMask & TCL_READABLE) { TcpAccept(statePtr); } return 1; } /* * Mask off unwanted events then notify the channel. */ mask = statePtr->checkMask & statePtr->watchMask; if (mask) { Tcl_NotifyChannel(statePtr->channel, mask); } return 1;}/* *---------------------------------------------------------------------- * * WaitForSocketEvent -- * * Waits until one of the specified events occurs on a socket. * * Results: * Returns 1 on success or 0 on failure, with an error code in * errorCodePtr. * * Side effects: * Processes socket events off the system queue. * *---------------------------------------------------------------------- */static int
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -