tcpcontrolblock.java

来自「纯java操作系统jnode,安装简单和操作简单的个人使用的Java操作系统」· Java 代码 · 共 751 行 · 第 1/2 页

JAVA
751
字号
			log.error("Error in timeout of " + this, ex);
		}
	}

	// ------------------------------------------
	// Utility methods
	// ------------------------------------------

	/**
	 * Notify a segment drop to the debug log
	 */
	private void drop(IPv4Header ipHdr, TCPHeader hdr, String reason) {
		log.debug("Dropping segment due to: " + reason);
	}

	/**
	 * Send a ACK segment
	 */
	protected final void sendACK(int extraFlags, int ackNr) throws SocketException {
		log.debug("sendACK(0x" + NumberUtils.hex(extraFlags, 4) + ", " + (ackNr & 0xFFFFFFFFL) + ")");

		// Create the FIN TCP reply
		final TCPHeader replyHdr = createOutgoingTCPHeader(extraFlags | TCPF_ACK, ackNr);
		// ACK takes 0 seq-nrs, so don't increment snd_next

		// Create the IP reply header
		final IPv4Header replyIp = createOutgoingIPv4Header();

		// Send the reply
		outChannel.send(replyIp, replyHdr);
	}

	/**
	 * Send a FIN segment
	 */
	private final void sendFIN() throws SocketException {
		log.debug("sendFIN");

		// Create the FIN TCP reply
		final TCPHeader replyHdr = createOutgoingTCPHeader(TCPF_FIN | TCPF_ACK, inChannel.getRcvNext());

		// Create the IP reply header
		final IPv4Header replyIp = createOutgoingIPv4Header();

		// Send the reply
		outChannel.send(replyIp, replyHdr);
	}

	/**
	 * Send a RST segment
	 */
	private final void sendRST() throws SocketException {
		log.debug("sendRST");

		// Create the RST TCP reply
		final TCPHeader replyHdr = createOutgoingTCPHeader(TCPF_RST, 0);
		// RST takes 0 seg-nrs TODO is this correct????

		// Create the IP reply header
		final IPv4Header replyIp = createOutgoingIPv4Header();

		// Send the reply
		outChannel.send(replyIp, replyHdr);
	}

	/**
	 * Send a SYN segment
	 */
	private final void sendSYN() throws SocketException {
		log.debug("sendSYN");

		// Create the SYN TCP
		final TCPHeader hdr = createOutgoingTCPHeader(TCPF_SYN, 0);

		// Create the IP reply header
		final IPv4Header ipHdr = createOutgoingIPv4Header();

		// Send the reply
		outChannel.send(ipHdr, hdr);
	}

	/**
	 * Notify this listening parent that one of my children have established a connection.
	 * 
	 * @param child
	 */
	private synchronized void notifyChildEstablished(TCPControlBlock child) throws SocketException {
		if (isState(TCPS_LISTEN)) {
			// Put on the waiting list for accept to handle and notify blocked threads.
			readyToAcceptList.add(child);
			notifyAll();
		} else {
			// I'm not listening anymore, close the connection.
			child.appClose();
		}
	}

	/**
	 * Notify a connection reset
	 */
	private synchronized void notifyConnectionReset() {
		this.reset = true;
		inChannel.notifyConnectionReset();
		outChannel.notifyConnectionReset();
		notifyAll();
	}

	/**
	 * Notify a connection refused
	 */
	private void notifyConnectionRefused() {
		this.refused = true;
		notifyAll();
	}

	/**
	 * Is the current state equal to the given state?
	 * 
	 * @param state
	 * @return
	 */
	private boolean isState(int state) {
		return (this.curState == state);
	}

	/**
	 * Update the state and notify any waiting threads
	 * 
	 * @param state
	 */
	private synchronized void setState(int state) throws SocketException {
		if (this.curState != state) {
			this.curState = state;
			if (state == TCPS_CLOSED) {
				super.removeFromList();
			}
			notifyAll();
		}
	}

	/**
	 * Wait until the state is equal to the given state or the connection is reset or refused.
	 */
	private synchronized void waitUntilState(int state, long timeout) throws TimeoutException {
		final long start = System.currentTimeMillis();
		while (!isState(state) && !isReset() && !isRefused()) {
			final long now = System.currentTimeMillis();
			if ((start + timeout <= now) && (timeout > 0)) {
				// We have a timeout
				throw new TimeoutException();
			}
			try {
				wait(timeout - (now - start));
			} catch (InterruptedException ex) {
				// Ignore
			}
		}
	}

	/**
	 * Create a TCP header for outgoing trafic
	 * 
	 * @param options
	 * @return The created TCP header
	 */
	protected TCPHeader createOutgoingTCPHeader(int options, int ackNr) {
		final TCPHeader hdr = new TCPHeader(getLocalPort(), getForeignPort(), 0, 0, ackNr, outWindowSize, 0);
		hdr.setFlags(options);
		return hdr;
	}

	// ------------------------------------------
	// Application methods
	// ------------------------------------------

	/**
	 * Wait for incoming requests
	 * 
	 * @throws SocketException
	 */
	public synchronized void appListen() throws SocketException {
		if (!isState(TCPS_CLOSED)) {
			throw new SocketException("Invalid connection state " + getStateName());
		}
		setState(TCPS_LISTEN);
	}

	/**
	 * Active connect to a foreign address. This method blocks until the connection has been
	 * established.
	 * 
	 * @throws SocketException
	 */
	public synchronized void appConnect(IPv4Address fAddr, int fPort) throws SocketException {
		if (!isState(TCPS_CLOSED)) {
			throw new SocketException("Invalid connection state " + getStateName());
		}
		super.connect(getLocalAddress(), fAddr, fPort);
		for (int attempt = 0; attempt < TCP_MAXCONNECT; attempt++) {
			try {
				// Send the SYN
				sendSYN();
				// Update the state
				setState(TCPS_SYN_SENT);
				// Wait for an ESTABLISHED state
				waitUntilState(TCPS_ESTABLISHED, timeout);
				// Check for reset condition
				if (isRefused()) {
					throw new ConnectException("Connection refused");
				}
				log.debug("Connected to " + fAddr + ":" + fPort);
				return;
			} catch (TimeoutException ex) {
				// Ignore and just try again
			}
		}
		// Not succeeded to connect
		throw new ConnectException("Connection request timeout");
	}

	/**
	 * Wait for an established connection.
	 * 
	 * @return The accepted connection
	 */
	public synchronized TCPControlBlock appAccept() {
		while (true) {
			if (!readyToAcceptList.isEmpty()) {
				final TCPControlBlock child = (TCPControlBlock) readyToAcceptList.getFirst();
				readyToAcceptList.remove(child);
				return child;
			} else {
				try {
					wait();
				} catch (InterruptedException ex) {
					// Ignore
				}
			}
		}
	}

	/**
	 * Active close the connection by the application.
	 */
	public synchronized void appClose() throws SocketException {
		if (log.isDebugEnabled()) {
			log.debug("active close state=" + getStateName());
		}
		try {
			switch (curState) {
				case TCPS_SYN_RECV :
				case TCPS_ESTABLISHED :
					{
						sendFIN();
						setState(TCPS_FIN_WAIT_1);
						waitUntilState(TCPS_CLOSED, 0);
					}
					break;
				case TCPS_SYN_SENT :
				case TCPS_LISTEN :
					{
						setState(TCPS_CLOSED);
					}
					break;
				case TCPS_CLOSE_WAIT :
					{
						sendFIN();
						setState(TCPS_LAST_ACK);
						waitUntilState(TCPS_CLOSED, 0);
					}
					break;
				default :
					throw new SocketException("Illegal state in close (" + getStateName() + ")");
			}
		} catch (TimeoutException ex) {
			throw (SocketException) new SocketException("Timeout").initCause(ex);
		}
		if (isReset()) {
			throw new SocketException("Connection reset");
		}
	}

	/**
	 * Send data to the foreign side. This method can split-up the data in chunks and blocks until
	 * there is space in the send buffer to hold the data.
	 * 
	 * @param data
	 * @param offset
	 * @param length
	 * @throws SocketException
	 */
	public void appSendData(byte[] data, int offset, int length) throws SocketException {
		log.debug("appSendData(data, " + offset + ", " + length + ")");
		if (!isState(TCPS_ESTABLISHED) && !isState(TCPS_CLOSE_WAIT)) {
			throw new SocketException("Illegal state to send data: " + getStateName());
		}
		if (offset < 0) {
			throw new IllegalArgumentException("offset " + offset);
		}
		if (length < 0) {
			throw new IllegalArgumentException("length " + length);
		}
		final int mss = outChannel.getMss();
		while (length > 0) {
			final int chunk = Math.min(length, mss);
			// Create the TCP header
			final TCPHeader hdr = createOutgoingTCPHeader(TCPF_ACK, inChannel.getRcvNext());
			// Create the IP header
			final IPv4Header ipHdr = createOutgoingIPv4Header();
			// Send the chunk of data
			outChannel.send(ipHdr, hdr, data, offset, chunk);
			// Update length & offset
			offset += chunk;
			length -= chunk;
		}
	}

	/**
	 * Return the number of available bytes in the input buffer.
	 */
	public int appAvailable() {
		return inChannel.available();
	}

	/**
	 * Read data from the input buffer up to len bytes long. Block until there is data available.
	 * 
	 * @param dst
	 * @param off
	 * @param len
	 * @return The number of bytes read
	 */
	public int appRead(byte[] dst, int off, int len) throws SocketException {
		return inChannel.read(dst, off, len);
	}

	/**
	 * @return Returns the state.
	 */
	public final int getState() {
		return this.curState;
	}

	/**
	 * @return Returns the name of the current state.
	 */
	public final String getStateName() {
		return TCP_STATE_NAMES[this.curState];
	}

	/**
	 * @see java.lang.Object#toString()
	 */
	public String toString() {
		return super.toString() + ", state " + TCP_STATE_NAMES[curState];
	}

	/**
	 * Has this connection been reset
	 * 
	 * @return Returns the reset.
	 */
	public final boolean isReset() {
		return this.reset;
	}

	/**
	 * @return Returns the refused.
	 */
	public final boolean isRefused() {
		return this.refused;
	}

}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?