From 816c80baa9ed96b381172d5ad0a021a722d4ed42 Mon Sep 17 00:00:00 2001 From: bgrozev Date: Mon, 7 Oct 2024 11:52:33 -0500 Subject: [PATCH] Move pseudotcp to its own project (#292) * ref: Remove pseudotcp (moved to jitsi-pseudotcp). --- README.md | 5 +- pom.xml | 15 - .../java/org/ice4j/pseudotcp/EnShutdown.java | 39 - .../org/ice4j/pseudotcp/IPseudoTcpNotify.java | 62 - src/main/java/org/ice4j/pseudotcp/Option.java | 51 - .../org/ice4j/pseudotcp/PseudoTCPBase.java | 2133 ----------------- .../ice4j/pseudotcp/PseudoTcpJavaSocket.java | 36 - .../org/ice4j/pseudotcp/PseudoTcpNotify.java | 63 - .../org/ice4j/pseudotcp/PseudoTcpSocket.java | 292 --- .../pseudotcp/PseudoTcpSocketFactory.java | 151 -- .../ice4j/pseudotcp/PseudoTcpSocketImpl.java | 1240 ---------- .../org/ice4j/pseudotcp/PseudoTcpState.java | 48 - .../java/org/ice4j/pseudotcp/RSegment.java | 35 - .../java/org/ice4j/pseudotcp/SSegment.java | 39 - .../java/org/ice4j/pseudotcp/Segment.java | 37 - .../java/org/ice4j/pseudotcp/SendFlags.java | 28 - .../java/org/ice4j/pseudotcp/WriteResult.java | 38 - .../ice4j/pseudotcp/util/ByteFifoBuffer.java | 397 --- .../pseudotcp/MultiThreadSupportTest.java | 87 - .../ice4j/pseudotcp/PseudoTcpStreamTest.java | 322 --- .../ice4j/pseudotcp/PseudoTcpTestBase.java | 684 ------ .../pseudotcp/PseudoTcpTestPingPong.java | 363 --- .../pseudotcp/PseudoTcpTestRecvWindow.java | 419 ---- .../pseudotcp/PseudoTcpTestTransfer.java | 456 ---- .../pseudotcp/util/ByteFifoBufferTest.java | 364 --- src/test/java/test/IcePseudoTcp.java | 438 ---- src/test/resources/logging.properties | 1 - 27 files changed, 4 insertions(+), 7839 deletions(-) delete mode 100644 src/main/java/org/ice4j/pseudotcp/EnShutdown.java delete mode 100755 src/main/java/org/ice4j/pseudotcp/IPseudoTcpNotify.java delete mode 100644 src/main/java/org/ice4j/pseudotcp/Option.java delete mode 100755 src/main/java/org/ice4j/pseudotcp/PseudoTCPBase.java delete mode 100644 src/main/java/org/ice4j/pseudotcp/PseudoTcpJavaSocket.java delete mode 100755 src/main/java/org/ice4j/pseudotcp/PseudoTcpNotify.java delete mode 100644 src/main/java/org/ice4j/pseudotcp/PseudoTcpSocket.java delete mode 100644 src/main/java/org/ice4j/pseudotcp/PseudoTcpSocketFactory.java delete mode 100755 src/main/java/org/ice4j/pseudotcp/PseudoTcpSocketImpl.java delete mode 100755 src/main/java/org/ice4j/pseudotcp/PseudoTcpState.java delete mode 100644 src/main/java/org/ice4j/pseudotcp/RSegment.java delete mode 100644 src/main/java/org/ice4j/pseudotcp/SSegment.java delete mode 100644 src/main/java/org/ice4j/pseudotcp/Segment.java delete mode 100644 src/main/java/org/ice4j/pseudotcp/SendFlags.java delete mode 100755 src/main/java/org/ice4j/pseudotcp/WriteResult.java delete mode 100755 src/main/java/org/ice4j/pseudotcp/util/ByteFifoBuffer.java delete mode 100755 src/test/java/org/ice4j/pseudotcp/MultiThreadSupportTest.java delete mode 100755 src/test/java/org/ice4j/pseudotcp/PseudoTcpStreamTest.java delete mode 100755 src/test/java/org/ice4j/pseudotcp/PseudoTcpTestBase.java delete mode 100755 src/test/java/org/ice4j/pseudotcp/PseudoTcpTestPingPong.java delete mode 100755 src/test/java/org/ice4j/pseudotcp/PseudoTcpTestRecvWindow.java delete mode 100755 src/test/java/org/ice4j/pseudotcp/PseudoTcpTestTransfer.java delete mode 100755 src/test/java/org/ice4j/pseudotcp/util/ByteFifoBufferTest.java delete mode 100755 src/test/java/test/IcePseudoTcp.java diff --git a/README.md b/README.md index 8a9eac40..d20d324e 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,13 @@ ice4j ===== The Interactive Connectivity Establishment (ICE) protocol combines various NAT traversal utilities such as the STUN and TURN protocols in order to offer a powerful mechanism that allows Offer/Answer based protocols such as SIP and XMPP to traverse NATs. -This project provides a Java implementation of the ICE protocol that would be usable by both SIP and XMPP applications. The project also provides features such as socket sharing and support for Pseudo TCP. +This project provides a Java implementation of the ICE protocol that would be usable by both SIP and XMPP applications. The project also provides features such as socket sharing. ice4j is maintained by the [Jitsi](https://jitsi.org/) community. Use Jitsi's [community forum](https://community.jitsi.org) for questions and discussions. +## Pseudo TCP +The pseudo TCP implementation was moved it its own [project](https://github.com/jitsi/jitsi-pseudotcp). + Thanks ------ Work on this project was graciously funded by the [NLnet Foundation](https://nlnet.nl/). Thank you! diff --git a/pom.xml b/pom.xml index 54e2d4a5..f3660726 100644 --- a/pom.xml +++ b/pom.xml @@ -359,21 +359,6 @@ - - run-pseudotcp - - - - org.codehaus.mojo - exec-maven-plugin - - test.IcePseudoTcp - - - - - - run-dist diff --git a/src/main/java/org/ice4j/pseudotcp/EnShutdown.java b/src/main/java/org/ice4j/pseudotcp/EnShutdown.java deleted file mode 100644 index 068a3de5..00000000 --- a/src/main/java/org/ice4j/pseudotcp/EnShutdown.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * ice4j, the OpenSource Java Solution for NAT and Firewall Traversal. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ice4j.pseudotcp; - -/** - * EnShutdown enumeration used internally - * - * @author Pawel Domas - */ -enum EnShutdown -{ - /** - * There was no shutdown - */ - SD_NONE, - /** - * There was a graceful shutdown - */ - SD_GRACEFUL, - /** - * There was a forceful shutdown - */ - SD_FORCEFUL -} diff --git a/src/main/java/org/ice4j/pseudotcp/IPseudoTcpNotify.java b/src/main/java/org/ice4j/pseudotcp/IPseudoTcpNotify.java deleted file mode 100755 index 155967d9..00000000 --- a/src/main/java/org/ice4j/pseudotcp/IPseudoTcpNotify.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * ice4j, the OpenSource Java Solution for NAT and Firewall Traversal. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ice4j.pseudotcp; - -import java.io.*; - -/** - * Notification of tcp events. - * Is implemented by @link(PseudoTcpSocket) to expose stream functionality. - * @author Pawel Domas - */ -interface IPseudoTcpNotify -{ - /** - * Called when tcp enters opened state - * @param tcp - */ - void OnTcpOpen(PseudoTCPBase tcp); - - /** - * Called when any data is available in read buffer - * @param tcp - */ - void OnTcpReadable(PseudoTCPBase tcp); - - /** - * Called when there is free space available in the send buffer - * @param tcp - */ - void OnTcpWriteable(PseudoTCPBase tcp); - - /** - * Called when tcp enters closed state - * @param tcp - * @param e null means no error - */ - void OnTcpClosed(PseudoTCPBase tcp, IOException e); - - /** - * Called when protocol requests packet transfer through the network. - * @param tcp - * @param buffer data - * @param len data length - * @return the result, see {@link WriteResult} description for more info - */ - WriteResult TcpWritePacket(PseudoTCPBase tcp, byte[] buffer, int len); -} diff --git a/src/main/java/org/ice4j/pseudotcp/Option.java b/src/main/java/org/ice4j/pseudotcp/Option.java deleted file mode 100644 index e0c9d88f..00000000 --- a/src/main/java/org/ice4j/pseudotcp/Option.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * ice4j, the OpenSource Java Solution for NAT and Firewall Traversal. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ice4j.pseudotcp; - -/** - * Options used in PseudoTCP - * - * @author Pawel Domas - */ -public enum Option -{ - /** - * Whether to enable Nagle's algorithm (0 == off) - */ - OPT_NODELAY, - /** - * The Delayed ACK timeout (0 == off). - */ - OPT_ACKDELAY, - /** - * Set the receive buffer size, in bytes. - */ - OPT_RCVBUF, - /** - * Set the send buffer size, in bytes. - */ - OPT_SNDBUF, - /** - * Timeout in ms for read operations(0 - no timeout) - */ - OPT_READ_TIMEOUT, - /** - * Timeout in ms for write operations(0 - no timeout) - */ - OPT_WRITE_TIMEOUT -} diff --git a/src/main/java/org/ice4j/pseudotcp/PseudoTCPBase.java b/src/main/java/org/ice4j/pseudotcp/PseudoTCPBase.java deleted file mode 100755 index cadb3e52..00000000 --- a/src/main/java/org/ice4j/pseudotcp/PseudoTCPBase.java +++ /dev/null @@ -1,2133 +0,0 @@ -/* - * ice4j, the OpenSource Java Solution for NAT and Firewall Traversal. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ice4j.pseudotcp; - -import java.io.*; -import java.util.*; -import java.util.logging.*; -import org.ice4j.pseudotcp.util.*; - -/** - * Main protocol logic class. To open connection use connect method. - * Then recv and send operations may be used for data transfer. - * To operate this class requires implementation of PseudoTcpNotify. - * Also it must be notified about the time progress. - * Based on https://developers.google.com/talk/libjingle/ - * - * @see PseudoTCPBase#connect - * @see PseudoTCPBase#recv - * @see PseudoTCPBase#send - * @see PseudoTcpNotify - * @author Pawel Domas - */ -public class PseudoTCPBase -{ - /** - * The logger. - */ - private static final Logger logger = - Logger.getLogger(PseudoTCPBase.class.getName()); - /** - * Keepalive - disabled by default - */ - private static boolean PSEUDO_KEEPALIVE = false; - /** - * Packet maximum levels - */ - static final int[] PACKET_MAXIMUMS = new int[] - { - 65535, // Theoretical maximum, Hyperchannel - 32000, // Nothing - 17914, // 16Mb IBM Token Ring - 8166, // IEEE 802.4 - //4464, // IEEE 802.5 (4Mb max) - 4352, // FDDI - //2048, // Wideband Network - 2002, // IEEE 802.5 (4Mb recommended) - //1536, // Expermental Ethernet Networks - //1500, // Ethernet, Point-to-Point (default) - 1492, // IEEE 802.3 - 1006, // SLIP, ARPANET - //576, // X.25 Networks - //544, // DEC IP Portal - //512, // NETBIOS - 508, // IEEE 802/Source-Rt Bridge, ARCNET - 296, // Point-to-Point (low delay) - //68, // Official minimum - 0, // End of list marker - }; - static final int MAX_PACKET = 65535; - // Note: we removed lowest level because packet overhead was larger! - static final int MIN_PACKET = 296; - static final int IP_HEADER_SIZE = 20; // (+ up to 40 bytes of options?) - static final int ICMP_HEADER_SIZE = 8; - static final int UDP_HEADER_SIZE = 8; - // TODO: Make JINGLE_HEADER_SIZE transparent to this code? - static final int JINGLE_HEADER_SIZE = 64; // when relay framing is in use - // Default size for receive and send buffer. - public static final int DEFAULT_RCV_BUF_SIZE = 60 * 1024; - public static final int DEFAULT_SND_BUF_SIZE = 90 * 1024; - ////////////////////////////////////////////////////////////////////// - // Global Constants and Functions - ////////////////////////////////////////////////////////////////////// - // - // 0 1 2 3 - // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // 0 | Conversation Number | - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // 4 | Sequence Number | - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // 8 | Acknowledgment Number | - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // | | |U|A|P|R|S|F| | - // 12 | Control | |R|C|S|S|Y|I| Window | - // | | |G|K|H|T|N|N| | - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // 16 | Timestamp sending | - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // 20 | Timestamp receiving | - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // 24 | data | - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // - ////////////////////////////////////////////////////////////////////// - static final long MAX_SEQ = 0xFFFFFFFFL; - static final int HEADER_SIZE = 24; - static final int PACKET_OVERHEAD = HEADER_SIZE + UDP_HEADER_SIZE + IP_HEADER_SIZE + JINGLE_HEADER_SIZE; - static final long MIN_RTO = 250; // 250 ms (RFC1122, Sec 4.2.3.1 "fractions of a second") - static final long DEF_RTO = 3000; // 3 seconds (RFC1122, Sec 4.2.3.1) - static final long MAX_RTO = 60000; // 60 seconds - static final long DEF_ACK_DELAY = 100; // 100 milliseconds - static final short FLAG_CTL = 0x02; - static final short FLAG_RST = 0x04; - static final short CTL_CONNECT = 0; - //static final short CTL_REDIRECT = 1; - static final short CTL_EXTRA = 255; - // TCP options. - /** - * End of list - */ - static final short TCP_OPT_EOL = 0; - /** - * No-op - */ - static final short TCP_OPT_NOOP = 1; - /** - * Maximum segment size - */ - static final short TCP_OPT_MSS = 2; - /** - * Window scale factor - */ - static final short TCP_OPT_WND_SCALE = 3; - //static final short FLAG_FIN = 0x01; static final short FLAG_SYN = 0x02; - //static final short FLAG_ACK = 0x10; - static final int CTRL_BOUND = 0x80000000; - /** - * If there are no pending clocks, wake up every 4 seconds - */ - static final long DEFAULT_TIMEOUT = 4000; - /** - * If the connection is closed, once per minute - */ - static final long CLOSED_TIMEOUT = 60 * 1000; // - /** - * Idle ping interval - */ - static final int IDLE_PING = 20 * 1000; // 20 seconds (note: WinXP SP2 firewall udp timeout is 90 seconds) - /** - * Idle timeout(used if keepalive is enabled) - */ - static final int IDLE_TIMEOUT = 90 * 1000; // 90 seconds; - // TCB data - /** - * Tcp state - */ - PseudoTcpState m_state; - /** - * Conversation number - */ - long m_conv; - boolean m_bReadEnable, m_bWriteEnable, m_bOutgoing; - /** - * Last traffic timestamp - */ - long m_lasttraffic; - /** - * List of incoming segments. Segments store info like stream offset and - * control flags. If segment contains any data it is stored in the receive - * buffer. - */ - List m_rlist = new ArrayList<>(); - /** - * Last receive timestamp - */ - long m_lastrecv; - /** - * Receive buffer length - */ - int m_rbuf_len; - /** - * The sequence number of the next byte of data that is expected from the - * other device - */ - int m_rcv_nxt; - /** - * Receive window size - */ - int m_rcv_wnd; - /** - * Window scale factor - */ - private short m_rwnd_scale; - /** - * The receive buffer - */ - ByteFifoBuffer m_rbuf; - /** - * Outgoing segments list - */ - List m_slist = new ArrayList<>(); - /** - * Last send timestamp - */ - long m_lastsend; - /** - * The sequence number of the next byte of data to be sent - */ - long m_snd_nxt; - /** - * The sequence number of the first byte of data that has been sent but not - * yet acknowledged - */ - long m_snd_una; - /** - * The send buffer's size - */ - int m_sbuf_len; - /** - * Send window size - */ - private int m_snd_wnd; - /** - * Send window scale factor - */ - private short m_swnd_scale; - /** - * The send buffer - */ - ByteFifoBuffer m_sbuf; - // Maximum segment size, estimated protocol level, largest segment sent - /** - * - */ - long m_mss; - /** - * - */ - long m_largest; - /** - * - */ - long m_mtu_advise; - /** - * - */ - int m_msslevel; - /** - * Retransmit timer - */ - long m_rto_base; - /** - * Timestamp tracking - */ - long m_ts_recent, m_ts_lastack; - /** - * Round-trip calculation - */ - long m_rx_rttvar, m_rx_srtt, m_rx_rto; - /** - * Congestion avoidance, Fast retransmit/recovery, Delayed ACKs - */ - long m_ssthresh, m_cwnd; - short m_dup_acks; - long m_recover; - long m_t_ack; - // Configuration options - /** - * Use nagling - */ - boolean m_use_nagling; - /* - * Acknowledgment delay - */ - long m_ack_delay; - boolean m_support_wnd_scale; - PseudoTcpNotify m_notify; - EnShutdown m_shutdown; - /** - * Debug name used to identify peers in log messages - */ - String debugName = ""; - - ////////////////////////////////////////////////////////////////////// - // PseudoTcp - ////////////////////////////////////////////////////////////////////// - /** - * - * @param notify {@link PseudoTcpNotify} implementation - * @param conv the conversation number used by this instance - */ - public PseudoTCPBase(PseudoTcpNotify notify, long conv) - { - m_notify = notify; - m_shutdown = EnShutdown.SD_NONE; - m_rbuf_len = DEFAULT_RCV_BUF_SIZE; - m_rbuf = new ByteFifoBuffer(m_rbuf_len); - m_sbuf_len = DEFAULT_SND_BUF_SIZE; - m_sbuf = new ByteFifoBuffer(m_sbuf_len); - // Sanity check on buffer sizes (needed for OnTcpWriteable notification logic) - assert m_rbuf_len + MIN_PACKET < m_sbuf_len; - long now = now(); - - m_state = PseudoTcpState.TCP_LISTEN; - m_conv = conv; - m_rcv_wnd = m_rbuf_len; - m_rwnd_scale = m_swnd_scale = 0; - m_snd_nxt = 0; - m_snd_wnd = 1; - m_snd_una = m_rcv_nxt = 0; - m_bReadEnable = true; - m_bWriteEnable = false; - m_t_ack = 0; - - m_msslevel = 0; - m_largest = 0; - assert MIN_PACKET > PACKET_OVERHEAD; - m_mss = MIN_PACKET - PACKET_OVERHEAD; - m_mtu_advise = MAX_PACKET; - - m_rto_base = 0; - - m_cwnd = 2 * m_mss; - m_ssthresh = m_rbuf_len; - m_lastrecv = m_lastsend = m_lasttraffic = now; - m_bOutgoing = false; - - m_dup_acks = 0; - m_recover = 0; - - m_ts_recent = m_ts_lastack = 0; - - m_rx_rto = DEF_RTO; - m_rx_srtt = m_rx_rttvar = 0; - - m_use_nagling = true; - m_ack_delay = DEF_ACK_DELAY; - m_support_wnd_scale = false; - } - - /** - * Enqueues connect message and starts connection procedure - * - * @throws IOException if the protocol is not in initial state - */ - public void connect() throws IOException - { - if (m_state != PseudoTcpState.TCP_LISTEN) - { - //m_error = PseudoTcpError.EINVAL; - throw new IOException("Invalid socket state: "+m_state); - } - - m_state = PseudoTcpState.TCP_SYN_SENT; - logger.log(Level.FINE, "State: TCP_SYN_SENT", ""); - - queueConnectMessage(); - attemptSend(SendFlags.sfNone); - } - - /** - * Set the MTU (maximum transmission unit) value - * - * @param mtu the new MTU value - */ - public void notifyMTU(int mtu) - { - m_mtu_advise = mtu; - if (m_state == PseudoTcpState.TCP_ESTABLISHED) - { - adjustMTU(); - } - } - - /** - * - * @return MTU value - */ - public int getMTU() - { - return (int)m_mtu_advise; - } - - /** - * @return current timestamp limited to 32 bits - */ - public static long now() - { - return (System.nanoTime() / 1000000) & 0xFFFFFFFFL; - } - - /** - * Evaluate next interval between getNextClock calls. - * It is based on current protocol action timeout - * - * @param now current timestamp - * @return next interval - * - */ - public long getNextClock(long now) - { - return clock_check(now); - } - - /** - * This method should be called in time intervals retrieved - * from getNextClock - * - * @param now current timestamp - * @see PseudoTCPBase#getNextClock(long) - */ - public void notifyClock(long now) - { - /*if (logger.isLoggable(Level.FINEST)) - { - logger.log(Level.FINEST, debugName + " update clock " + now); - }*/ - if (m_state == PseudoTcpState.TCP_CLOSED) - { - return; - } - - // Check if it's time to retransmit a segment - if (m_rto_base > 0 && (timeDiff(m_rto_base + m_rx_rto, now) <= 0)) - { - assert !m_slist.isEmpty(); - // retransmit segments - if (logger.isLoggable(Level.FINER)) - { - logger.log(Level.FINER, "timeout retransmit (rto: " + m_rx_rto - + ")(rto_base: " + m_rto_base + ") (now: " + now + ") (dup_acks: " - + m_dup_acks + ")"); - } - if (!transmit(m_slist.get(0), now)) - { - closedown(new IOException("Connection aborted")); - return; - } - - long nInFlight = m_snd_nxt - m_snd_una; - m_ssthresh = Math.max(nInFlight / 2, 2 * m_mss); - //Logger.Log(LS_INFO) - // << "m_ssthresh: " << m_ssthresh << " nInFlight: " << nInFlight << " m_mss: " << m_mss; - m_cwnd = m_mss; - - // Back off retransmit timer. Note: the limit is lower when connecting. - long rto_limit = (m_state.ordinal() < PseudoTcpState.TCP_ESTABLISHED.ordinal()) - ? DEF_RTO : MAX_RTO; - m_rx_rto = Math.min(rto_limit, m_rx_rto * 2); - m_rto_base = now; - } - - // Check if it's time to probe closed windows - if ((getM_snd_wnd() == 0) && (timeDiff(m_lastsend + m_rx_rto, now) <= 0)) - { - if (timeDiff(now, m_lastrecv) >= 15000) - { - closedown(new IOException("Connection aborted")); - return; - } - // probe the window - packet(m_snd_nxt - 1, (short) 0, 0, 0); - m_lastsend = now; - - // back off retransmit timer - m_rx_rto = Math.min(MAX_RTO, m_rx_rto * 2); - } - - // Check if it's time to send delayed acks - long timeDiff = timeDiff(m_t_ack + m_ack_delay, now); - if (m_t_ack > 0 && (timeDiff <= 0)) - { - packet(m_snd_nxt, (short) 0, 0, 0); - } - - if (PSEUDO_KEEPALIVE) // Check for idle timeout - { - if ((m_state == PseudoTcpState.TCP_ESTABLISHED) - && (timeDiff(m_lastrecv + IDLE_TIMEOUT, now) <= 0)) - { - closedown(new IOException("Connection aborted")); - return; - } - - // Check for ping timeout (to keep udp mapping open) - if ((m_state == PseudoTcpState.TCP_ESTABLISHED) - && (timeDiff(m_lasttraffic + (m_bOutgoing ? IDLE_PING * 3 / 2 : IDLE_PING), now) <= 0)) - { - packet(m_snd_nxt, (short) 0, 0, 0); - } - - } - } - - /** - * Use this method to notify protocol about packets received from the - * network - * - * @param buffer packet's data - * @param len data length - * @return true if packet was successfully parsed - */ - synchronized public boolean notifyPacket(byte[] buffer, int len) - { - if (len > MAX_PACKET) - { - logger.log(Level.WARNING, debugName + " packet too large"); - return false; - } - return parse(buffer, len); - } - - /** - * Retrieve option's value. See {@link Option} for available options - * - * @param opt option which value will be retrieved - * @return option's value - */ - long getOption(Option opt) - { - if (opt == Option.OPT_NODELAY) - { - return m_use_nagling ? 0 : 1; - } - else - { - if (opt == Option.OPT_ACKDELAY) - { - return m_ack_delay; - } - else - { - if (opt == Option.OPT_SNDBUF) - { - return m_sbuf_len; - } - else - { - assert opt == Option.OPT_RCVBUF; - return m_rbuf_len; - } - } - } - } - - /** - * Sets {@link Option} value - * - * @param opt option whose value will be set - * @param value the value to be set - */ - void setOption(Option opt, long value) - { - if (opt == Option.OPT_NODELAY) - { - m_use_nagling = value == 0; - } - else - { - if (opt == Option.OPT_ACKDELAY) - { - m_ack_delay = value; - } - else - { - if (opt == Option.OPT_SNDBUF) - { - assert m_state == PseudoTcpState.TCP_LISTEN; - resizeSendBuffer((int)value); - } - else - { - assert opt == Option.OPT_RCVBUF; - assert m_state == PseudoTcpState.TCP_LISTEN; - resizeReceiveBuffer((int)value); - } - } - } - } - - /** - * - * @return congestion window size - */ - long getCongestionWindow() - { - return m_cwnd; - } - - /** - * - * @return bytes in flight - */ - long getBytesInFlight() - { - return m_snd_nxt - m_snd_una; - } - - /** - * - * @return bytes buffered, but not sent yet - */ - long getBytesBufferedNotSent() - { - long buffered_bytes = m_sbuf.getBuffered(); - return m_snd_una + buffered_bytes - m_snd_nxt; - } - - /** - * - * @return bytes available in receive buffer - */ - int getAvailable() - { - return m_rbuf.getBuffered(); - } - - /** - * - * @return space available in the send buffer - */ - int getAvailableSendBuffer() - { - return m_sbuf.getWriteRemaining(); - } - - /** - * - * @return round trip time estimate in ms - */ - long getRoundTripTimeEstimateMs() - { - return m_rx_srtt; - } - - /** - * Reads the data available in receive buffer. This method returns 0 if - * there's no data available at the moment. - * - * @param buffer destination buffer - * @param offset destination buffer's offset - * @param len bytes to be read - * @return byte count actually read - * @throws IOException if the protocol is not in the connected state - */ - public synchronized int recv(byte[] buffer, int offset, int len) throws IOException - { - if (m_state != PseudoTcpState.TCP_ESTABLISHED) - { - throw new IOException("Socket not connected"); - } - - int read = m_rbuf.read(buffer, offset, len); - - // If there's no data in |m_rbuf|. - if (read == 0) - { - m_bReadEnable = true; - return 0; - } - assert read != -1; - - int available_space = m_rbuf.getWriteRemaining(); - if (available_space - m_rcv_wnd >= Math.min(m_rbuf_len / 8, m_mss)) - { - boolean bWasClosed = (m_rcv_wnd == 0); // !?! Not sure about this was closed business - m_rcv_wnd = available_space; - - if (bWasClosed) - { - attemptSend(SendFlags.sfImmediateAck); - } - } - return read; - } - - /** - * Reads the data available in receive buffer. This method returns 0 if - * there's no data available at the moment. - * - * @param buffer destination buffer - * @param len bytes to be read - * @return received byte count - * @throws IOException if the protocol is not in the connected state - */ - public int recv(byte[] buffer, int len) throws IOException - { - return recv(buffer, 0, len); - } - - /** - * Enqueues data in the send buffer - * - * @param buffer source data buffer - * @param len bytes count to be sent - * @return sent byte count - * @throws IOException if the protocol is not in connected state - */ - public int send(byte[] buffer, int len) throws IOException - { - return send(buffer, 0, len); - } - - /** - * Enqueues data in the send buffer - * - * @param buffer source data buffer - * @param offset offset of the source data buffer - * @param len bytes count to be sent - * @return bytes count written to the send buffer - * @throws IOException if the protocol is not in connected state - */ - public synchronized int send(byte[] buffer, int offset, int len) - throws IOException - { - if (m_state != PseudoTcpState.TCP_ESTABLISHED) - { - throw new IOException("Socket not connected"); - } - - long available_space; - available_space = m_sbuf.getWriteRemaining(); - - if (available_space == 0) - { - m_bWriteEnable = true; - return 0; - } - - int written = queue(buffer, offset, len, false); - attemptSend(SendFlags.sfNone); - return written; - } - - /** - * Shuts down the protocol which enters closed state - * - * @param force if true all data received from this moment will be discarded - */ - void close(boolean force) - { - logger.log(Level.FINE, debugName + " close (" + force + ")"); - m_shutdown = force ? EnShutdown.SD_FORCEFUL : EnShutdown.SD_GRACEFUL; - if (force) - { - m_state = PseudoTcpState.TCP_CLOSED; - } - } - -// -// Internal Implementation -// - /** - * Enqueues data segment in the send buffer - * - * @param buffer source buffer - * @param offset source buffer's offset - * @param len data length - * @param bCtrl true for control data - * @return written byte count - */ - int queue(byte[] buffer, int offset, int len, boolean bCtrl) - { - int available_space; - available_space = m_sbuf.getWriteRemaining(); - if (len > available_space) - { - assert !bCtrl; - len = available_space; - } - - // We can concatenate data if the last segment is the same type - // (control v. regular data), and has not been transmitted yet - SSegment back = null; - if (!m_slist.isEmpty()) - { - back = m_slist.get(m_slist.size() - 1); - } - if (back != null && (back.bCtrl == bCtrl) && (back.xmit == 0)) - { - back.len += len; - } - else - { - long snd_buffered; - snd_buffered = m_sbuf.getBuffered(); - SSegment sseg = new SSegment( - m_snd_una + snd_buffered, - len, - bCtrl); - //m_slist.push_back(sseg); - if (logger.isLoggable(Level.FINEST)) - { - logger.log(Level.FINEST, debugName + " enqueued send segment seq: " - + sseg.seq + " len: " + sseg.len); - } - m_slist.add(sseg); - } - - int written = m_sbuf.write(buffer, offset, len); - return written; - } - - /** - * Creates a packet starting at offset in the send buffer of - * specified length and sends it with help of PseudoTcpNotify. - * - * @param seq used sequence number - * @param flags - * @param offset in the send buffer - * @param len length of data from - * @return WriteResult returned by PseudoTcpNotify - * - * @see PseudoTcpNotify - * @see WriteResult - */ - WriteResult packet(long seq, short flags, long offset, long len) - { - assert HEADER_SIZE + len <= MAX_PACKET; - - long now = now(); - - byte[] buffer = new byte[HEADER_SIZE + (int)len]; - long_to_bytes(m_conv, buffer, 0); - long_to_bytes(seq, buffer, 4); - long_to_bytes(m_rcv_nxt, buffer, 8); - buffer[12] = 0; - buffer[13] = (byte) (flags & 0xFF); - short_to_bytes(m_rcv_wnd >> m_rwnd_scale, buffer, 14); - - // Timestamp computations - long_to_bytes(now, buffer, 16); - long_to_bytes(m_ts_recent, buffer, 20); - m_ts_lastack = m_rcv_nxt; - - if (len > 0) - { - int bytes_read = m_sbuf.readOffset(buffer, HEADER_SIZE, - (int) len, - (int) offset); - assert bytes_read == len; - } - if (logger.isLoggable(Level.FINE)) - { - logger.log(Level.FINE, "<-- " + debugName + " " - + ""); - } - WriteResult wres = m_notify.tcpWritePacket(this, - buffer, - (int) len + HEADER_SIZE); - /** - * Note: When len is 0, this is an ACK packet. We don't read the return - * value for those, and thus we won't retry. So go ahead and treat the - * packet as a success (basically simulate as if it were dropped), which - * will prevent our timers from being messed up. - */ - if ((wres != WriteResult.WR_SUCCESS) && (0 != len)) - { - return wres; - } - m_t_ack = 0; - if (len > 0) - { - m_lastsend = now; - } - m_lasttraffic = now; - m_bOutgoing = true; - - return WriteResult.WR_SUCCESS; - } - - /** - * Method can be used in debugging utilities to parse PTCP segment - * @param buffer data to parse - * @param size length of the data in the buffer - * @return the parsed segment - */ - public static Segment parseSeg(byte[] buffer, int size) - { - if (size < 12) - { - return null; - } - - Segment seg = new Segment(); - seg.conv = bytes_to_long(buffer, 0); - seg.seq = bytes_to_long(buffer, 4); - seg.ack = bytes_to_long(buffer, 8); - seg.flags = buffer[13]; - seg.wnd = bytes_to_short(buffer, 14); - - seg.tsval = bytes_to_long(buffer, 16); - seg.tsecr = bytes_to_long(buffer, 20); - - seg.data = copy_buffer(buffer, HEADER_SIZE, size - HEADER_SIZE); - seg.len = size - HEADER_SIZE; - - return seg; - } - - /** - * Can be used to convert segments to text - * - * @param seg the {@link Segment} to format - * @return segment in readable text form - */ - public static String segToStr(Segment seg) - { - String data="data: "; - for (byte b : seg.data) - { - data += b; - } - return " "+data; - } - - /** - * Creates new segment from the data in buffer which is processed - * by the protocol. - * - * @param buffer source buffer - * @param size data length - * @return true if successfully parsed the data - */ - boolean parse(byte[] buffer, int size) - { - if (size < 12) - { - return false; - } - - Segment seg = parseSeg(buffer, size); - - if (logger.isLoggable(Level.FINE)) - { - logger.log(Level.FINE, - "--> " + debugName + ""); - } - return process(seg); - } - - /** - * Calculates timeout in ms for current operation - * - * @param now current timestamp in ms - * @return next timeout or -1 in case of an error - */ - long clock_check(long now) - { - if (m_shutdown == EnShutdown.SD_FORCEFUL) - { - return -1; - } - - long nTimeout; - long snd_buffered; - snd_buffered = m_sbuf.getBuffered(); - if ((m_shutdown == EnShutdown.SD_GRACEFUL) - && ((m_state != PseudoTcpState.TCP_ESTABLISHED) - || ((snd_buffered == 0) && (m_t_ack == 0)))) - { - return -1; - } - - if (m_state == PseudoTcpState.TCP_CLOSED) - { - return CLOSED_TIMEOUT; - } - - nTimeout = DEFAULT_TIMEOUT; - - if (m_t_ack > 0) - { - nTimeout = Math.min(nTimeout, timeDiff(m_t_ack + m_ack_delay, now)); - } - if (m_rto_base > 0) - { - nTimeout = Math.min(nTimeout, timeDiff(m_rto_base + m_rx_rto, now)); - } - if (getM_snd_wnd() == 0) - { - nTimeout = Math.min(nTimeout, timeDiff(m_lastsend + m_rx_rto, now)); - } - if (PSEUDO_KEEPALIVE) - { - if (m_state == PseudoTcpState.TCP_ESTABLISHED) - { - nTimeout = Math.min( - nTimeout, - timeDiff(m_lasttraffic + (m_bOutgoing ? IDLE_PING * 3 / 2 : IDLE_PING), now)); - } - } - //nTimeout is used on wait methods, so cannot be equal to 0 - return nTimeout <= 0 ? 1 : nTimeout; - } - - /** - * Process given segment - * - * @param seg - * @return false in case of error - */ - boolean process(Segment seg) - { - // If this is the wrong conversation, send a reset!?! (with the correct conversation?) - if (seg.conv != m_conv) - { - //if ((seg.flags & FLAG_RST) == 0) { - // packet(tcb, seg.ack, 0, FLAG_RST, 0, 0); - //} - //closedown(new IOException( - // debugName + " wrong conversation number, this: " + m_conv - // + " remote: " + seg.conv)); - logger.info(debugName + " wrong conversation number, this: " + m_conv - + " remote: " + seg.conv); - return false; - } - - long now = now(); - m_lasttraffic = m_lastrecv = now; - m_bOutgoing = false; - - if (m_state == PseudoTcpState.TCP_CLOSED) - { - // !?! send reset? - closedown(new IOException(debugName + " in closed state")); - return false; - } - - // Check if this is a reset segment - if ((seg.flags & FLAG_RST) > 0) - { - //closedown(PseudoTcpError.ECONNRESET); - closedown(new IOException("Connection reset")); - return false; - } - - // Check for control data - boolean bConnect = false; - if ((seg.flags & FLAG_CTL) > 0) - { - if (seg.len == 0) - { - logger.log(Level.SEVERE, debugName + " Missing control code"); - return false; - } - else - { - if (seg.data[0] == CTL_CONNECT) - { - bConnect = true; - - // TCP options are in the remainder of the payload after CTL_CONNECT. - if (!parseOptions(seg.data, 1, seg.len - 1)) - { - return false; - } - - if (m_state == PseudoTcpState.TCP_LISTEN) - { - m_state = PseudoTcpState.TCP_SYN_RECEIVED; - logger.log(Level.FINE, - debugName + " State: TCP_SYN_RECEIVED"); - //m_notify->associate(addr); - queueConnectMessage(); - } - else - { - if (m_state == PseudoTcpState.TCP_SYN_SENT) - { - m_state = PseudoTcpState.TCP_ESTABLISHED; - logger.log(Level.FINE, - debugName + " State: TCP_ESTABLISHED"); - adjustMTU(); - if (m_notify != null) - { - m_notify.onTcpOpen(this); - } - //notify(evOpen); - } - } - } - else - { - logger.log(Level.SEVERE, - debugName + " Unknown control code: " + seg.data[0]); - return false; - } - } - } - - // Update timestamp - if ((seg.seq <= m_ts_lastack) && (m_ts_lastack < seg.seq + seg.len)) - { - m_ts_recent = seg.tsval; - } - - // Check if this is a valuable ack - if ((seg.ack > m_snd_una) && (seg.ack <= m_snd_nxt)) - { - // Calculate round-trip time - if (seg.tsecr > 0) - { - long rtt = timeDiff(now, seg.tsecr); - assert rtt >= 0; - if (m_rx_srtt == 0) - { - m_rx_srtt = rtt; - m_rx_rttvar = rtt / 2; - } - else - { - m_rx_rttvar = (3 * m_rx_rttvar + Math.abs(rtt - m_rx_srtt)) / 4; - m_rx_srtt = (7 * m_rx_srtt + rtt) / 8; - } - m_rx_rto = bound(MIN_RTO, m_rx_srtt - + Math.max(1, 4 * m_rx_rttvar), - MAX_RTO); - if (logger.isLoggable(Level.FINER)) - { - logger.log(Level.FINER, - "rtt: " + rtt + " srtt: " + m_rx_srtt + " rto: " + m_rx_rto); - } - } - - m_snd_wnd = seg.wnd << m_swnd_scale; - //setWindowWithScale(seg.wnd, getM_swnd_scale()); - //setM_snd_wnd(seg.wnd << m_swnd_scale); - - long nAcked = seg.ack - m_snd_una; - synchronized (ack_notify) - { - - m_snd_una = seg.ack; - - m_rto_base = (m_snd_una == m_snd_nxt) ? 0 : now; - - m_sbuf.consumeReadData((int) nAcked); - - if (logger.isLoggable(Level.FINER)) - { - logger.log(Level.FINER, - debugName + " acked: " + nAcked - + " m_snd_una: " + m_snd_una); - } - ack_notify.notifyAll(); - } - - for (long nFree = nAcked; nFree > 0;) - { - assert !m_slist.isEmpty(); - if (nFree < m_slist.get(0).len) - { - m_slist.get(0).len -= nFree; - m_slist.get(0).seq += nFree; - nFree = 0; - } - else - { - if (m_slist.get(0).len > m_largest) - { - m_largest = m_slist.get(0).len; - } - nFree -= m_slist.get(0).len; - m_slist.remove(0); - //m_slist.pop_front(); - } - } - - if (m_dup_acks >= 3) - { - if (m_snd_una >= m_recover) - { // NewReno - long nInFlight = m_snd_nxt - m_snd_una; - m_cwnd = Math.min(m_ssthresh, nInFlight + m_mss); // (Fast Retransmit) - logger.log(Level.FINE, "exit recovery"); - m_dup_acks = 0; - } - else - { - logger.log(Level.FINE, "recovery retransmit"); - if (!transmit(m_slist.get(0), now)) - { - //closedown(PseudoTcpError.ECONNABORTED); - closedown(new IOException("Connection aborted")); - return false; - } - m_cwnd += m_mss - Math.min(nAcked, m_cwnd); - } - } - else - { - m_dup_acks = 0; - // Slow start, congestion avoidance - if (m_cwnd < m_ssthresh) - { - m_cwnd += m_mss; - } - else - { - m_cwnd += Math.max(1, m_mss * m_mss / m_cwnd); - } - } - } - else - { - if (seg.ack == m_snd_una) - { - // !?! Note, tcp says don't do this... but otherwise how does a closed window become open? - //setWindowWithScale(seg.wnd, getM_swnd_scale()); - m_snd_wnd = seg.wnd << m_swnd_scale; - //setM_snd_wnd(seg.wnd << m_swnd_scale); - - // Check duplicate acks - if (seg.len > 0) - { - // it's a dup ack, but with a data payload, so don't modify m_dup_acks - } - else - { - if (m_snd_una != m_snd_nxt) - { - m_dup_acks += 1; - if (m_dup_acks == 3) - { // (Fast Retransmit) - if (logger.isLoggable(Level.FINE)) - { - logger.log(Level.FINE, - debugName + " enter recovery"); - logger.log(Level.FINE, - debugName + " recovery retransmit"); - } - if (!transmit(m_slist.get(0), now)) - { - closedown(new IOException("Connection aborted")); - //closedown(PseudoTcpError.ECONNABORTED); - return false; - } - m_recover = m_snd_nxt; - long nInFlight = m_snd_nxt - m_snd_una; - m_ssthresh = Math.max(nInFlight / 2, 2 * m_mss); - //Logger.Log(LS_INFO) - // << "m_ssthresh: " << m_ssthresh << " nInFlight: " << nInFlight << " m_mss: " << m_mss; - m_cwnd = m_ssthresh + 3 * m_mss; - } - else - { - if (m_dup_acks > 3) - { - m_cwnd += m_mss; - } - } - } - else - { - m_dup_acks = 0; - } - } - } - } - // !?! A bit hacky - if ((m_state == PseudoTcpState.TCP_SYN_RECEIVED) && !bConnect) - { - m_state = PseudoTcpState.TCP_ESTABLISHED; - logger.log(Level.FINE, debugName + " State: TCP_ESTABLISHED"); - adjustMTU(); - if (m_notify != null) - { - m_notify.onTcpOpen(this); - } - //notify(evOpen); - } - // If we make room in the send queue, notify the user - // The goal it to make sure we always have at least enough data to fill the - // window. We'd like to notify the app when we are halfway to that point. - long kIdealRefillSize = (m_sbuf_len + m_rbuf_len) / 2; - long snd_buffered = m_sbuf.getBuffered(); - if (m_bWriteEnable && snd_buffered < kIdealRefillSize) - { - m_bWriteEnable = false; - if (m_notify != null) - { - m_notify.onTcpWriteable(this); - } - //notify(evWrite); - } - // Conditions were acks must be sent: - // 1) Segment is too old (they missed an ACK) (immediately) - // 2) Segment is too new (we missed a segment) (immediately) - // 3) Segment has data (so we need to ACK!) (delayed) - // ... so the only time we don't need to ACK, is an empty segment that points to rcv_nxt! - SendFlags sflags = SendFlags.sfNone; - if (seg.seq != m_rcv_nxt) - { - sflags = SendFlags.sfImmediateAck; // (Fast Recovery) - } - else - { - if (seg.len - != 0) - { - if (m_ack_delay == 0) - { - sflags = SendFlags.sfImmediateAck; - } - else - { - sflags = SendFlags.sfDelayedAck; - } - } - } - - if (sflags == SendFlags.sfImmediateAck) - { - if (seg.seq > m_rcv_nxt) - { - logger.log(Level.FINER, - "too new, seq.seq=" + seg.seq - + ", seg.len=" + seg.len - + ", m_rcv_nxt=" + m_rcv_nxt); - } - else - { - if (seg.seq + seg.len <= m_rcv_nxt) - { - logger.log(Level.FINER, - "too old, seq.seq=" + seg.seq - + ", seg.len=" + seg.len - + ", m_rcv_nxt=" + m_rcv_nxt); - } - } - } - - // Adjust the incoming segment to fit our receive buffer - if (seg.seq < m_rcv_nxt) - { - long nAdjust = m_rcv_nxt - seg.seq; - if (nAdjust < seg.len) - { - seg.seq += nAdjust; - seg.data = scrollBuffer(seg.data, (int)nAdjust); - seg.len -= nAdjust; - } - else - { - seg.len = 0; - } - } - long available_space = m_rbuf.getWriteRemaining(); - if ((seg.seq + seg.len - m_rcv_nxt) > available_space) - { - long nAdjust = seg.seq + seg.len - m_rcv_nxt - available_space; - if (nAdjust < seg.len) - { - seg.len -= nAdjust; - } - else - { - seg.len = 0; - } - } - boolean bIgnoreData = ((seg.flags & FLAG_CTL) > 0) || (m_shutdown != EnShutdown.SD_NONE); - boolean bNewData = false; - if (seg.len > 0) - { - if (bIgnoreData) - { - if (seg.seq == m_rcv_nxt) - { - m_rcv_nxt += seg.len; - } - } - else - { - long nOffset = seg.seq - m_rcv_nxt; - - int result = m_rbuf.writeOffset(seg.data, seg.len, - (int) nOffset); - assert result == seg.len; - - if (seg.seq == m_rcv_nxt) - { - if (logger.isLoggable(Level.FINEST)) - { - logger.log(Level.FINEST, - "Avail space: " + available_space - + " seg.len: " + seg.len); - } - m_rbuf.consumeWriteBuffer(seg.len); - m_rcv_nxt += seg.len; - m_rcv_wnd -= seg.len; - bNewData = true; - - - Iterator iter = m_rlist.iterator(); - List toBeRemoved = new ArrayList<>(); - while (iter.hasNext()) - { - RSegment it = iter.next(); - if (it.seq > m_rcv_nxt) - { - break; - } - if (it.seq + it.len > m_rcv_nxt) - { - sflags = SendFlags.sfImmediateAck; // (Fast Recovery) - long nAdjust = (it.seq + it.len) - m_rcv_nxt; - if (logger.isLoggable(Level.FINE)) - { - logger.log(Level.FINE, - "Recovered " + nAdjust + " bytes (" - + m_rcv_nxt + " -> " + (m_rcv_nxt + nAdjust) - + ")"); - } - m_rbuf.consumeWriteBuffer((int) nAdjust); - m_rcv_nxt += nAdjust; - m_rcv_wnd -= nAdjust; - } - toBeRemoved.add(it); - } - m_rlist.removeAll(toBeRemoved); - } - else - { - if (logger.isLoggable(Level.FINE)) - { - logger.log(Level.FINE, - "Saving " + seg.len + " bytes (" + seg.seq - + " -> " + (seg.seq + seg.len) + ")"); - } - RSegment rseg = new RSegment(seg.seq, seg.len); - int insertPos; - for (insertPos = 0; insertPos < m_rlist.size(); insertPos++) - { - RSegment it = m_rlist.get(insertPos); - if (it.seq >= rseg.seq) - { - break; - } - } - m_rlist.add(insertPos, rseg); - } - } - } - - attemptSend(sflags); - // If we have new data, notify the user - if (bNewData && m_bReadEnable) - { - m_bReadEnable = false; - if (m_notify != null) - { - m_notify.onTcpReadable(this); - } - //notify(evRead); - } - return true; - } - - /** - * Util time method - * - * @param later timestamp in ms - * @param earlier timestamp in ms - * @return difference between later and earlier - */ - private static long timeDiff(long later, long earlier) - { - return later - earlier; - } - - /** - * Stores 32 bit unsigned int in a buffer at specified offset - * - * @param anUnsignedInt - * @param buf destination buffer - * @param offset destination buffer's offset - */ - private static void long_to_bytes(long anUnsignedInt, byte[] buf, int offset) - { - buf[offset] = (byte) ((anUnsignedInt & 0xFF000000L) >>> 24); - buf[offset + 1] = (byte) ((anUnsignedInt & 0x00FF0000L) >>> 16); - buf[offset + 2] = (byte) ((anUnsignedInt & 0x0000FF00L) >>> 8); - buf[offset + 3] = (byte) ((anUnsignedInt & 0x000000FFL)); - //java.nio.ByteBuffer.wrap(buffer, offset, 4).putInt((int) (m_conv & 0xFFFFFFFFL)); - } - - /** - * Stores 16 bit unsigned int in the buffer at specified offset - * - * @param anUnsignedShort - * @param buf destination buffer - * @param offset destination buffer's offset - */ - private static void short_to_bytes(int anUnsignedShort, byte[] buf, int offset) - { - buf[offset] = (byte) ((anUnsignedShort & 0xFF00) >>> 8); - buf[offset + 1] = (byte) ((anUnsignedShort & 0x00FF)); - //java.nio.ByteBuffer.wrap(buffer, offset, 2).putShort((short) (shrt & 0xFFFF)); - } - - /** - * Reads 32 bit unsigned int from the buffer at specified offset - * - * @param buffer - * @param offset - * @return 32 bit unsigned value - */ - private static long bytes_to_long(byte[] buffer, int offset) - { - int fByte = (0x000000FF & ((int) buffer[offset])); - int sByte = (0x000000FF & ((int) buffer[offset + 1])); - int tByte = (0x000000FF & ((int) buffer[offset + 2])); - int foByte = (0x000000FF & ((int) buffer[offset + 3])); - return ((long) (fByte << 24 - | sByte << 16 - | tByte << 8 - | foByte)) - & 0xFFFFFFFFL; - } - - /** - * Reads 16 bit unsigned int from the buffer at specified offset - * - * @param buffer - * @param offset - * @return 16 bit unsigned int - */ - private static int bytes_to_short(byte[] buffer, int offset) - { - int fByte = (0x000000FF & ((int) buffer[offset])); - int sByte = (0x000000FF & ((int) buffer[offset + 1])); - return ((fByte << 8 - | sByte)) - & 0xFFFF; - } - - /** - * Wrapped system function arrayCopy - * - * @param buffer source buffer - * @param sOffset source buffer offset - * @param len bytes count to be copied - * @return new buffer size of len - */ - private static byte[] copy_buffer(byte[] buffer, int sOffset, int len) - { - byte[] newData = new byte[len]; - System.arraycopy(buffer, sOffset, newData, 0, len); - return newData; - } - - /** - * - * @param lower - * @param middle - * @param upper - * @return - */ - private long bound(long lower, long middle, long upper) - { - return Math.min(Math.max(lower, middle), upper); - } - - private byte[] scrollBuffer(byte[] data, int nAdjust) - { - byte[] newBuffer = new byte[data.length - nAdjust]; - System.arraycopy(data, nAdjust, newBuffer, 0, newBuffer.length); - return newBuffer; - } - - /** - * Transmits given segment - * - * @param seg segment to be sent - * @param now current timestamp - * @return false in case of error - */ - boolean transmit(SSegment seg, long now) - { - // Logger.Log(LS_INFO) << "seg->xmit: "<< seg->xmit; - if (seg.xmit >= ((m_state == PseudoTcpState.TCP_ESTABLISHED) ? 15 : 30)) - { - logger.log(Level.FINE, "too many retransmits"); - return false; - } - - long nTransmit = Math.min(seg.len, m_mss); - - while (true) - { - long seq = seg.seq; - short flags = (seg.bCtrl ? FLAG_CTL : 0); - WriteResult wres = packet(seq, - flags, - seg.seq - m_snd_una, - nTransmit); - - if (wres == WriteResult.WR_SUCCESS) - { - break; - } - - if (wres == WriteResult.WR_FAIL) - { - logger.log(Level.WARNING, "packet failed"); - return false; - } - - assert wres == WriteResult.WR_TOO_LARGE; - - while (true) - { - if (PACKET_MAXIMUMS[(m_msslevel + 1)] == 0) - { - logger.log(Level.INFO, "MTU too small"); - return false; - } - // !?! We need to break up all outstanding and pending packets and then retransmit!?! - - m_mss = PACKET_MAXIMUMS[++m_msslevel] - PACKET_OVERHEAD; - m_cwnd = 2 * m_mss; // I added this... haven't researched actual formula - if (m_mss < nTransmit) - { - nTransmit = m_mss; - break; - } - } - if (logger.isLoggable(Level.INFO)) - { - logger.log(Level.INFO, "Adjusting mss to " + m_mss + " bytes"); - } - } - - if (nTransmit < seg.len) - { - if (logger.isLoggable(Level.INFO)) - { - logger.log(Level.INFO, "mss reduced to " + m_mss); - } - SSegment subseg = new SSegment(seg.seq + nTransmit, - seg.len - nTransmit, seg.bCtrl); - //subseg.tstamp = seg->tstamp; - subseg.xmit = seg.xmit; - seg.len = nTransmit; - - //SList::iterator next = seg; - m_slist.add(m_slist.indexOf(seg) + 1, subseg); - } - - if (seg.xmit == 0) - { - m_snd_nxt += seg.len; - } - seg.xmit += 1; - //seg->tstamp = now; - if (m_rto_base == 0) - { - m_rto_base = now; - } - - return true; - } - - /** - * This method checks if it's time to send a packet(ack or retransmit - * anything) - * - * @param sflags - */ - void attemptSend(SendFlags sflags) - { - long now = now(); - - if (timeDiff(now, m_lastsend) > m_rx_rto) - { - m_cwnd = m_mss; - } - boolean bFirst = true; - - while (true) - { - long cwnd = m_cwnd; - if ((m_dup_acks == 1) || (m_dup_acks == 2)) - { // Limited Transmit - cwnd += m_dup_acks * m_mss; - } - long nWindow = Math.min(getM_snd_wnd(), cwnd); - long nInFlight = m_snd_nxt - m_snd_una; - long nUseable = (nInFlight < nWindow) ? (nWindow - nInFlight) : 0; - - long snd_buffered = m_sbuf.getBuffered(); - /* - * System.out.println("is available? buffered: " + snd_buffered + " - * inFlight: " + nInFlight + " m_mss: " + m_mss + " m_snd_wnd: " + - * getM_snd_wnd() + " cwnd: " + cwnd + " nWindow: " + nWindow + " - * nUseable: " + nUseable); - */ - long nAvailable = Math.min(snd_buffered - nInFlight, m_mss); - - if (nAvailable > nUseable) - { - if (nUseable * 4 < nWindow) - { - // RFC 813 - avoid SWS - logger.log(Level.FINER, - "RFC 813 - avoid SWS(nAvailable = 0)"); - nAvailable = 0; - } - else - { - nAvailable = nUseable; - } - } - - if (bFirst) - { - long available_space = m_sbuf.getWriteRemaining(); - - bFirst = false; - if (logger.isLoggable(Level.FINE)) - { - logger.log(Level.FINE, - "[cwnd: " + m_cwnd + " nWindow: " + nWindow - + " nInFlight: " + nInFlight + " nAvailable: " + nAvailable - + " nQueued: " + snd_buffered + " nEmpty: " + available_space - + " ssthresh: " + m_ssthresh + "]"); - } - } - - if (nAvailable == 0) - { - if (sflags == SendFlags.sfNone) - { - logger.log(Level.FINEST, "nAvailable == 0: quit"); - return; - } - - // If this is an immediate ack, or the second delayed ack - if ((sflags == SendFlags.sfImmediateAck) || (m_t_ack > 0)) - { - packet(m_snd_nxt, (short) 0, 0, 0); - logger.log(Level.FINER, "Immediate ack: "); - } - else - { - m_t_ack = now(); - if (logger.isLoggable(Level.FINER)) - { - logger.log( - Level.FINER, "Delayed ack, m_t_ack: " + m_t_ack); - } - } - return; - } - - // Nagle's algorithm. - // If there is data already in-flight, and we haven't a full segment of - // data ready to send then hold off until we get more to send, or the - // in-flight data is acknowledged. - if (m_use_nagling && (m_snd_nxt > m_snd_una) && (nAvailable < m_mss)) - { - logger.log(Level.FINER, "wait until more data is acked"); - return; - } - - // Find the next segment to transmit - SSegment seg = null; - Iterator iter = m_slist.iterator(); - do - { - SSegment it = iter.next(); - if (it.xmit == 0) - { - seg = it; - break; - } - } - while (iter.hasNext()); - - assert seg != null; - - // If the segment is too large, break it into two - if (seg.len > nAvailable) - { - logger.log(Level.FINEST, "Break a segment into 2"); - SSegment subseg = new SSegment( - seg.seq + nAvailable, - seg.len - nAvailable, - seg.bCtrl); - seg.len = nAvailable; - //m_slist.insert(++it, subseg); - m_slist.add(m_slist.indexOf(seg) + 1, subseg); - } - if (logger.isLoggable(Level.FINEST)) - { - logger.log(Level.FINEST, - "TRANSMIT SEGMENT seq: " + seg.seq - + " len: " + seg.len); - } - if (!transmit(seg, now)) - { - logger.log(Level.SEVERE, "transmit failed"); - // TODO: consider closing socket - return; - } - - sflags = SendFlags.sfNone; - } - } - - /** - * This metod is called in case of en error. Tcp enters closed state and - * notifies listener about it. - * - * @param e exception to be propagated - */ - void closedown(IOException e) - { - logger.log(Level.FINE, debugName + " State: TCP_CLOSED "); - m_state = PseudoTcpState.TCP_CLOSED; - if (m_notify != null) - { - m_notify.onTcpClosed(this, e); - } - } - - /** - * Adjusts MTU - */ - void adjustMTU() - { - // Determine our current mss level, so that we can adjust appropriately later - for (m_msslevel = 0; PACKET_MAXIMUMS[(m_msslevel + 1)] > 0; ++m_msslevel) - { - if (PACKET_MAXIMUMS[m_msslevel] <= m_mtu_advise) - { - break; - } - } - m_mss = m_mtu_advise - PACKET_OVERHEAD; - // !?! Should we reset m_largest here? - logger.log(Level.FINE, "Adjusting mss to " + m_mss + " bytes"); - - // Enforce minimums on ssthresh and cwnd - m_ssthresh = Math.max(m_ssthresh, 2 * m_mss); - m_cwnd = Math.max(m_cwnd, m_mss); - } - - /** - * - * @return true if receive buffer is full - */ - boolean isReceiveBufferFull() - { - return m_rbuf.getWriteRemaining() == 0; - } - - /** - * Disables window scaling. Must be called before the connection is - * established. - */ - void disableWindowScale() - { - m_support_wnd_scale = false; - } - - /** - * Enqueues connect message - */ - void queueConnectMessage() - { - byte[] buff = null; - if (m_support_wnd_scale) - { - buff = new byte[4]; - buff[1] = TCP_OPT_WND_SCALE & 0xFF; - buff[2] = 1; - buff[3] = (byte) (m_rwnd_scale & 0xFF); - } - else - { - buff = new byte[1]; - } - buff[0] = CTL_CONNECT & 0xFF; - m_snd_wnd = buff.length; - queue(buff, 0, buff.length, true); - } - - /** - * Parse and process option in given buffer/offset/length - * - * @param data source buffer - * @param offset source offset - * @param len byte count - * @return true if options were properly parsed - */ - boolean parseOptions(byte[] data, int offset, int len) - { - List options_specified = new ArrayList<>(); - - // See http://www.freesoft.org/CIE/Course/Section4/8.htm for - // parsing the options list. - java.nio.ByteBuffer buf = java.nio.ByteBuffer.wrap(data, offset, len); - while (buf.hasRemaining()) - { - short kind = TCP_OPT_EOL; - short tmp = buf.get(); - if (tmp != -1) - { - kind = tmp; - } - if (kind == TCP_OPT_EOL) - { - // End of option list. - break; - } - else - { - if (kind == TCP_OPT_NOOP) - { - // No op. - continue; - } - } - - // Length of this option. - assert len != 0; - //UNUSED(len); - short opt_len = buf.get(); - - // Content of this option. - if (opt_len <= buf.remaining()) - { - byte[] opt_data = new byte[opt_len]; - buf.get(opt_data); - applyOption(kind, opt_data, opt_len); - } - else - { - logger.log(Level.SEVERE, - "Invalid option length received: "+opt_len - +" data len: "+buf.remaining()); - return false; - } - options_specified.add(kind); - } - - if (!options_specified.contains(TCP_OPT_WND_SCALE)) - { - logger.log(Level.WARNING, "Peer doesn't support window scaling"); - if (getM_rwnd_scale() > 0) - { - // Peer doesn't support TCP options and window scaling. - // Revert receive buffer size to default value. - resizeReceiveBuffer(DEFAULT_RCV_BUF_SIZE); - m_swnd_scale = 0; - } - } - return true; - } - - /** - * Applies kind of option and it's data - * - * @param kind option type - * @param data option's data buffer - * @param len data length - */ - void applyOption(short kind, byte[] data, long len) - { - if (kind == TCP_OPT_MSS) - { - logger.log( - Level.WARNING, - "Peer specified MSS option which is not supported."); - // TODO: Implement. - } - else - { - if (kind == TCP_OPT_WND_SCALE) - { - // Window scale factor. - // http://www.ietf.org/rfc/rfc1323.txt - if (len != 1) - { - logger.log(Level.SEVERE, "Invalid window scale option received."); - return; - } - applyWindowScaleOption(data[0]); - } - } - } - - /** - * Applies window scale option with given scale_factor - * - * @param scale_factor - */ - void applyWindowScaleOption(short scale_factor) - { - m_swnd_scale = scale_factor; - } - - /** - * Resizes send buffer to new_size - * - * @param new_size - */ - void resizeSendBuffer(int new_size) - { - m_sbuf_len = new_size; - m_sbuf.setCapacity(new_size); - } - - /** - * Resizes receive buffer to new_size - * - * @param new_size - */ - void resizeReceiveBuffer(int new_size) - { - short scale_factor = 0; - - // Determine the scale factor such that the scaled window size can fit - // in a 16-bit unsigned integer. - while (new_size > 0xFFFF) - { - ++scale_factor; - new_size >>= 1; - } - - // Determine the proper size of the buffer. - new_size <<= scale_factor; - boolean result = m_rbuf.setCapacity(new_size); - - // Make sure the new buffer is large enough to contain data in the old - // buffer. This should always be true because this method is called either - // before connection is established or when peers are exchanging connect - // messages. - assert result; - m_rbuf_len = new_size; - m_rwnd_scale = scale_factor; - m_ssthresh = new_size; - - int available_space = m_rbuf.getWriteRemaining(); - m_rcv_wnd = available_space; - } - - /** - * @return send window size - */ - int getM_snd_wnd() - { - return m_snd_wnd; - } - - /** - * - * @return current @link{PseudoTcpState} - */ - public PseudoTcpState getState() - { - return m_state; - } - - /** - * - * @return send buffer's length - */ - int getSendBufferSize() - { - return m_sbuf_len; - } - - /** - * - * @return receive buffer's length - */ - int getRecvBufferSize() - { - return m_rbuf_len; - } - - /** - * @return the receive window scale - */ - public short getM_rwnd_scale() - { - return m_rwnd_scale; - } - - /** - * @return the send window scale - */ - public short getM_swnd_scale() - { - return m_swnd_scale; - } - private final Object ack_notify = new Object(); - - public Object getAckNotify() - { - return ack_notify; - } - - long getConversationID() - { - return m_conv; - } - - void setConversationID(long convID) - { - if (m_state != PseudoTcpState.TCP_LISTEN) - throw new IllegalStateException(); - this.m_conv = convID; - } - - -} diff --git a/src/main/java/org/ice4j/pseudotcp/PseudoTcpJavaSocket.java b/src/main/java/org/ice4j/pseudotcp/PseudoTcpJavaSocket.java deleted file mode 100644 index 40b18ce0..00000000 --- a/src/main/java/org/ice4j/pseudotcp/PseudoTcpJavaSocket.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * ice4j, the OpenSource Java Solution for NAT and Firewall Traversal. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ice4j.pseudotcp; - -import java.net.*; - -/** - * - * @author Pawel Domas - */ -public class PseudoTcpJavaSocket extends Socket { - public PseudoTcpJavaSocket(long conv_id) throws SocketException { - super(new PseudoTcpSocketImpl(conv_id)); - } - - public PseudoTcpJavaSocket(long conv_id, DatagramSocket socket) - throws SocketException { - super(new PseudoTcpSocketImpl(conv_id, socket)); - } - -} diff --git a/src/main/java/org/ice4j/pseudotcp/PseudoTcpNotify.java b/src/main/java/org/ice4j/pseudotcp/PseudoTcpNotify.java deleted file mode 100755 index a4a87427..00000000 --- a/src/main/java/org/ice4j/pseudotcp/PseudoTcpNotify.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * ice4j, the OpenSource Java Solution for NAT and Firewall Traversal. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ice4j.pseudotcp; - -import java.io.*; - -/** - * Notification of tcp events. - * Is implemented by PseudoTcpSocketImpl to expose stream functionality. - * - * @author Pawel Domas - */ -public interface PseudoTcpNotify -{ - /** - * Called when TCP enters opened state - * @param tcp the socket that was opened - */ - void onTcpOpen(PseudoTCPBase tcp); - - /** - * Called when any data is available in read buffer - * @param tcp the socket that became readable - */ - void onTcpReadable(PseudoTCPBase tcp); - - /** - * Called when there is free space available in the send buffer - * @param tcp the socket that became writable - */ - void onTcpWriteable(PseudoTCPBase tcp); - - /** - * Called when tcp enters closed state - * @param tcp the socket that was closed - * @param e null means no error - */ - void onTcpClosed(PseudoTCPBase tcp, IOException e); - - /** - * Called when protocol requests packet transfer through the network. - * @param tcp the socket on which the write occurred - * @param buffer the data that was written - * @param len data length - * @return the result, see {@link WriteResult} description for more info - */ - WriteResult tcpWritePacket(PseudoTCPBase tcp, byte[] buffer, int len); -} diff --git a/src/main/java/org/ice4j/pseudotcp/PseudoTcpSocket.java b/src/main/java/org/ice4j/pseudotcp/PseudoTcpSocket.java deleted file mode 100644 index 387909e5..00000000 --- a/src/main/java/org/ice4j/pseudotcp/PseudoTcpSocket.java +++ /dev/null @@ -1,292 +0,0 @@ -/* - * ice4j, the OpenSource Java Solution for NAT and Firewall Traversal. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ice4j.pseudotcp; - -import java.io.*; -import java.net.*; - -/** - * - * @author Pawel Domas - */ -public class PseudoTcpSocket extends Socket -{ - private final PseudoTcpSocketImpl socketImpl; - - private final Object connectLock = new Object(); - - private final Object closeLock = new Object(); - - PseudoTcpSocket(PseudoTcpSocketImpl socketImpl) - throws SocketException - { - super(socketImpl); - this.socketImpl = socketImpl; - } - - /** - * - * @return PseudoTCP conversation ID - */ - public long getConversationID() - { - return socketImpl.getConversationID(); - } - - /** - * Set conversation ID for the socket - * Must be called on unconnected socket - * - * @param convID the conversation ID to set - * @throws IllegalStateException when called on connected or closed socket - */ - public void setConversationID(long convID) - throws IllegalStateException - { - socketImpl.setConversationID(convID); - } - - /** - * Sets MTU (maximum transmission unit) value - * @param mtu the MTU value - */ - public void setMTU(int mtu) - { - socketImpl.setMTU(mtu); - } - - /** - * Gets MTU (maximum transmission unit) value - * @return MTU value - */ - public int getMTU() - { - return socketImpl.getMTU(); - } - - /** - * Sets an {@link Option} on this socket. - * @return PseudoTCP option value - * - * @param option the option to set on this socket. - * @see Option - */ - public long getOption(Option option) - { - return socketImpl.getPTCPOption(option); - } - - /** - * - * @param option PseudoTCP option to set - * @param optValue option's value - * - * @see Option - */ - public void setOption(Option option, long optValue) - { - socketImpl.setPTCPOption(option, optValue); - } - - /** - * Blocking method waits for connection. - * - * @param timeout for this operation in ms - * @throws IOException If socket gets closed or timeout expires - */ - public void accept(int timeout) - throws IOException - { - socketImpl.accept(timeout); - } - - /** - * Sets debug name that will be displayed in log messages for this socket - * @param debugName the name of this socket for debug messages - */ - public void setDebugName(String debugName) - { - socketImpl.setDebugName(debugName); - } - - /** - * Returns current PseudoTcpState of this socket - * @return current PseudoTcpState - * - * @see PseudoTcpState - */ - public PseudoTcpState getState() - { - return socketImpl.getState(); - } - - @Override - public boolean isConnected() - { - return getState() == PseudoTcpState.TCP_ESTABLISHED; - } - - /** - * - * @return true if socket is connected or is trying to connect - */ - public boolean isConnecting() - { - PseudoTcpState currentState = getState(); - return currentState == PseudoTcpState.TCP_ESTABLISHED - || currentState == PseudoTcpState.TCP_SYN_RECEIVED - || currentState == PseudoTcpState.TCP_SYN_SENT; - } - - @Override - public boolean isClosed() - { - return getState() == PseudoTcpState.TCP_CLOSED; - } - - /** - * {@inheritDoc} - * - * Connects without the timeout. - */ - @Override - public void connect(SocketAddress endpoint) - throws IOException - { - this.connect(endpoint, 0); - } - - /** - * Checks destination port number. - * - * @param dstPort the destination port to check. - */ - private void checkDestination(int dstPort) - { - if (dstPort < 0 || dstPort > 65535) - { - throw new IllegalArgumentException("Port out of range: " + dstPort); - } - } - - /** - * {@inheritDoc} - * - * On Android, we must not use the default connect implementation, - * because that one deals directly with physical resources, while we create - * a socket on top of another socket. - * - */ - @Override - public void connect(SocketAddress remoteAddr, int timeout) - throws IOException - { - if (isClosed()) - { - throw new SocketException("Socket is closed"); - } - if (timeout < 0) - { - throw new IllegalArgumentException("timeout < 0"); - } - if (isConnected()) - { - throw new SocketException("Already connected"); - } - if (remoteAddr == null) - { - throw new IllegalArgumentException("remoteAddr == null"); - } - if (!(remoteAddr instanceof InetSocketAddress)) - { - throw new IllegalArgumentException( - "Remote address not an InetSocketAddress: " + - remoteAddr.getClass()); - } - InetSocketAddress inetAddr = (InetSocketAddress) remoteAddr; - if (inetAddr.getAddress() == null) - { - throw new UnknownHostException( - "Host is unresolved: " + inetAddr.getHostName()); - } - - int port = inetAddr.getPort(); - checkDestination(port); - - synchronized (connectLock) - { - try - { - socketImpl.connect(remoteAddr, timeout); - } - catch (IOException e) - { - socketImpl.close(); - throw e; - } - } - } - - /** - * {@inheritDoc} - */ - @Override - public synchronized void close() - throws IOException - { - synchronized (closeLock) - { - if (isClosed()) - return; - socketImpl.close(); - } - } - - @Override - public OutputStream getOutputStream() throws IOException - { - return socketImpl.getOutputStream(); - } - - /** - * Allows to set up the remote address directly. - * Otherwise, when using the other accept methods, - * the first address from which a packet is received, is considered - * the remote address. - * - * @param remoteAddress the one and only remote address that will be - * accepted as remote packet's source - * @param timeout connection accept timeout value in milliseconds, after - * which the exception will be thrown. - * @throws IOException if socket is closed or timeout expires - */ - public void accept(SocketAddress remoteAddress, int timeout) - throws IOException - { - socketImpl.accept(remoteAddress, timeout); - } - - /** - * Return the FileDescriptor of the underlying socket. - * @return the FileDescriptor of the underlying socket. - */ - public FileDescriptor getFileDescriptor() - { - return socketImpl.getFileDescriptor(); - } -} diff --git a/src/main/java/org/ice4j/pseudotcp/PseudoTcpSocketFactory.java b/src/main/java/org/ice4j/pseudotcp/PseudoTcpSocketFactory.java deleted file mode 100644 index 15ca99e5..00000000 --- a/src/main/java/org/ice4j/pseudotcp/PseudoTcpSocketFactory.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * ice4j, the OpenSource Java Solution for NAT and Firewall Traversal. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ice4j.pseudotcp; - -import java.io.*; -import java.net.*; -import javax.net.*; - -public class PseudoTcpSocketFactory - extends SocketFactory - implements SocketImplFactory -{ - /** - * Default conversation ID - */ - public static final long DEFAULT_CONVERSATION_ID=0; - - /** - * Default timeout for connect operation - */ - public static final int DEFAULT_CONNECT_TIMEOUT=5000; - - /** - * Creates a socket and connects it to the specified - * port number at the specified address. - */ - public Socket createSocket(String host, int port) - throws IOException, - UnknownHostException - { - Socket socket = createSocket(); - connectSocket(socket, new InetSocketAddress(host, port)); - return socket; - } - - /** - * Creates a socket and connect it to the specified remote address - * on the specified remote port. - */ - public Socket createSocket(InetAddress host, int port) throws IOException - { - Socket socket = createSocket(); - connectSocket(socket, new InetSocketAddress(host, port)); - return socket; - } - - private void connectSocket(Socket socket, InetSocketAddress remoteSockAddr) - throws IOException - { - socket.connect(remoteSockAddr, DEFAULT_CONNECT_TIMEOUT); - } - - /** - * Creates socket bound to local sockAddr - * @param sockAddr address for the pseudo socket - * @return socket bound to local address - * @throws IOException if the socket could not be opened, or the socket - * could not bind to the specified local port. - */ - public Socket createBoundSocket(InetSocketAddress sockAddr) - throws IOException - { - return new PseudoTcpSocket( - new PseudoTcpSocketImpl(DEFAULT_CONVERSATION_ID, - new DatagramSocket(sockAddr))); - } - - /** - * Creates a socket and connects it to the specified remote host at the specified remote port. - */ - public Socket createSocket(String host, - int port, - InetAddress localHost, - int localPort) - throws IOException, - UnknownHostException - { - Socket socket = createBoundSocket( - new InetSocketAddress(localHost, localPort)); - connectSocket(socket, new InetSocketAddress(host, port)); - return socket; - } - - /** - * Creates a socket and connects it to the specified remote host on the specified remote port. - */ - public Socket createSocket(InetAddress address, int port, - InetAddress localAddress, int localPort) throws IOException - { - Socket socket = createBoundSocket( - new InetSocketAddress(localAddress, localPort)); - connectSocket(socket, new InetSocketAddress(address, port)); - return socket; - } - - /** - * Creates a socket that will run on given datagramSocket - * - * @param datagramSocket the socket to run on - * @return new socket running on given datagramSocket - * @throws SocketException if there is an error in the underlying protocol, - * such as a TCP error. - */ - public PseudoTcpSocket createSocket(DatagramSocket datagramSocket) - throws SocketException - { - return new PseudoTcpSocket( - new PseudoTcpSocketImpl(DEFAULT_CONVERSATION_ID, datagramSocket)); - } - - /** - * Creates the PseudoTcp socket and binds it to any available port - * on the local host machine. The socket will be bound to the - * {@link InetAddress#isAnyLocalAddress wildcard} address, - * an IP address chosen by the kernel. - */ - @Override - public PseudoTcpSocket createSocket() - throws SocketException - { - return new PseudoTcpSocket( - new PseudoTcpSocketImpl(DEFAULT_CONVERSATION_ID)); - } - - public SocketImpl createSocketImpl() - { - try - { - return new PseudoTcpSocketImpl(DEFAULT_CONVERSATION_ID); - } - catch (SocketException e) - { - throw new RuntimeException(e); - } - } -} diff --git a/src/main/java/org/ice4j/pseudotcp/PseudoTcpSocketImpl.java b/src/main/java/org/ice4j/pseudotcp/PseudoTcpSocketImpl.java deleted file mode 100755 index 2d0b58ac..00000000 --- a/src/main/java/org/ice4j/pseudotcp/PseudoTcpSocketImpl.java +++ /dev/null @@ -1,1240 +0,0 @@ -/* - * ice4j, the OpenSource Java Solution for NAT and Firewall Traversal. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ice4j.pseudotcp; - -import java.io.*; -import java.net.*; -import java.util.*; -import java.util.concurrent.*; -import java.util.logging.*; - -class PseudoTcpSocketImpl - extends SocketImpl - implements PseudoTcpNotify -{ - /** - * The logger. - */ - private static final java.util.logging.Logger logger = - java.util.logging.Logger.getLogger(PseudoTcpSocketImpl.class.getName()); - /** - * Pseudotcp logic instance - */ - private final PseudoTCPBase pseudoTcp; - /** - * Datagram socket used to handle network operations - */ - private DatagramSocket socket; - /** - * Current socket address of remote socket that we are connected to - */ - private SocketAddress remoteAddr; - /** - * Receive buffer size used for receiving packets TODO: this should be - * checked with MTU ? - */ - private int DATAGRAM_RCV_BUFFER_SIZE = 8000; - /** - * Monitor object used to block threads on write operation. That is when the - * send buffer is full. - */ - private final Object write_notify = new Object(); - /** - * Monitor object used to block threads on read operation. That is when - * there's no more data available for reading. - */ - private final Object read_notify = new Object(); - /** - * Monitor object used to block thread waiting for change of TCP state. - */ - private final Object state_notify = new Object(); - - /** - * Exception which occurred in pseudotcp logic and must be propagated to - * threads blocked on any operations. - */ - private IOException exception; - /** - * Read operations timeout in ms - */ - private long writeTimeout; - /** - * Write operations timeout in ms - */ - private long readTimeout; - - private PseudoTcpInputStream inputStream; - - private PseudoTcpOutputStream outputstream; - - /** - * - * @param conv_id conversation id, must be the same on both sides - * @param sock datagram socket used for network operations - */ - public PseudoTcpSocketImpl(long conv_id, DatagramSocket sock) - { - pseudoTcp = new PseudoTCPBase(this, conv_id); - //Default MTU - setMTU(1450); - this.socket = sock; - //TODO: find out if this call is required - /*try - { - setOption(SO_TIMEOUT, 0); - } - catch (SocketException e) - { - throw new RuntimeException(e); - }*/ - } - - /** - * This constructor creates DatagramSocket with random port. Should - * be used for clients. - * - * @param conv_id conversation id, must be the same on both sides - * @throws SocketException - */ - public PseudoTcpSocketImpl(long conv_id) - throws SocketException - { - this(conv_id, new DatagramSocket()); - } - - /** - * Binds DatagramSocket to given local_port - * - * @param conv_id conversation id, must be the same on both sides - * @param local_port the local port that will be used for this socket - * @throws SocketException - */ - public PseudoTcpSocketImpl(long conv_id, int local_port) - throws SocketException - { - this(conv_id, new DatagramSocket(local_port)); - } - - /** - * Creates DatagramSocket for local_ip:local_port - * - * @param conv_id conversation id, must be the same on both sides - * @param local_ip used by DatagramSocket - * @param local_port used by DatagramSocket - * @throws SocketException - * @throws UnknownHostException - */ - public PseudoTcpSocketImpl(long conv_id, String local_ip, int local_port) - throws SocketException, - UnknownHostException - { - this(conv_id, new DatagramSocket(local_port, - InetAddress.getByName(local_ip))); - } - - /** - * Sets the MTU parameter for this socket - * @param mtu the MTU value - */ - public void setMTU(int mtu) - { - this.pseudoTcp.notifyMTU(mtu); - } - - /** - * - * @return current MTU set - */ - public int getMTU() - { - return pseudoTcp.getMTU(); - } - - long getConversationID() - { - return pseudoTcp.getConversationID(); - } - - void setConversationID(long convID) - { - pseudoTcp.setConversationID(convID); - } - - /** - * Sets debug name that will be displayed in log messages for this socket - * @param debugName the debug name to set - */ - public void setDebugName(String debugName) - { - this.pseudoTcp.debugName = debugName; - } - - /** - * Creates either a stream or a datagram socket. - * @param stream if true, create a stream socket; otherwise, create a datagram socket. - * @throws IOException if an I/O error occurs while creating the socket. - */ - @Override - protected void create(boolean stream) - throws IOException - { - //no effect - } - - /** - * Connects this socket to the specified port on the named host. - * @param host the name of the remote host. - * @param port the port number. - * @throws IOException - */ - @Override - protected void connect(String host, int port) - throws IOException - { - doConnect(new InetSocketAddress(InetAddress.getByName(host), port), 0); - } - - /** - * Connects this socket to the specified port number on the specified host. - * @param address the IP address of the remote host. - * @param port the port number. - * @throws IOException if an I/O error occurs when attempting a connection. - */ - @Override - protected void connect(InetAddress address, int port) - throws IOException - { - connect(address.getHostAddress(), port); - } - - /** - * Connects this socket to the specified port number on the specified host. - * A timeout of zero is interpreted as an infinite timeout. - * The connection will then block until established or an error occurs. - * @param address the Socket address of the remote host. - * @param timeout the timeout value, in milliseconds, or zero for no timeout. - * @throws IOException if an I/O error occurs when attempting a connection. - */ - @Override - protected void connect(SocketAddress address, int timeout) - throws IOException - { - InetSocketAddress inetAddr = (InetSocketAddress) address; - doConnect(inetAddr, timeout); - } - - /** - * Binds this socket to the specified port number on the specified host. - * @param host the IP address of the remote host. - * @param port the port number. - * @throws IOException - */ - @Override - public void bind(InetAddress host, int port) - throws IOException - { - if (socket != null) - socket.close(); - InetSocketAddress newAddr = new InetSocketAddress(host.getHostAddress(), port); - this.socket = new DatagramSocket(newAddr); - } - - /** - * Sets the maximum queue length for incoming connection - * indications (a request to connect) to the count argument. - * If a connection indication arrives when the queue is full, - * the connection is refused. - * @param backlog the maximum length of the queue. - * @throws IOException if an I/O error occurs when creating the queue. - */ - @Override - protected void listen(int backlog) - throws IOException - { - throw new UnsupportedOperationException("Not supported yet."); - } - - private Map options = new HashMap<>(); - @Override - public void setOption(int optID, Object value) - throws SocketException - { - //TODO: map options to PTCP options/method calls - options.put(optID, value); - } - - @Override - public Object getOption(int optID) - throws SocketException - { - //TODO: map options to PTCP options/method calls - if (optID == SocketOptions.TCP_NODELAY) - { - Object ret = options.get(Option.OPT_NODELAY.ordinal()); - return ret != null; - } - - Object option = options.get(optID); - if (option == null) - { - logger.warning("Asked for unknown optID" + optID); - } - return option; - } - - public long getPTCPOption(Option opt) - { - if (Option.OPT_READ_TIMEOUT == opt) - { - return this.readTimeout; - } - else if (Option.OPT_WRITE_TIMEOUT == opt) - { - return this.writeTimeout; - } - else - { - return pseudoTcp.getOption(opt); - } - } - - public void setPTCPOption(Option opt, long optValue) - { - if (Option.OPT_WRITE_TIMEOUT == opt) - { - this.writeTimeout = optValue >= 0 ? optValue : 0; - } - else if (Option.OPT_READ_TIMEOUT == opt) - { - this.readTimeout = optValue >= 0 ? optValue : 0; - } - else - { - pseudoTcp.setOption(opt, optValue); - } - } - - - /** - * Start connection procedure - * - * @param remoteAddress to which this socket connects to - * @param timeout for this operation in ms - */ - void doConnect(InetSocketAddress remoteAddress, long timeout) - throws IOException - { - logger.fine("Connecting to "+remoteAddress); - this.remoteAddr = remoteAddress; - startThreads(); - pseudoTcp.connect(); - updateClock(); - boolean noTimeout = timeout <= 0; - try - { - long elapsed = 0; - //Here the threads is blocked untill we reach TCP_ESTABLISHED state - //There's also check for timeout for that op - synchronized (state_notify) - { - while (pseudoTcp.getState() != PseudoTcpState.TCP_ESTABLISHED - && (noTimeout || (elapsed < timeout)) ) - { - long start = System.nanoTime(); - state_notify.wait(timeout); - elapsed += TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); - } - if (pseudoTcp.getState() != PseudoTcpState.TCP_ESTABLISHED) - { - throw new IOException("Connect timeout"); - } - } - } - catch (InterruptedException ex) - { - close(); - throw new IOException("Connect aborted"); - } - } - - /** - * Blocking method waits for connection. - * - * @param remoteAddress the one and only address that will be - * accepted as the source for remote packets - * @param timeout for this operation in ms - * @throws IOException If socket gets closed or timeout expires - */ - void accept(SocketAddress remoteAddress, int timeout) - throws IOException - { - this.remoteAddr = remoteAddress; - accept(timeout); - } - - - /** - * Blocking method waits for connection. - * - * @param timeout for this operation in ms - * @throws IOException If socket gets closed or timeout expires - */ - void accept(int timeout) - throws IOException - { - try - { - startThreads(); - PseudoTcpState state = pseudoTcp.getState(); - if (state == PseudoTcpState.TCP_CLOSED) - { - throw new IOException("Socket closed"); - } - if (pseudoTcp.getState() != PseudoTcpState.TCP_ESTABLISHED) - { - synchronized (state_notify) - { - state_notify.wait(timeout); - } - } - if (pseudoTcp.getState() != PseudoTcpState.TCP_ESTABLISHED) - { - throw new IOException("Accept timeout"); - } - } - catch (InterruptedException ex) - { - IOException e = new IOException("Accept aborted"); - pseudoTcp.closedown(e); - throw e; - } - } - - /** - * Accepts a connection. - * @param s the accepted connection. - * @throws IOException if an I/O error occurs when accepting the connection. - */ - @Override - protected void accept(SocketImpl s) - throws IOException - { - //TODO: not sure how this should work - int timeout = 5000; - accept(timeout); - } - - /** - * - * @return current TCP state - */ - public PseudoTcpState getState() - { - return pseudoTcp.getState(); - } - - /** - * Interrupts clock thread's wait method to force time update - */ - private void updateClock() - { - scheduleClockTask(0); - } - - /** - * Starts all threads required by the socket - */ - private void startThreads() - { - pseudoTcp.notifyClock(PseudoTCPBase.now()); - receiveThread = new Thread(new Runnable() - { - @Override - public void run() - { - receivePackets(); - } - }, "PseudoTcpReceiveThread"); - - runReceive = true; - runClock = true; - receiveThread.start(); - scheduleClockTask(0); - } - - /** - * Implements PseudoTcpNotify - * Called when TCP enters connected state. - * - * @param tcp the {@link PseudoTCPBase} that caused an event - * @see PseudoTcpNotify#onTcpOpen(PseudoTCPBase) - */ - @Override - public void onTcpOpen(PseudoTCPBase tcp) - { - logger.log(Level.FINE, "tcp opened"); - //Release threads blocked at state_notify monitor object. - synchronized (state_notify) - { - state_notify.notifyAll(); - } - //TCP is considered writeable at this point - onTcpWriteable(tcp); - } - - /** - * Implements PseudoTcpNotify - * - * @param tcp the {@link PseudoTCPBase} that caused an event - * @see PseudoTcpNotify#onTcpReadable(PseudoTCPBase) - */ - @Override - public void onTcpReadable(PseudoTCPBase tcp) - { - if (logger.isLoggable(Level.FINER)) - { - logger.log( - Level.FINER, - "TCP READABLE data available for reading: "+tcp.getAvailable()); - } - //release all thread blocked at read_notify monitor - synchronized (read_notify) - { - read_notify.notifyAll(); - } - } - - /** - * Implements PseudoTcpNotify - * - * @param tcp the {@link PseudoTCPBase} that caused an event - * @see PseudoTcpNotify#onTcpWriteable(PseudoTCPBase) - */ - @Override - public void onTcpWriteable(PseudoTCPBase tcp) - { - - logger.log(Level.FINER, "stream writeable"); - //release all threads blocked at write monitor - synchronized (write_notify) - { - write_notify.notifyAll(); - } - //writeSemaphore.release(1); - logger.log(Level.FINER, "write notified - now !"); - - } - - /** - * Implements PseudoTcpNotify - * - * @param tcp the {@link PseudoTCPBase} that caused an event - * @param e the Exception which is the reason for closing socket, - * or null if there wasn't any - * - * @see PseudoTcpNotify#onTcpClosed(PseudoTCPBase, IOException) - */ - @Override - public void onTcpClosed(PseudoTCPBase tcp, IOException e) - { - if (e != null) - { - //e.printStackTrace(); - logger.log(Level.SEVERE, "PseudoTcp closed: " + e); - } - else - { - logger.log(Level.FINE, "PseudoTcp closed"); - } - runReceive = false; - runClock = false; - this.exception = e; - releaseAllLocks(); - cancelClockTask(true); - - } - - /** - * Releases all monitor objects so that the threads will check their "run - * flags" - */ - private void releaseAllLocks() - { - synchronized (read_notify) - { - read_notify.notifyAll(); - } - synchronized (write_notify) - { - write_notify.notifyAll(); - } - synchronized (state_notify) - { - state_notify.notifyAll(); - } - //this interrupt won't work for DatagramSocket read packet operation - //receiveThread.interrupt(); - } - - /** - * Joins all running threads - * - * @throws InterruptedException - */ - private void joinAllThreads() throws InterruptedException - { - receiveThread.join(); - } - - /** - * Implements PseudoTcpNotify - * - * @param tcp the {@link PseudoTCPBase} that caused an event - * @param buffer the buffer containing packet data - * @param len packet data length in bytes - * @return operation result - * - * @see PseudoTcpNotify#tcpWritePacket(PseudoTCPBase, byte[], int) - */ - @Override - public WriteResult tcpWritePacket(PseudoTCPBase tcp, byte[] buffer, int len) - { - if (logger.isLoggable(Level.FINEST)) - { - logger.log(Level.FINEST, - "write packet to network length " + len - + " address " + remoteAddr); - } - try - { - //TODO: in case the packet is too long it should return WR_TOO_LARGE - DatagramPacket packet = new DatagramPacket(buffer, len, remoteAddr); - socket.send(packet); - return WriteResult.WR_SUCCESS; - } - catch (IOException ex) - { - logger.log(Level.SEVERE, "TcpWritePacket exception: " + ex); - return WriteResult.WR_FAIL; - } - - } - /** - * Flag which enables packets receive thread - */ - private boolean runReceive = false; - /** - * Thread receiving packets from the network - */ - private Thread receiveThread; - - /** - * Receives packets from the network and passes them to TCP logic class - */ - private void receivePackets() - { - byte[] buffer = new byte[DATAGRAM_RCV_BUFFER_SIZE]; - DatagramPacket packet = new DatagramPacket(buffer, - DATAGRAM_RCV_BUFFER_SIZE); - while (runReceive) - { - try - { - socket.receive(packet); - //Here is the binding point for remote socket if wasn't - //specified earlier - if (remoteAddr == null) - { - remoteAddr = packet.getSocketAddress(); - logger.log(Level.WARNING, - "Remote addr not set previously, setting to " - + remoteAddr); - } - else - { - if (!packet.getSocketAddress().equals(remoteAddr)) - { - logger.log(Level.WARNING, - "Ignoring packet from " + packet.getAddress() - + ":" + packet.getPort() - + " should be: " + remoteAddr); - continue; - } - } - synchronized (pseudoTcp) - { - pseudoTcp.notifyPacket(buffer, packet.getLength()); - //we need to update the clock after new packet is receivied - updateClock(); - } - } - catch (IOException ex) - { - //this exception occurs even when the socket - //is closed with the close operation, so we check - //here if this exception is important - if (runReceive) - { - logger.log(Level.SEVERE, - "ReceivePackets exception: " + ex); - pseudoTcp.closedown(ex); - } - break; - } - } - } - /** - * The run flag for clock thread - */ - private boolean runClock = false; - - // FIXME: consider larger thread pool and/or making it configurable - private final static ScheduledThreadPoolExecutor clockExecutor - = new ScheduledThreadPoolExecutor(1); - - private volatile ScheduledFuture currentlyScheduledClockTask = null; - - /** - * Method runs cyclic notification about time progress for TCP logic class - * It runs in a separate thread - */ - private void runClock() - { - if (!runClock) - { - return; - } - long sleep; - - synchronized (pseudoTcp) - { - pseudoTcp.notifyClock(PseudoTCPBase.now()); - sleep = pseudoTcp.getNextClock(PseudoTCPBase.now()); - } - - //there might be negative interval even if there's no error - if (sleep == -1) - { - releaseAllLocks(); - if (exception != null) - { - logger.log(Level.SEVERE, - "STATE: " + pseudoTcp.getState() - + " ERROR: " + exception.getMessage()); - } - } - else - { - //logger.log(Level.FINEST, "Clock sleep for " + sleep); - scheduleClockTask(sleep); - } - } - - private final Runnable clockTaskRunner = this::runClock; - - private void scheduleClockTask(long sleep) - { - synchronized (clockTaskRunner) - { - // Cancel any existing tasks, to make sure we don't run duplicates. - cancelClockTask(false); - if (runClock) - { - currentlyScheduledClockTask - = clockExecutor.schedule( - clockTaskRunner, sleep, TimeUnit.MILLISECONDS); - } - } - } - - private void cancelClockTask(boolean interruptIfRunning) - { - // Copy the reference, in case it changes. - ScheduledFuture taskToCancel = this.currentlyScheduledClockTask; - if (taskToCancel != null) - { - taskToCancel.cancel(interruptIfRunning); - } - } - - /** - * Returns an output stream for this socket. - * @return an output stream for writing to this socket. - * @throws IOException if an I/O error occurs when creating the output stream. - */ - @Override - public OutputStream getOutputStream() - throws IOException - { - if (outputstream == null) - { - outputstream = new PseudoTcpOutputStream(); - } - return outputstream; - } - - - /** - * Returns an input stream for this socket. - * @return a stream for reading from this socket. - * @throws IOException - */ - @Override - public InputStream getInputStream() - throws IOException - { - if (inputStream == null) - { - inputStream = new PseudoTcpInputStream(); - } - return inputStream; - } - - /** - * Returns the number of bytes that can be read from this socket without blocking. - * @return the number of bytes that can be read from this socket without blocking. - * @throws IOException if an I/O error occurs when determining the number of bytes available. - */ - @Override - protected int available() - throws IOException - { - return getInputStream().available(); - } - - /** - * Closes this socket. - */ - @Override - public void close() - throws IOException - { - try - { - pseudoTcp.close(true); - //System.out.println("ON CLOSE: in flight "+pseudoTcp.getBytesInFlight()); - //System.out.println("ON CLOSE: buff not sent "+pseudoTcp.getBytesBufferedNotSent()); - onTcpClosed(pseudoTcp, null); - socket.close(); - joinAllThreads(); - //UpdateClock(); - //TODO: closing procedure - //Here the thread should be blocked until TCP - //reaches CLOSED state, but there's no closing procedure - /* - * synchronized(state_notify){ while(pseudoTcp.getState() != - * PseudoTcpState.TCP_CLOSED){ try { state_notify.wait(); } catch - * (InterruptedException ex) { throw new IOException("Close - * connection aborted"); } } } - */ - } - catch (InterruptedException ex) - { - throw new IOException("Closing socket interrupted", ex); - } - } - - /** - * Send one byte of urgent data on the socket. The byte to be sent is the low eight bits of the parameter - * @param data The byte of data to send - * @throws IOException if there is an error sending the data. - */ - @Override - protected void sendUrgentData(int data) - throws IOException - { - throw new RuntimeException("Sending urgent data is not supported"); - } - - - - /** - * This class implements java.io.InputStream - */ - class PseudoTcpInputStream extends InputStream - { - public PseudoTcpInputStream() - { - } - - @Override - public boolean markSupported() - { - return false; - } - - /** - * There's no end of stream detection at the moment. Method blocks until - * it returns any data or an exception is thrown - * - * @return read byte count - * @throws IOException in case of en error - */ - @Override - public int read() throws IOException - { - byte[] buff = new byte[1]; - int readCount = read(buff, 0, 1); - return readCount == 1 ? (buff[0] & 0xFF) : -1; - } - - @Override - public int read(byte[] bytes) throws IOException - { - return read(bytes, 0, bytes.length); - } - - /** - * This method blocks until any data is available - * - * @param buffer destination buffer - * @param offset destination buffer's offset - * @param length maximum count of bytes that can be read - * @return byte count actually read - * @throws IOException in case of error or if timeout occurs - */ - @Override - public int read(byte[] buffer, int offset, int length) - throws IOException - { - long start = System.nanoTime(); - int read; - while (true) - { - logger.log(Level.FINER, "Read Recv"); - try - { - read = pseudoTcp.recv(buffer, offset, length); - if (logger.isLoggable(Level.FINER)) - { - logger.log(Level.FINER, - "Read Recv read count: " + read); - } - if (read > 0) - { - return read; - } - logger.log(Level.FINER, "Read wait for data available"); - if (readTimeout > 0) - { - //Check for timeout - long elapsed = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); - long left = readTimeout - elapsed; - if (left <= 0) - { - IOException exc = - new IOException("Read operation timeout"); - pseudoTcp.closedown(exc); - throw exc; - } - synchronized (read_notify) - { - if (pseudoTcp.getAvailable() == 0) - { - read_notify.wait(left); - } - } - } - else - { - synchronized (read_notify) - { - if (pseudoTcp.getAvailable() == 0) - { - read_notify.wait(); - } - } - } - if (logger.isLoggable(Level.FINER)) - { - logger.log( - Level.FINER, - "Read notified: " + pseudoTcp.getAvailable()); - } - if (exception != null) - { - throw exception; - } - } - catch (InterruptedException ex) - { - if (exception != null) - { - throw new IOException("Read aborted", exception); - } - else - { - throw new IOException("Read aborted"); - } - } - } - } - - @Override - public int available() throws IOException - { - return pseudoTcp.getAvailable(); - } - - @Override - public void close() throws IOException - { - } - - /** - * {@inheritDoc} - */ - @Override - public long skip(long n) throws IOException - { - return super.skip(n); - } - - /** - * {@inheritDoc} - */ - @Override - public synchronized void mark(int readlimit) - { - throw new UnsupportedOperationException("mark"); - } - - /** - * {@inheritDoc} - */ - @Override - public synchronized void reset() throws IOException - { - throw new UnsupportedOperationException("reset"); - } - } - - /** - * Implements java.io.OutputStream - */ - class PseudoTcpOutputStream extends OutputStream - { - @Override - public void write(int b) throws IOException - { - byte[] bytes = new byte[1]; - bytes[0] = (byte) b; - write(bytes); - } - - /** - * This method blocks until all data has been written. - * - * @param buffer source buffer - * @param offset source buffer's offset - * @param length byte count to be written - * @throws IOException in case of error or if timeout occurs - * - */ - @Override - public void write(byte[] buffer, int offset, int length) throws IOException - { - int toSend = length; - int sent; - long start = System.nanoTime(); - while (toSend > 0) - { - synchronized (pseudoTcp) - { - sent = pseudoTcp.send(buffer, offset + length - toSend, toSend); - } - if (sent > 0) - { - toSend -= sent; - } - else - { - try - { - logger.log(Level.FINER, "Write wait for notify"); - synchronized (write_notify) - { - if (writeTimeout > 0) - { - long elapsed = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); - long left = writeTimeout - elapsed; - if (left <= 0) - { - IOException exc = - new IOException("Write operation timeout"); - pseudoTcp.closedown(exc); - throw exc; - } - write_notify.wait(left); - } - else - { - write_notify.wait(); - } - } - logger.log(Level.FINER, - "Write notified, available: " - + pseudoTcp.getAvailableSendBuffer()); - if (exception != null) - { - throw exception; - } - } - catch (InterruptedException ex) - { - if (exception != null) - { - throw new IOException("Write aborted", exception); - } - else - { - throw new IOException("Write aborted", ex); - } - } - } - } - } - - /** - * This method block until all buffered data has been written - * - * @throws IOException in case of error or if timeout occurs - */ - @Override - public synchronized void flush() throws IOException - { - logger.log(Level.FINE, "Flushing..."); - long start = System.nanoTime(); - final Object ackNotify = pseudoTcp.getAckNotify(); - synchronized (ackNotify) - { - while (pseudoTcp.getBytesBufferedNotSent() > 0) - { - try - { - if (writeTimeout > 0) - { - //Check write timeout - long elapsed = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); - long left = writeTimeout - elapsed; - if (left <= 0) - { - IOException e = - new IOException("Flush operation timeout"); - pseudoTcp.closedown(e); - throw e; - } - ackNotify.wait(left); - } - else - { - ackNotify.wait(); - } - } - catch (InterruptedException ex) - { - throw new IOException("Flush stream interrupted", ex); - } - } - } - logger.log(Level.FINE, "Flushing completed"); - } - - @Override - public void close() throws IOException - { - PseudoTcpSocketImpl.this.close(); - } - } - - /** - * {@inheritDoc} - */ - @Override - protected FileDescriptor getFileDescriptor() - { - return fd; - } - - /** - * {@inheritDoc} - */ - @Override - protected void shutdownInput() - throws IOException - { - throw new IOException("Method not implemented!"); - } - - /** - * {@inheritDoc} - */ - @Override - protected void shutdownOutput() - throws IOException - { - throw new IOException("Method not implemented!"); - } - - /** - * {@inheritDoc} - */ - @Override - protected InetAddress getInetAddress() - { - return ((InetSocketAddress) remoteAddr).getAddress(); - } - - /** - * {@inheritDoc} - */ - @Override - protected int getPort() - { - return ((InetSocketAddress) remoteAddr).getPort(); - } - - /** - * {@inheritDoc} - */ - @Override - protected boolean supportsUrgentData() - { - return false; - } - - /** - * {@inheritDoc} - */ - @Override - protected int getLocalPort() - { - return socket.getLocalPort(); - } - - /** - * {@inheritDoc} - */ - @Override - protected void setPerformancePreferences(int connectionTime, - int latency, - int bandwidth) - { - throw new UnsupportedOperationException("setPerformancePreferences"); - } -} diff --git a/src/main/java/org/ice4j/pseudotcp/PseudoTcpState.java b/src/main/java/org/ice4j/pseudotcp/PseudoTcpState.java deleted file mode 100755 index 6a3ab550..00000000 --- a/src/main/java/org/ice4j/pseudotcp/PseudoTcpState.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * ice4j, the OpenSource Java Solution for NAT and Firewall Traversal. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ice4j.pseudotcp; - -/** - * TCP states defined for pseudoTCP - * @author Pawel Domas - */ -public enum PseudoTcpState -{ - /** - * Initial state, can accept connection - */ - TCP_LISTEN, // = 0, - /** - * SYN sent to remote peer, wits for SYN - */ - TCP_SYN_SENT, // = 1, - /** - * SYN received from remote peer, sends back SYN - */ - TCP_SYN_RECEIVED, // = 2; - /** - * SYN sent and received - connection established - */ - TCP_ESTABLISHED, // = 3; - /** - * Closed state. In current implementation reached on error - * or explicite by close method with force option - * TODO: closing procedure - */ - TCP_CLOSED // = 4; -} diff --git a/src/main/java/org/ice4j/pseudotcp/RSegment.java b/src/main/java/org/ice4j/pseudotcp/RSegment.java deleted file mode 100644 index e36067d3..00000000 --- a/src/main/java/org/ice4j/pseudotcp/RSegment.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * ice4j, the OpenSource Java Solution for NAT and Firewall Traversal. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ice4j.pseudotcp; - - -/** - * Class used internally as a structure for receive segments - * - * @author Pawel - */ -class RSegment -{ - public long seq, len; - - public RSegment(long seq, long len) - { - this.seq = seq; - this.len = len; - } -} diff --git a/src/main/java/org/ice4j/pseudotcp/SSegment.java b/src/main/java/org/ice4j/pseudotcp/SSegment.java deleted file mode 100644 index 53bc7d3d..00000000 --- a/src/main/java/org/ice4j/pseudotcp/SSegment.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * ice4j, the OpenSource Java Solution for NAT and Firewall Traversal. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ice4j.pseudotcp; - -/** - * Class used internally as a structure for send segments - * - * @author Pawel Domas - */ -class SSegment -{ - long seq, len; - //uint32 tstamp; - short xmit; - boolean bCtrl; - - SSegment(long s, long l, boolean c) - { - seq = s; - len = l; - xmit = 0; - bCtrl = c; - } -} diff --git a/src/main/java/org/ice4j/pseudotcp/Segment.java b/src/main/java/org/ice4j/pseudotcp/Segment.java deleted file mode 100644 index 8925eeef..00000000 --- a/src/main/java/org/ice4j/pseudotcp/Segment.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * ice4j, the OpenSource Java Solution for NAT and Firewall Traversal. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ice4j.pseudotcp; - - -/** - * Class used as a segment structure - * - * @author Pawel Domas - */ -public class Segment -{ - long conv; - long seq; - long ack; - byte flags; - int wnd; - long tsval; - long tsecr; - byte[] data; - int len; -} diff --git a/src/main/java/org/ice4j/pseudotcp/SendFlags.java b/src/main/java/org/ice4j/pseudotcp/SendFlags.java deleted file mode 100644 index 1491eba3..00000000 --- a/src/main/java/org/ice4j/pseudotcp/SendFlags.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * ice4j, the OpenSource Java Solution for NAT and Firewall Traversal. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ice4j.pseudotcp; - -/** - * Send flags used internally - * - * @author Pawel Domas - */ -enum SendFlags -{ - sfNone, sfImmediateAck, sfDelayedAck -} diff --git a/src/main/java/org/ice4j/pseudotcp/WriteResult.java b/src/main/java/org/ice4j/pseudotcp/WriteResult.java deleted file mode 100755 index 2f0e8ff3..00000000 --- a/src/main/java/org/ice4j/pseudotcp/WriteResult.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * ice4j, the OpenSource Java Solution for NAT and Firewall Traversal. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ice4j.pseudotcp; - -/** - * The result of write packet operations - * @author Pawel Domas - */ -public enum WriteResult -{ - /** - * Packet successfully transmitted - */ - WR_SUCCESS, - /** - * Packet was too large - */ - WR_TOO_LARGE, - /** - * Write failed - */ - WR_FAIL -} diff --git a/src/main/java/org/ice4j/pseudotcp/util/ByteFifoBuffer.java b/src/main/java/org/ice4j/pseudotcp/util/ByteFifoBuffer.java deleted file mode 100755 index 73fd03d1..00000000 --- a/src/main/java/org/ice4j/pseudotcp/util/ByteFifoBuffer.java +++ /dev/null @@ -1,397 +0,0 @@ -/* - * ice4j, the OpenSource Java Solution for NAT and Firewall Traversal. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ice4j.pseudotcp.util; - -import java.nio.*; - -/** - * First in - first out byte buffer - * - * @author Pawel Domas - */ -public class ByteFifoBuffer -{ - /** - * Backing byte array - */ - private byte[] array; - /** - * Current write position - */ - private int write_pos = 0; - /** - * Stored bytes count - */ - private int buffered = 0; - /** - * Current read position - */ - private int read_pos = 0; - - /** - * Creates buffer of specified size - * - * @param len buffer's size - */ - public ByteFifoBuffer(int len) - { - array = new byte[len]; - } - - /** - * @return buffer's capacity - */ - public int length() - { - return array.length; - } - - /** - * Reads count bytes into out_buffer. - * Current read position is incremented by count of bytes - * that has been successfully read. - * - * @param out_buffer read count bytes into this buffer - * @param count number of bytes to read into the out_buffer - * @return bytes successfully read - */ - public int read(byte[] out_buffer, int count) - { - return read(out_buffer, 0, count); - } - - /** - * Read with buffer offset - * - * @param out_buffer read count bytes into this buffer - * @param buff_offset offset where to start writing into out_buffer - * @param count bytes to read - * @return read byte count - */ - public int read(byte[] out_buffer, int buff_offset, int count) { - count = readLimit(count); - if (count > 0) - { - readOp(out_buffer, buff_offset, count, array, read_pos, array.length); - read_pos = (read_pos + count) % array.length; - buffered -= count; - } - return count; - } - - /** - * Limits desiredReadCount to count that is actually available - * @param desiredReadCount desired amount of bytes to read - * @return min(buffered, desiredReadCount) - */ - private int readLimit(int desiredReadCount) - { - return desiredReadCount > buffered ? buffered : desiredReadCount; - } - - /** - * Utility method used for read operations - * @param outBuffer - * @param dst_buff_offset - * @param count - * @param srcBuffer - * @param read_pos - * @param buff_len - */ - private static void readOp(byte[] outBuffer, int dst_buff_offset, int count, - byte[] srcBuffer, int read_pos, int buff_len) - { - if (read_pos + count <= buff_len) - { - //single operation - System.arraycopy(srcBuffer, read_pos, outBuffer, dst_buff_offset, - count); - } - else - { - //two operations - int tillEndCount = buff_len - read_pos; - System.arraycopy(srcBuffer, read_pos, outBuffer, - dst_buff_offset, tillEndCount); - int fromStartCount = count - tillEndCount; - System.arraycopy(srcBuffer, 0, outBuffer, - dst_buff_offset + tillEndCount, fromStartCount); - } - } - - /** - * - * @return space left in buffer for write - */ - public int getWriteRemaining() - { - return array.length - buffered; - } - - /** - * - * @return bytes stored in buffer and available for reading - */ - public int getBuffered() - { - return buffered; - } - - /** - * Writes count of bytes from the buffer - * - * @param buffer data to write into the buffer - * @param count number of bytes to read from the buffer - * @return bytes successfully written to buffer - */ - public int write(byte[] buffer, int count) - { - return write(buffer, 0, count); - } - - /** - * Writes data into the buffer. - * - * @param data source data - * @param offset source buffer's offset - * @param count number of bytes to read from the buffer - * @return byte count actually read - */ - public int write(byte[] data, int offset, int count) - { - /* - * System.out.println("----write " + this + " " + len + " buffered " + - * GetBuffered() + " buff avail: " + GetWriteRemaining()); - */ - count = writeLimit(count); - writeOp(data, offset, count, array, write_pos, array.length); - write_pos = (write_pos + count) % array.length; - buffered += count; - /* - * System.out.println("----write "+this+" "+len+" buffered - * "+GetBuffered()); for(int i=0; i < len; i++){ - * System.out.println("WDATA: "+data[i]); } - */ - return count; - } - - /** - * Utility method for write operations - * @param inBuffer - * @param inOffset - * @param count - * @param outBuffer - * @param write_pos - * @param buff_len - */ - private static void writeOp(byte[] inBuffer, - int inOffset, - int count, - byte[] outBuffer, - int write_pos, - int buff_len) - { - if ((write_pos + count) <= buff_len) - { - //single op - System.arraycopy(inBuffer, inOffset, outBuffer, write_pos, count); - } - else - { - //till end and from beginning - int tillEndCount; - int fromStartCount; - tillEndCount = buff_len - write_pos; - fromStartCount = count - tillEndCount; - System.arraycopy(inBuffer, inOffset, outBuffer, - write_pos, tillEndCount); - System.arraycopy(inBuffer, inOffset + tillEndCount, - outBuffer, 0, fromStartCount); - } - } - - /** - * Limits desiredWriteCount to what's actually available - * @param desiredWriteCount - * @return - */ - private int writeLimit(int desiredWriteCount) - { - return desiredWriteCount > (array.length - buffered) ? - (array.length - buffered) : desiredWriteCount; - } - - /** - * Checks if new write position is correct - * - * @param newWrPos new write position - */ - private void assertWriteLimit(int newWrPos) - throws IllegalArgumentException - { - int spaceReq; - int availSpace = getWriteRemaining(); - if (newWrPos < write_pos) - { - spaceReq = newWrPos + (array.length - write_pos); - } - else - { - spaceReq = newWrPos - write_pos; - } - - if (spaceReq > availSpace) - { - throw new IllegalArgumentException(); - } - } - - /** - * Advances current buffer's write position by count bytes - * - * @param count number of bytes to move forward - */ - public void consumeWriteBuffer(int count) - throws IllegalArgumentException, - BufferOverflowException - { - if (count > getWriteRemaining()) - { - throw new BufferOverflowException(); - } - if (count < 0) - { - throw new IllegalArgumentException(); - } - int newPos = (write_pos + count) % array.length; - assertWriteLimit(newPos); - - write_pos = newPos; - buffered += count; - } - - /** - * Sets new buffer's capacity - * - * @param new_size number of bytes - * @return true if operation is possible to perform, that is if new - * buffered data fits into new buffer - */ - public boolean setCapacity(int new_size) - { - if (new_size < getBuffered()) - { - return false; - } - byte[] newBuff = new byte[new_size]; - readOp(newBuff, 0, buffered, array, read_pos, array.length); - this.array = newBuff; - return true; - } - - /** - * Aligns current read position by count - * - * @param count number of bytes to move the read position - * @throws BufferUnderflowException if new position exceeds buffered data - * count - */ - public void consumeReadData(int count) - throws IllegalArgumentException, - BufferUnderflowException - { - /* - * System.out.println("Consume read " + this + " " + count + " read pos: - * " + read_pos); - */ - if (count > buffered) - { - throw new BufferUnderflowException(); - } - if (count < 0) - { - throw new IllegalArgumentException(); - } - this.read_pos = (read_pos + count) % array.length; - buffered -= count; - } - - /** - * Reads count bytes from buffer without storing new read position - * - * @param dst_buff buffer to write the read data to - * @param dst_buff_offset offset of destination buffer - * @param count bytes to read - * @param offset from current read position - * @return bytes successfully read - */ - public int readOffset(byte[] dst_buff, - int dst_buff_offset, - int count, - int offset) - { - //TODO: not sure if should decrease read count or throw an exception - /* - * System.out.println("Read dst offset " + dst_buff_offset + " offset " - * + offset + " len " + count + " " + this); - */ - int read_offset = (this.read_pos + offset) % array.length; - readOp(dst_buff, dst_buff_offset, count, array, read_offset, array.length); - - return count; - } - - /** - * Writes count bytes from data to the buffer without - * affecting buffer's write position - * - * @param data the data to write to the buffer - * @param count number of bytes to read from data - * @param nOffset from buffer's write position - * @return bytes successfully written - */ - public int writeOffset(byte[] data, int count, int nOffset) - throws BufferOverflowException - { - if (count > getWriteRemaining()) - { - throw new BufferOverflowException(); - } - if (count < 0) - { - throw new IllegalArgumentException(); - } - int offWritePos = (this.write_pos + nOffset) % array.length; - count = writeLimit(count); - assertWriteLimit(offWritePos + count); - writeOp(data, 0, count, array, offWritePos, array.length); - - return count; - } - - public void resetReadPosition() - { - this.read_pos = 0; - } - - public void resetWritePosition() - { - this.write_pos = 0; - this.buffered = 0; - } -} diff --git a/src/test/java/org/ice4j/pseudotcp/MultiThreadSupportTest.java b/src/test/java/org/ice4j/pseudotcp/MultiThreadSupportTest.java deleted file mode 100755 index b52a306b..00000000 --- a/src/test/java/org/ice4j/pseudotcp/MultiThreadSupportTest.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * ice4j, the OpenSource Java Solution for NAT and Firewall Traversal. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ice4j.pseudotcp; - -import static org.junit.jupiter.api.Assertions.*; - -import java.util.concurrent.TimeUnit; -import java.util.function.BooleanSupplier; -import java.util.function.Function; -import java.util.function.Predicate; - -/** - * This class enables multi thread tests where main thread waits in loop for - * specified condition to be met, while others perform some operations. For - * example until the connection is established or closed. It also registers - * default uncaught exception handler to catch exceptions from other threads. - *

- * Condition checks are passed as IWaitUntilDone interface. - * - * @author Pawel Domas - */ -public class MultiThreadSupportTest implements Thread.UncaughtExceptionHandler -{ - private volatile Throwable testError; - - private volatile Thread errorThread; - - private final Object testLock = new Object(); - - @Override - public void uncaughtException(Thread t, Throwable e) - { - synchronized (testLock) - { - testError = e; - errorThread = t; - testLock.notifyAll(); - } - } - - private static final long ASSERT_WAIT_INTERVAL = 100; - - protected boolean assert_wait_until(BooleanSupplier wait, long timeoutMs) - { - long timeoutNanos = TimeUnit.MILLISECONDS.toNanos(timeoutMs); - try - { - long start = System.nanoTime(); - while (!wait.getAsBoolean() && (System.nanoTime() - start) < timeoutNanos) - { - synchronized (testLock) - { - testLock.wait(ASSERT_WAIT_INTERVAL); - if (testError != null) - { - testError.printStackTrace(); - fail("Error in thread: " + errorThread.getName() + " : " - + testError.getMessage()); - } - } - } - return wait.getAsBoolean(); - } - catch (InterruptedException ex) - { - ex.printStackTrace(); - fail("assert_wait - interrupted"); - //return is unreachable - return false; - } - } -} diff --git a/src/test/java/org/ice4j/pseudotcp/PseudoTcpStreamTest.java b/src/test/java/org/ice4j/pseudotcp/PseudoTcpStreamTest.java deleted file mode 100755 index efbe390a..00000000 --- a/src/test/java/org/ice4j/pseudotcp/PseudoTcpStreamTest.java +++ /dev/null @@ -1,322 +0,0 @@ -/* - * ice4j, the OpenSource Java Solution for NAT and Firewall Traversal. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ice4j.pseudotcp; - -import static org.junit.jupiter.api.Assertions.*; - -import java.io.*; -import java.net.*; -import java.util.concurrent.atomic.*; -import java.util.logging.*; -import org.junit.jupiter.api.*; - -public class PseudoTcpStreamTest - extends MultiThreadSupportTest -{ - /** - * The logger. - */ - private static final Logger logger = Logger - .getLogger(PseudoTcpStreamTest.class.getName()); - - /** - * Test one-way transfer with @link(PseudoTcpStream) - */ - @Test - @Timeout(10) - public void testConnectTransferClose() - throws IOException - { - Thread.setDefaultUncaughtExceptionHandler(this); - int transferTimeout = 5000; - - // bytes that will be read as a single byte - final int singleStepCount = 34; - final byte[] bufferSingle = - PseudoTcpTestBase.createDummyData(singleStepCount); - final int sizeA = 138746; - final byte[] bufferA = PseudoTcpTestBase.createDummyData(sizeA); - final int sizeB = 983746; - final byte[] bufferB = PseudoTcpTestBase.createDummyData(sizeB); - final PseudoTcpSocket server = - new PseudoTcpSocketFactory().createSocket(); - server.setDebugName("L"); - server.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0)); - final InetSocketAddress serverAddress = - new InetSocketAddress(InetAddress.getLoopbackAddress(), server.getLocalPort()); - AtomicBoolean clientThreadEnded = new AtomicBoolean(); - AtomicBoolean serverThreadEnded = new AtomicBoolean(); - Thread serverThread = new Thread(() -> - { - try - { - server.accept(5000); - byte[] rcvdSingle = new byte[singleStepCount]; - // read by one byte - for (int i = 0; i < singleStepCount; i++) - rcvdSingle[i] = (byte) server.getInputStream().read(); - assertArrayEquals(bufferSingle, rcvdSingle); - // receive buffer A - byte[] recvdBufferA = - receiveBuffer(server.getInputStream(), sizeA); - assertArrayEquals(bufferA, recvdBufferA); - // receive buffer B - byte[] recvdBufferB = - receiveBuffer(server.getInputStream(), sizeB); - assertArrayEquals(bufferB, recvdBufferB); - // server.close(); - serverThreadEnded.set(true); - } - catch (IOException ex) - { - throw new RuntimeException(ex); - } - }); - - final PseudoTcpSocket client = - new PseudoTcpSocketFactory().createSocket(); - Thread clientThread = new Thread(() -> - { - try - { - client.setDebugName("R"); - client.connect(serverAddress, 5000); - OutputStream os = client.getOutputStream(); - - // write single array - for (int i = 0; i < singleStepCount; i++) - os.write(bufferSingle[i]); - - // write whole array - os.write(bufferA); - - // write by parts - int partCount = 7; - boolean notExact = sizeB % partCount != 0; - int[] partsSize = - notExact ? new int[partCount + 1] : new int[partCount]; - for (int i = 0; i < partsSize.length; i++) - { - if (notExact && i == partCount) - partsSize[i] = sizeB % partCount; - else - partsSize[i] = sizeB / partCount; - } - int written = 0; - for (int j : partsSize) - { - os.write(bufferB, written, j); - written += j; - } - assertEquals(sizeB, written); - os.flush(); - client.close(); - clientThreadEnded.set(true); - } - catch (IOException ex) - { - throw new RuntimeException(ex); - } - }); - - serverThread.start(); - clientThread.start(); - try - { - boolean success = assert_wait_until( - () -> client.getState() == PseudoTcpState.TCP_CLOSED, - transferTimeout); - if (success) - { - clientThread.join(10_000); - if (!clientThreadEnded.get()) - { - fail("client thread did not end"); - } - serverThread.join(10_000); - if (!serverThreadEnded.get()) - { - fail("server thread did not end"); - } - server.close(); - } - else - { - fail("Transfer timeout"); - } - } - catch (InterruptedException ex) - { - throw new RuntimeException(ex); - } - } - - private static byte[] receiveBuffer(InputStream input, int size) - throws IOException - { - int rcvd = 0; - byte[] buffer = new byte[size]; - rcvd += input.read(buffer); - while (rcvd != size) - { - rcvd += input.read(buffer, rcvd, size - rcvd); - if (logger.isLoggable(Level.FINER)) - { - logger.log(Level.FINER, "Received: " + rcvd); - } - } - - return buffer; - } - - /** - * Test the timeout on accept method - */ - @Test - public void testAccept() - { - assertThrows(IOException.class, ()-> - { - PseudoTcpSocketImpl server = new PseudoTcpSocketImpl(0); - server.accept(10); - }); - } - - /** - * Interface used to pass timeout test function - */ - private interface TimeoutOperationTest - { - void testTimeout(PseudoTcpSocketImpl socket) throws IOException; - } - - private void doTestTimeout(final TimeoutOperationTest testOperation) - throws Exception - { - Thread.setDefaultUncaughtExceptionHandler(this); - final PseudoTcpSocketImpl server; - final PseudoTcpSocketImpl client; - DatagramSocket serverSocket = new DatagramSocket(0, InetAddress.getLoopbackAddress()); - server = new PseudoTcpSocketImpl(0, serverSocket); - client = new PseudoTcpSocketImpl(0); - //Servers thread waiting for connection - new Thread(() -> - { - try - { - server.accept(2000); - } - catch (IOException e) - { - throw new RuntimeException(e); - } - try - { - testOperation.testTimeout(server); - fail("No expected timeout occurred on operation"); - } - catch (IOException e) - { - //success - try - { - server.close(); - } - catch (IOException exc) - { - throw new RuntimeException(exc); - } - } - }).start(); - //Clients thread connects and closes socket - new Thread(() -> - { - try - { - client.connect(new InetSocketAddress( - InetAddress.getLoopbackAddress(), - serverSocket.getLocalPort()), - 2000); - Thread.sleep(500); - client.close(); - } - catch (Exception e) - { - throw new RuntimeException(e); - } - }).start(); - //Waits for server to close socket - boolean done = assert_wait_until(() - -> server.getState() == PseudoTcpState.TCP_CLOSED, 3000); - if(!done) - { - fail("Test timed out"); - } - } - - /** - * Tests timeout on read method - */ - @Test - public void testReadTimeout() throws Exception - { - doTestTimeout(socket -> - { - socket.setPTCPOption(Option.OPT_READ_TIMEOUT, 300); - socket.getInputStream().read(new byte[500]); - }); - } - - /** - * Tests timeout on write method - */ - @Test - public void testWriteTimeout() throws Exception - { - doTestTimeout(socket -> - { - //buffer that will exceed stack's buffer size - byte[] bigBuffer = new byte[PseudoTCPBase.DEFAULT_SND_BUF_SIZE*2]; - socket.setPTCPOption(Option.OPT_WRITE_TIMEOUT, 300); - socket.getOutputStream().write(bigBuffer); - }); - } - - /** - * Tests timeout on flush method - */ - @Test - public void testFlushTimeout() throws Exception - { - doTestTimeout(socket -> - { - //buffer that will exceed stack's buffer size - byte[] buffer = new byte[PseudoTCPBase.DEFAULT_SND_BUF_SIZE]; - socket.setPTCPOption(Option.OPT_WRITE_TIMEOUT, 300); - try - { - socket.getOutputStream().write(buffer); - } - catch(IOException e) - { - throw new RuntimeException("Unexpected exception: "+e); - } - socket.getOutputStream().flush(); - }); - } -} diff --git a/src/test/java/org/ice4j/pseudotcp/PseudoTcpTestBase.java b/src/test/java/org/ice4j/pseudotcp/PseudoTcpTestBase.java deleted file mode 100755 index 0ef0c926..00000000 --- a/src/test/java/org/ice4j/pseudotcp/PseudoTcpTestBase.java +++ /dev/null @@ -1,684 +0,0 @@ -/* - * ice4j, the OpenSource Java Solution for NAT and Firewall Traversal. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ice4j.pseudotcp; - -import java.io.*; -import java.util.*; -import java.util.logging.*; - -/** - * Base class for other pseduoTCP logic tests. Runs all threads required for the - * protocol to work. There are two instances of pseudoTCP logic being run. Data - * packets between them are passes directly with some loss and/or delay - * introduced optionally. - * - * @author Pawel Domas - */ -public abstract class PseudoTcpTestBase - extends MultiThreadSupportTest - implements PseudoTcpNotify -{ - /** - * The logger. - */ - private static final Logger logger = - Logger.getLogger(PseudoTCPBase.class.getName()); - /** - * Remote peer TCP logic instance - */ - private final PseudoTCPBase remoteTcp; - /** - * Local peer TCP logic instance - */ - private final PseudoTCPBase localTcp; - private int local_mtu_; - private int remote_mtu_; - /** - * Delay in ms introduced to packets delivery - */ - private int delay_; - /** - * Simulated packets loss in % - */ - private int loss_; - /** - * Stores info about connection state for use by child classes - */ - protected boolean have_connected_; - protected boolean have_disconnected_; - /** - * Timer used to delay packets delivery - */ - private Timer timer = new Timer("Delay timer"); - /** - * Timeout for connect operation in ms - */ - static final int kConnectTimeoutMs = 5000; // ~3 * default RTO of 3000ms - //static final int kMinTransferRate = 1050000; - /** - * Transfer rate used to calculate timeout for transfer operations. This - * timeout counts before the transfer tests will fail. - */ - static final int kMinTransferRate = 1000; - /** - * Transfer blocks size - */ - static final int kBlockSize = 4096; - - public PseudoTcpTestBase() - { - this.remoteTcp = new PseudoTCPBase(this, 1); - //Debug names are usefull to identify peers in log messages - remoteTcp.debugName = "REM"; - this.localTcp = new PseudoTCPBase(this, 1); - localTcp.debugName = "LOC"; - setLocalMtu(65535); - setRemoteMtu(65535); - } - - /** - * Creates some random data array - * - * @param size - * @return - */ - static public byte[] createDummyData(int size) - { - byte[] dummy = new byte[size]; - Random r = new Random(); - r.nextBytes(dummy); - return dummy; - } - - /** - * Sets the mtu for local peer - * - * @param mtu - */ - void setLocalMtu(int mtu) - { - localTcp.notifyMTU(mtu); - local_mtu_ = mtu; - } - - /** - * Sets the mtu for remote peer - * - * @param mtu - */ - void setRemoteMtu(int mtu) - { - remoteTcp.notifyMTU(mtu); - remote_mtu_ = mtu; - } - - /** - * Sets the delay introduced to packets delivery between peers - * - * @param delay - */ - void setDelay(int delay) - { - delay_ = delay; - } - - /** - * Sets loss % of packets transferred between local and remote peers - * - * @param percent - */ - void setLoss(int percent) - { - loss_ = percent; - } - - /** - * Sets OptNagling for both local and remote peers - * - * @param enable_nagles - */ - void setOptNagling(boolean enable_nagles) - { - localTcp.setOption(Option.OPT_NODELAY, enable_nagles ? 0 : 1); - remoteTcp.setOption(Option.OPT_NODELAY, enable_nagles ? 0 : 1); - - } - - /** - * Sets ack delay option for local and remote peers - * - * @param ack_delay - */ - void setOptAckDelay(int ack_delay) - { - localTcp.setOption(Option.OPT_ACKDELAY, ack_delay); - remoteTcp.setOption(Option.OPT_ACKDELAY, ack_delay); - } - - /** - * Sets send buffer option for local and remote peers - * - * @param size - */ - void setOptSndBuf(int size) - { - localTcp.setOption(Option.OPT_SNDBUF, size); - remoteTcp.setOption(Option.OPT_SNDBUF, size); - } - - /** - * Sets receive buffer size option for remote peer - * - * @param size - */ - void setRemoteOptRcvBuf(int size) - { - remoteTcp.setOption(Option.OPT_RCVBUF, size); - } - - /** - * Sets receive buffer size option for local peer - * - * @param size - */ - void setLocalOptRcvBuf(int size) - { - localTcp.setOption(Option.OPT_RCVBUF, size); - } - - /** - * Disable window scaling for remote peer - */ - void disableRemoteWindowScale() - { - remoteTcp.disableWindowScale(); - } - - /** - * Disable window scaling for local peer - */ - void disableLocalWindowScale() - { - localTcp.disableWindowScale(); - } - - /** - * Starts the connection from local to remote peer - * - * @throws IOException - */ - void connect() throws IOException - { - localTcp.connect(); - updateLocalClock(); - } - - /** - * Closes the connection - */ - void close() - { - localTcp.close(false); - updateLocalClock(); - } - - /** - * Catches the event OnTcpOpen on the local peer and marks have_connected - * flag - * - * @param tcp - */ - @Override - public void onTcpOpen(PseudoTCPBase tcp) - { - if (tcp == localTcp) - { - have_connected_ = true; - onTcpWriteable(tcp); - } - } - - /** - * Catches OnTcpClosed event on remote peer and marks have_disconnected flag - * - * @param tcp - * @param exc - */ - @Override - public void onTcpClosed(PseudoTCPBase tcp, IOException exc) - { - assert exc == null; - if (tcp == remoteTcp) - { - have_disconnected_ = true; - } - } - /** - * Randomizer instance used to decide about packet loss - */ - private Random random = new Random(); - - int randomInt() - { - return random.nextInt(100); - } - - /** - * Send the data from local to remote peer - * - * @param data - * @param len - * @return - * @throws IOException - */ - int localSend(byte[] data, int len) throws IOException - { - return localTcp.send(data, len); - } - - /** - * Receive data as local peer - * - * @param buffer - * @param len - * @return - * @throws IOException - */ - int localRecv(byte[] buffer, int len) throws IOException - { - return localTcp.recv(buffer, len); - } - - /** - * Receive data as remote peer - * - * @param buffer - * @param len - * @return - * @throws IOException - */ - int remoteRecv(byte[] buffer, int len) throws IOException - { - - return remoteTcp.recv(buffer, len); - } - - /** - * Sends the data from remote to local peer - * - * @param data - * @param len - * @return - * @throws IOException - */ - int remoteSend(byte[] data, int len) - throws IOException - { - return remoteTcp.send(data, len); - } - - /** - * Simulates packet received by local peer - * - * @param data - * @param len - * @throws IOException - */ - private void localPacket(byte[] data, int len) - throws IOException - { - localTcp.notifyPacket(data, len); - updateLocalClock(); - } - - /** - * Simulates packet received by remote peer - * - * @param data - * @param len - * @throws IOException - */ - private void remotePacket(byte[] data, int len) - throws IOException - { - remoteTcp.notifyPacket(data, len); - updateRemoteClock(); - } - - /** - * Creates TimerTask with @link(RemotePacket) action - * - * @param data - * @param len - * @return - */ - private TimerTask getWriteRemotePacketTask(final byte[] data, final int len) - { - return new TimerTask() - { - @Override - public void run() - { - try - { - remotePacket(data, len); - } - catch (IOException ex) - { - throw new RuntimeException(ex); - } - } - }; - } - - /** - * Creates TimerTask with @link(LocalPacket) action - * - * @param data - * @param len - * @return - */ - private TimerTask getWriteLocalPacketTask(final byte[] data, final int len) - { - return new TimerTask() - { - @Override - public void run() - { - try - { - localPacket(data, len); - } - catch (IOException ex) - { - throw new RuntimeException(ex); - } - } - }; - } - - /** - * Handles passing packets between local and remote peers. Here are taken - * decisions about packets loss and delay. - * - * @param tcp - * @param buffer - * @param len - * @return - */ - @Override - public WriteResult tcpWritePacket(PseudoTCPBase tcp, byte[] buffer, int len) - { - // Randomly drop the desired percentage of packets. - // Also drop packets that are larger than the configured MTU. - if (randomInt() < loss_) - { - if (logger.isLoggable(Level.FINE)) - { - logger.log(Level.FINE, "Randomly dropping packet, size=" + len); - } - } - else - { - if (len > Math.min(local_mtu_, remote_mtu_)) - { - if (logger.isLoggable(Level.FINE)) - { - logger.log(Level.FINE, - "Dropping packet that exceeds path MTU, size=" - + len); - } - } - else - { - if (tcp == localTcp) - { - timer.schedule(getWriteRemotePacketTask(buffer, len), delay_); - } - else - { - timer.schedule(getWriteLocalPacketTask(buffer, len), delay_); - } - } - } - return WriteResult.WR_SUCCESS; - } - - /** - * Wakes up local clock thread from wait method causing forced time update - */ - protected void updateLocalClock() - { - if (localClockThread != null) - { - synchronized (localClockLock) - { - localClockLock.notifyAll(); - } - } - } - - /** - * Wakes up remote clock thread from wait method causing forced time update - */ - protected void updateRemoteClock() - { - if (remoteClockThread != null) - { - synchronized (remoteClockLock) - { - remoteClockLock.notifyAll(); - } - } - } - - /** - * Method handles time update for pseudoTCP logic class - * - * @param tcp - * @param lock - */ - private void updateNextClock(final PseudoTCPBase tcp, final Object lock) - { - try - { - - long now = PseudoTCPBase.now(); - //System.out.println(tcp.debugName + " NOTIFY CLOCK: " + now); - synchronized (tcp) - { - tcp.notifyClock(now); - } - //UpdateClock(tcp); - long interval; // NOLINT - synchronized (tcp) - { - interval = tcp.getNextClock(PseudoTCPBase.now()); - } - //interval = Math.max(interval, 0L); // sometimes interval is < 0 - if (logger.isLoggable(Level.FINEST)) - { - logger.log(Level.FINEST, - tcp.debugName + " CLOCK sleep for " + interval); - } - if (interval < 0) - { - if (interval == -1) - { - interval = 1000; - } - else - { - return; - } - } - synchronized (lock) - { - lock.wait(interval); - } - } - catch (InterruptedException ex) - { - //Logger.getLogger(PseudoTcpTestBase.class.getName()).log(Level.SEVERE, null, ex); - } - } - /** - * Local peer clock thread - */ - private Thread localClockThread; - private final Object localClockLock = new Object(); - /** - * Remote peer clock thread - */ - private Thread remoteClockThread; - private final Object remoteClockLock = new Object(); - /** - * The "run flag" for clock threads - */ - private boolean runClocks = false; - - /** - * Start clock threads - */ - protected void startClocks() - { - if (localClockThread == null && remoteClockThread == null) - { - runClocks = true; - localClockThread = new Thread(new Runnable() - { - @Override - public void run() - { - while (runClocks) - { - //localTcp.NotifyClock(PseudoTCPBase.Now()); - updateNextClock(localTcp, localClockLock); - - } - - } - }, "LocalClockThread"); - remoteClockThread = new Thread(new Runnable() - { - @Override - public void run() - { - while (runClocks) - { - //remoteTcp.NotifyClock(PseudoTCPBase.Now()); - updateNextClock(remoteTcp, remoteClockLock); - } - - } - }, "RemoteClockThread"); - localClockThread.start(); - remoteClockThread.start(); - } - else - { - throw new IllegalStateException(); - } - } - - /** - * Stops clock threads - */ - protected void stopClocks() - { - if (localClockThread != null && remoteClockThread != null) - { - try - { - runClocks = false; - localClockThread.interrupt(); - remoteClockThread.interrupt(); - localClockThread.join(5000); - localClockThread = null; - remoteClockThread.join(5000); - remoteClockThread = null; - } - catch (InterruptedException ex) - { - ex.printStackTrace(); - } - } - else - { - throw new IllegalStateException(); - } - } - - /** - * This method waits kConnectTimeoutMs miliseconds or until the - * connection has been established between local and remote peers - * - * @param kConnectTimeoutMs - * @return isDone result - */ - protected boolean assert_Connected_wait(int kConnectTimeoutMs) - { - return assert_wait_until( - () -> PseudoTcpTestBase.this.have_connected_, - kConnectTimeoutMs); - } - - /** - * This method waits kTransferTimeoutMs miliseconds or until the - * connection has been closed, which means that the data was transferred - * - * @param kTransferTimeoutMs - * @return isDone result - */ - protected boolean assert_Disconnected_wait(long kTransferTimeoutMs) - { - return assert_wait_until( - () -> PseudoTcpTestBase.this.have_disconnected_, - kTransferTimeoutMs); - } - - /** - * @return the remoteTcp - */ - PseudoTCPBase getRemoteTcp() - { - return remoteTcp; - } - - /** - * @return the localTcp - */ - PseudoTCPBase getLocalTcp() - { - return localTcp; - } - - /** - * Calculates maximum transfer time of size bytes for specified - * transfer rate - * - * @param size - * @param kBps - * @return timeout for transfer in ms(minimum 3000 ms) - */ - public long maxTransferTime(long size, long kBps) - { - long transferTout = ((size) / kBps) * 8 * 1000; - return transferTout > 3000 ? transferTout : 3000; - } -} diff --git a/src/test/java/org/ice4j/pseudotcp/PseudoTcpTestPingPong.java b/src/test/java/org/ice4j/pseudotcp/PseudoTcpTestPingPong.java deleted file mode 100755 index 0baedf87..00000000 --- a/src/test/java/org/ice4j/pseudotcp/PseudoTcpTestPingPong.java +++ /dev/null @@ -1,363 +0,0 @@ -/* - * ice4j, the OpenSource Java Solution for NAT and Firewall Traversal. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ice4j.pseudotcp; - -import java.io.*; -import java.util.logging.*; - -import static org.junit.jupiter.api.Assertions.*; - -import org.ice4j.pseudotcp.util.*; -import org.junit.jupiter.api.*; - -/** - * This class implements test for two way transfers - * - * @author Pawel Domas - */ -public class PseudoTcpTestPingPong extends PseudoTcpTestBase -{ - /** - * The logger. - */ - private static final Logger logger = - Logger.getLogger(PseudoTCPBase.class.getName()); - /** - * The sender - */ - private PseudoTCPBase sender; - /** - * The receiver - */ - private PseudoTCPBase receiver; - /** - * How much data is sent per ping - */ - private int bytesPerSend; - /** - * Iterations count - */ - private int iterationsRemaining; - - public void setBytesPerSend(int bytes_per_send) - { - this.bytesPerSend = bytes_per_send; - } - /** - * The send stream buffer - */ - ByteFifoBuffer send_stream; - /** - * The receive stream buffer - */ - ByteFifoBuffer recv_stream; - - /** - * Performs ping-pong test for iterations with packets of - * size bytes - */ - private void doTestPingPong(int size, int iterations) - { - Thread.setDefaultUncaughtExceptionHandler(this); - long start; - iterationsRemaining = iterations; - receiver = getRemoteTcp(); - sender = getLocalTcp(); - // Create some dummy data - byte[] dummy = createDummyData(size); - send_stream = new ByteFifoBuffer(size); - send_stream.write(dummy, size); - //Prepare the receive stream - recv_stream = new ByteFifoBuffer(size); - //Connect and wait until connected - start = PseudoTCPBase.now(); - startClocks(); - try - { - connect(); - } - catch (IOException ex) - { - ex.printStackTrace(); - fail(ex.getMessage()); - } - //assert Connect() == 0; - assert_Connected_wait(kConnectTimeoutMs); - // Sending will start from OnTcpWriteable and stop when the required - // number of iterations have completed. - assert_Disconnected_wait(kMinTransferRate); - long elapsed = PseudoTCPBase.now() - start; - stopClocks(); - logger.log(Level.INFO, - "Performed " + iterations + " pings in " + elapsed + " ms"); - } - - /** - * Catches onTcpReadable event for receiver - */ - @Override - public void onTcpReadable(PseudoTCPBase tcp) - { - assertEquals(receiver, tcp, "Unexpected onTcpReadable"); - try - { - // Stream bytes to the recv stream as they arrive. - readData(); - } - catch (IOException ex) - { - //will be caught by default handler and test will fail - throw new RuntimeException(ex); - } - // If we've received the desired amount of data, rewind things - // and send it back the other way! - int recvd = recv_stream.getBuffered(); - int required = send_stream.length(); - if (logger.isLoggable(Level.FINER)) - { - logger.log(Level.FINER, - "test - receivied: " + recvd + " required: " + required); - } - - if (recvd == required) - { - if (receiver == getLocalTcp() && --iterationsRemaining == 0) - { - close(); - // TODO: Fake OnTcpClosed() on the receiver for now. - onTcpClosed(getRemoteTcp(), null); - return; - } - //switches receivier with sender and performs test the other way - PseudoTCPBase tmp = receiver; - receiver = sender; - sender = tmp; - send_stream.resetReadPosition(); - send_stream.consumeWriteBuffer(send_stream.getWriteRemaining()); - recv_stream.resetWritePosition(); - onTcpWriteable(sender); - } - - } - - /** - * Catches the ontcpWriteable event for sender - */ - @Override - public void onTcpWriteable(PseudoTCPBase tcp) - { - if (tcp != sender) - { - return; - } - // Write bytes from the send stream when we can. - // Shut down when we've sent everything. - logger.log(Level.FINER, "Flow Control Lifted"); - try - { - writeData(); - } - catch (IOException ex) - { - throw new RuntimeException(ex); - } - - } - - /** - * Reads the data in loop until is something available - */ - private void readData() throws IOException - { - byte[] block = new byte[kBlockSize]; - int rcvd; - do - { - rcvd = receiver.recv(block, block.length); - if (rcvd > 0) - { - recv_stream.write(block, rcvd); - if (logger.isLoggable(Level.FINE)) - { - logger.log(Level.FINE, - "Receivied: " + recv_stream.getBuffered()); - } - } - } - while (rcvd > 0); - } - - /** - * Writes all data to the receiver - */ - private void writeData() throws IOException - { - int tosend; - int sent; - byte[] block = new byte[kBlockSize]; - do - { - tosend = bytesPerSend != 0 ? bytesPerSend : block.length; - tosend = send_stream.read(block, tosend); - if (tosend > 0) - { - sent = sender.send(block, tosend); - updateLocalClock(); - if (sent != -1) - { - if(logger.isLoggable(Level.FINE)) - { - logger.log(Level.FINE, "Sent: " + sent); - } - } - else - { - logger.log(Level.FINE, "Flow controlled"); - } - } - else - { - sent = tosend = 0; - } - } - while (sent > 0); - } - - /* - * - * Ping-pong (request/response) tests - * - */ - /** - * Test sending <= 1x MTU of data in each ping/pong. Should take <10ms. - */ - @Test - public void testPingPong1xMtu() - { - //logger.log(Level.INFO, "Test ping - pong 1xMTU"); - PseudoTcpTestPingPong test = new PseudoTcpTestPingPong(); - test.setLocalMtu(1500); - test.setRemoteMtu(1500); - test.doTestPingPong(100, 100); - } - - /** - * Test sending 2x-3x MTU of data in each ping/pong. Should take <10ms. - */ - @Test - public void testPingPong3xMtu() - { - //logger.log(Level.INFO, "Test ping - pong 3xMTU"); - PseudoTcpTestPingPong test = new PseudoTcpTestPingPong(); - test.setLocalMtu(1500); - test.setRemoteMtu(1500); - test.doTestPingPong(400, 100); - } - - /** - * Test sending 1x-2x MTU of data in each ping/pong. Should take ~1s, due to - * interaction between Nagling and Delayed ACK. - */ - @Test - public void testPingPong2xMtu() - { - //logger.log(Level.INFO, "Test ping - pong 2xMTU"); - PseudoTcpTestPingPong test = new PseudoTcpTestPingPong(); - test.setLocalMtu(1500); - test.setRemoteMtu(1500); - test.doTestPingPong(2000, 5); - } - - /** - * Test sending 1x-2x MTU of data in each ping/pong with Delayed ACK off. - * Should take <10ms. - */ - @Test - public void testPingPong2xMtuWithAckDelayOff() - { - //logger.log(Level.INFO, "Test ping - pong 2xMTU ack delay off"); - PseudoTcpTestPingPong test = new PseudoTcpTestPingPong(); - test.setLocalMtu(1500); - test.setRemoteMtu(1500); - test.setOptAckDelay(0); - test.doTestPingPong(2000, 100); - } - - /** - * Test sending 1x-2x MTU of data in each ping/pong with Nagling off. Should - * take <10ms. - */ - @Test - public void testPingPong2xMtuWithNaglingOff() - { - //logger.log(Level.INFO, "Test ping - pong 2xMTU nagling off"); - PseudoTcpTestPingPong test = new PseudoTcpTestPingPong(); - test.setLocalMtu(1500); - test.setRemoteMtu(1500); - test.setOptNagling(false); - test.doTestPingPong(2000, 5); - } - - /** - * Test sending a ping as pair of short (non-full) segments. Should take - * ~1s, due to Delayed ACK interaction with Nagling. - */ - @Test - public void testPingPongShortSegments() - { - //logger.log(Level.INFO, "Test ping - pong short segments"); - PseudoTcpTestPingPong test = new PseudoTcpTestPingPong(); - test.setLocalMtu(1500); - test.setRemoteMtu(1500); - test.setOptAckDelay(5000); - test.setBytesPerSend(50); // i.e. two Send calls per payload - test.doTestPingPong(100, 5); - } - - /** - * Test sending ping as a pair of short (non-full) segments, with Nagling - * off. Should take <10ms. - */ - @Test - public void testPingPongShortSegmentsWithNaglingOff() - { - //logger.log(Level.INFO, "Test ping - pong short segments nagling off"); - PseudoTcpTestPingPong test = new PseudoTcpTestPingPong(); - test.setLocalMtu(1500); - test.setRemoteMtu(1500); - test.setOptNagling(false); - test.setBytesPerSend(50); // i.e. two Send calls per payload - test.doTestPingPong(100, 5); - } - - /** - * Test sending <= 1x MTU of data ping/pong, in two segments, no Delayed - * ACK. Should take ~1s. - */ - @Test - public void testPingPongShortSegmentsWithAckDelayOff() - { - //logger.log(Level.INFO, "Test ping - pong short segments nagling off"); - PseudoTcpTestPingPong test = new PseudoTcpTestPingPong(); - test.setLocalMtu(1500); - test.setRemoteMtu(1500); - test.setBytesPerSend(50); // i.e. two Send calls per payload - test.setOptAckDelay(0); - test.doTestPingPong(100, 5); - } -} diff --git a/src/test/java/org/ice4j/pseudotcp/PseudoTcpTestRecvWindow.java b/src/test/java/org/ice4j/pseudotcp/PseudoTcpTestRecvWindow.java deleted file mode 100755 index cd0fdc43..00000000 --- a/src/test/java/org/ice4j/pseudotcp/PseudoTcpTestRecvWindow.java +++ /dev/null @@ -1,419 +0,0 @@ -/* - * ice4j, the OpenSource Java Solution for NAT and Firewall Traversal. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ice4j.pseudotcp; - -import java.io.*; -import java.util.*; -import java.util.logging.*; - -import static org.junit.jupiter.api.Assertions.*; - -import org.ice4j.pseudotcp.util.*; -import org.junit.jupiter.api.*; - -/** - * Fill the receiver window until it is full, drain it and then fill it with the - * same amount. This is to test that receiver window contracts and enlarges - * correctly. - * - * @author Pawel Domas - */ -public class PseudoTcpTestRecvWindow extends PseudoTcpTestBase -{ - /** - * The logger. - */ - private static final Logger logger = - Logger.getLogger(PseudoTCPBase.class.getName()); - /** - * Send data buffer - */ - private ByteFifoBuffer send_stream; - /** - * List which stores stream position after each write cycle - */ - private List send_position; - /** - * Receive data buffer - */ - private ByteFifoBuffer recv_stream; - /** - * List which stores stream position after each read cycle - */ - private List recv_position; - /** - * Times used for write operations scheduling - */ - private Timer writeTimer = new Timer("WriteTimer"); - /** - * Stores data length used for the test. - */ - private int testDataSize; - - private void doTestTransfer(int size) - { - Thread.setDefaultUncaughtExceptionHandler(this); - testDataSize = size; - long start, elapsed; - send_position = new ArrayList<>(); - recv_position = new ArrayList<>(); - // Create some dummy data - byte[] dummy = createDummyData(size); - send_stream = new ByteFifoBuffer(size); - send_stream.write(dummy, size); - //Prepare the receive stream - recv_stream = new ByteFifoBuffer(size); - //Connect and wait until connected - start = PseudoTCPBase.now(); - startClocks(); - try - { - connect(); - } - catch (IOException ex) - { - fail(ex.getMessage()); - } - //assert Connect() == 0; - //TODO: check assert result and fail - // Connect and wait until connected. - assert_Connected_wait(kConnectTimeoutMs); - - scheduleWriteAction(0); - - long transferTout = maxTransferTime(dummy.length, kMinTransferRate); - boolean transferInTime = assert_Disconnected_wait(transferTout); - elapsed = PseudoTCPBase.now() - start; - stopClocks(); - int received = recv_stream.getBuffered(); - assertTrue(transferInTime, "Transfer timeout, transferred: " + received - + " required: " + dummy.length - + " elapsed: " - + elapsed + " limit: " + transferTout); - - assert 2 == send_position.size(); - assert 2 == recv_position.size(); - - int estimated_recv_window = estimateReceiveWindowSize(); - - // The difference in consecutive send positions should equal the - // receive window size or match very closely. This verifies that receive - // window is open after receiver drained all the data. - int send_position_diff = send_position.get(1) - send_position.get(0); - assertTrue(estimated_recv_window - send_position_diff <= 1024); - - // Receiver drained the receive window twice.(+-2 because of window scaling) - assert ((recv_position.get(1) - 2 * estimated_recv_window) - <= getShadowedBytes(getRemoteScaleFactor())); - } - - /** - * This function calculates amount of bytes witch may introduce error to - * estimation of receive window size caused by scale factor. This is because - * data is being sent until all sent data is available in remote side's - * buffer. But because of scale factor window size 0 is reached earlier than - * expected and some data may still wait for window to open in the send - * queue. - * - * For example: m_rcv_scale == 1 and m_rcv_wnd < 2 then rcv_wnd == 0 (1 byte - * may block) m_rcv_scale == 2 and m_rcv_wnd < 4 then rcv_wnd == 0 (3 bytes - * may block) m_rcv_scale == 3 and m_rcv_wnd < 8 then rcv_wnd == 0 (7 bytes - * may block) and so on... - * - * In normal operation something would read data on remote side causing - * window to expand. - * - * - * - * - - * - * @return count bytes shadowed by scale actor - */ - static int getShadowedBytes(int scaleFactor) - { - return (int) (Math.pow(2, scaleFactor) - 1); - } - - /** - * Reads all data available at the buffer - */ - void readUntilIOPending() throws IOException - { - byte[] block = new byte[getRemoteTcp().getRecvBufferSize() * 2]; - int position = recv_stream.getBuffered(); - int rcvd, total = 0; - do - { - rcvd = remoteRecv(block, block.length); - if (rcvd > 0) - { - recv_stream.write(block, rcvd); - total += rcvd; - position += rcvd; - } - } - while (rcvd > 0 && total != 0); - recv_position.add(position); - - // Disconnect if we have done two transfers. - if (recv_position.size() == 2) - { - close(); - onTcpClosed(getRemoteTcp(), null); - } - else - { - writeData(); - } - } - - /** - * Schedules write operation with delay given in ms - */ - void scheduleWriteAction(long delay) - { - writeTimer.schedule(new TimerTask() - { - @Override - public void run() - { - try - { - writeData(); - } - catch (IOException ex) - { - //it will get cought by - //deafult exception handler in PseudoTcpTestBase - throw new RuntimeException(ex); - } - } - }, delay); - } - - /** - * Writes the data - */ - void writeData() throws IOException - { - //writeOpCount++; - int tosend; - int sent; - int totalSent = 0; - byte[] block = new byte[getRemoteTcp().getRecvBufferSize() * 2]; - int position = testDataSize - send_stream.getBuffered(); - synchronized (getLocalTcp()) - { - do - { - tosend = send_stream.readOffset(block, 0, block.length, 0); - if (tosend > 0) - { - sent = localSend(block, tosend); - updateLocalClock(); - if (sent > 0) - { - totalSent += sent; - send_stream.consumeReadData(sent); - position += sent; - } - else - { - logger.log(Level.FINE, "Flow Controlled"); - } - } - else - { - sent = tosend = 0; - } - } - while (sent > 0); - //position = send_stream.GetBuffered(); - - } - // Measured with precision according to window scale option used - if (totalSent - getRemoteTcp().getAvailable() - > getShadowedBytes(getRemoteScaleFactor())) - { - //send buffer was fully filled - //waits until it will be received by remote peer - while (totalSent - getRemoteTcp().getAvailable() - > getShadowedBytes(getRemoteScaleFactor()) - && !getRemoteTcp().isReceiveBufferFull()) - { - try - { - Thread.sleep(50); - if (logger.isLoggable(Level.FINER)) - { - logger.log(Level.FINER, - "Waiting... sent: " + totalSent + " avail: " - + getRemoteTcp().getAvailable() + " buffered not sent: " - + getLocalTcp().getBytesBufferedNotSent() - + " isFull? " + getRemoteTcp().isReceiveBufferFull()); - } - } - catch (InterruptedException ex) - { - throw new RuntimeException(ex); - } - } - } - send_position.add(position); - writeTimer.schedule(new TimerTask() - { - @Override - public void run() - { - try - { - readUntilIOPending(); - } - catch (IOException ex) - { - throw new RuntimeException(ex); - } - } - }, 10); - } - - /** - * - * @return estimated receive window size - */ - int estimateReceiveWindowSize() - { - return recv_position.get(0); - } - - /** - * - * @return estimated send window size - */ - int estimateSendWindowSize() - { - return send_position.get(0); - } - - @Override - public void onTcpReadable(PseudoTCPBase tcp) - { - } - - @Override - public void onTcpWriteable(PseudoTCPBase tcp) - { - } - - void setLocalOptSndBuf(int len) - { - getLocalTcp().setOption(Option.OPT_SNDBUF, len); - } - - int getRemoteScaleFactor() - { - return getRemoteTcp().getM_rwnd_scale(); - } - - @Test - public void testGetShadowedBytes() - { - assert (PseudoTcpTestRecvWindow.getShadowedBytes(0) == 0); - assert (PseudoTcpTestRecvWindow.getShadowedBytes(1) == 1); - assert (PseudoTcpTestRecvWindow.getShadowedBytes(2) == 3); - assert (PseudoTcpTestRecvWindow.getShadowedBytes(3) == 7); - assert (PseudoTcpTestRecvWindow.getShadowedBytes(4) == 15); - assert (PseudoTcpTestRecvWindow.getShadowedBytes(11) == 2047); - assert (PseudoTcpTestRecvWindow.getShadowedBytes(14) == 16383); - } - - /** - * Test that receive window expands and contract correctly. - */ - @Test - public void testReceiveWindow() - { - //logger.log(Level.INFO, "Test receive window"); - PseudoTcpTestRecvWindow test = new PseudoTcpTestRecvWindow(); - test.setLocalMtu(1500); - test.setRemoteMtu(1500); - test.setOptNagling(false); - test.setOptAckDelay(0); - test.doTestTransfer(1024 * 1000); - } - - /** - * Test setting send window size to a very small value. - */ - @Test - public void testSetVerySmallSendWindowSize() - { - //TODO: finish test - logger.log(Level.INFO, "Test very small receive window"); - PseudoTcpTestRecvWindow test = new PseudoTcpTestRecvWindow(); - test.setLocalMtu(1500); - test.setRemoteMtu(1500); - test.setOptNagling(false); - test.setOptAckDelay(0); - test.setOptSndBuf(900); - test.doTestTransfer(1024 * 1000); - assertEquals(900, test.estimateSendWindowSize()); - } - - /** - * Test setting receive window size to a value other than default. - */ - @Test - public void testSetReceiveWindowSize() - { - //logger.log(Level.INFO, "Test set receive window size"); - PseudoTcpTestRecvWindow test = new PseudoTcpTestRecvWindow(); - test.setLocalMtu(1500); - test.setRemoteMtu(1500); - test.setOptNagling(false); - test.setOptAckDelay(0); - int wndSize = 300000; - // if window scaling is not supported by either local or remote, use - // default size - if (!test.getLocalTcp().m_support_wnd_scale || - !test.getRemoteTcp().m_support_wnd_scale) - { - wndSize = 65535; - } - test.setLocalOptSndBuf(wndSize); - test.setRemoteOptRcvBuf(wndSize); - int wndScale = test.getRemoteScaleFactor(); - //logger.log(Level.INFO, "Using scale factor: {0}", wndScale); - test.doTestTransfer(1024 * 3000); - //beacuse there may be situations - //when 1 byte may be waiting in send queue - //before - //scaling factor == 1 not allows to determine exact window size (+-1) - assert (wndSize - test.estimateReceiveWindowSize() - <= PseudoTcpTestRecvWindow.getShadowedBytes(wndScale)); - } - - /* - * Test sending data with mismatched MTUs. We should detect this and reduce - * // our packet size accordingly. // TODO: This doesn't actually work right - * now. The current code // doesn't detect if the MTU is set too high on - * either side. TEST_F(PseudoTcpTest, TestSendWithMismatchedMtus) { - * SetLocalMtu(1500); SetRemoteMtu(1280); TestTransfer(1000000); } - */ -} diff --git a/src/test/java/org/ice4j/pseudotcp/PseudoTcpTestTransfer.java b/src/test/java/org/ice4j/pseudotcp/PseudoTcpTestTransfer.java deleted file mode 100755 index 232df670..00000000 --- a/src/test/java/org/ice4j/pseudotcp/PseudoTcpTestTransfer.java +++ /dev/null @@ -1,456 +0,0 @@ -/* - * ice4j, the OpenSource Java Solution for NAT and Firewall Traversal. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ice4j.pseudotcp; - -import static org.junit.jupiter.api.Assertions.*; - -import java.io.*; -import java.util.logging.*; -import org.junit.jupiter.api.*; - -/** - * Implements one way transfer test - * - * @author Pawel Domas - */ -public class PseudoTcpTestTransfer extends PseudoTcpTestBase -{ - /** - * The logger. - */ - private static final Logger logger = - Logger.getLogger(PseudoTCPBase.class.getName()); - - /** - * The send data - */ - private byte[] sendData; - /** - * Send stream size - */ - private int sendStreamSize; - /** - * Total bytes sent counter - */ - private int totalSent; - /** - * Receive stream - */ - private ByteArrayOutputStream recvStream; - - /** - * Transfers the data of size bytes - */ - private void doTestTransfer(int size) - { - Thread.setDefaultUncaughtExceptionHandler(this); - long start, elapsed; - int received; - // Create some dummy data to send - sendData = createDummyData(size); - sendStreamSize = size; - - // Prepare the receive stream. - recvStream = new ByteArrayOutputStream(size); - // Connect and wait until connected. - start = PseudoTCPBase.now(); - startClocks(); - try - { - connect(); - } - catch (IOException ex) - { - fail(ex.getMessage()); - } - - assert_Connected_wait(kConnectTimeoutMs); - // Sending will start from OnTcpWriteable and complete when all data has - // been received. - long transferTout = maxTransferTime(sendData.length, kMinTransferRate); - boolean transferInTime = assert_Disconnected_wait(transferTout); - elapsed = PseudoTCPBase.now() - start; - stopClocks(); - received = recvStream.size(); - assertTrue(transferInTime,"Transfer timeout, transferred: " + received - + " required: " + sendData.length - + " elapsed: " - + elapsed + " limit: " + transferTout); - - // Ensure we closed down OK and we got the right data. - assertEquals(size, received); - byte[] recvdArray = recvStream.toByteArray(); - assertArrayEquals(sendData, recvdArray); - - logger.log(Level.INFO, - "Transferred " + received + " bytes in " + elapsed - + " ms (" + (size * 8 / elapsed) + " Kbps"); - - } - - /** - * Reads all data available at remote peer's buffer - */ - void readData() throws IOException - { - byte[] block = new byte[kBlockSize]; - int rcvd; - do - { - rcvd = remoteRecv(block, block.length); - updateRemoteClock(); - if (rcvd != -1) - { - recvStream.write(block, 0, rcvd); - } - } - while (rcvd > 0); - } - - /** - * Writes the data until there's space available - * - * @return true if there's no more data left to write - */ - boolean writeData() throws IOException - { - int tosend; - int sent; - byte[] block = new byte[kBlockSize]; - do - { - tosend = Math.min(sendStreamSize - totalSent, block.length); - System.arraycopy(sendData, totalSent, block, 0, tosend); - if (tosend > 0) - { - sent = localSend(block, tosend); - updateLocalClock(); - if (sent != -1) - { - totalSent += sent; - } - else - { - logger.log(Level.FINE, "Flow Controlled"); - } - } - else - { - sent = tosend = 0; - } - } - while (sent > 0); - - if (logger.isLoggable(Level.FINER)) - { - logger.log(Level.FINER, "Sent: " + totalSent - + " remaining: " + (sendStreamSize - totalSent)); - } - - return tosend == 0; - } - - /** - * Catches TCP readable event for remote peer and reads the data. When total - * read count equals send data size the test is finished. - */ - @Override - public void onTcpReadable(PseudoTCPBase tcp) - { - if (tcp == getRemoteTcp()) - { - try - { - readData(); - // TODO: OnTcpClosed() is currently only notified on error - - // there is no on-the-wire equivalent of TCP FIN. - // So we fake the notification when all the data has been read. - int received, required; - received = recvStream.size(); - required = sendStreamSize; - if (logger.isLoggable(Level.FINER)) - { - logger.log(Level.FINER, "Receivied: " + received - + " required: " + required); - } - if (received == required) - { - onTcpClosed(getRemoteTcp(), null); - } - } - catch (IOException ex) - { - throw new RuntimeException(ex); - } - } - } - - /** - * Catches on TCP writeable event for local peer. Writes all data and closes - * the stream - */ - @Override - public void onTcpWriteable(PseudoTCPBase tcp) - { - if (tcp == getLocalTcp()) - { - // Write bytes from the send stream when we can. - // Shut down when we've sent everything. - logger.log(Level.FINER, "Flow Control Lifted"); - try - { - if (writeData()) - { - close(); - } - } - catch (IOException ex) - { - ex.printStackTrace(); - fail(ex.getMessage()); - } - } - } - - /** - * Basic end-to-end data transfer tests Test the normal case of sending data - * from one side to the other. - */ - @Test - public void testSend() - { - //logger.log(Level.INFO, "Test send"); - PseudoTcpTestTransfer test = new PseudoTcpTestTransfer(); - test.setLocalMtu(1500); - test.setRemoteMtu(1500); - test.doTestTransfer(1000000); - } - - /** - * Test sending data with a 50 ms RTT. Transmission should take longer due - * to a slower ramp-up in send rate. - */ - @Test - public void testSendWithDelay() - { - //logger.log(Level.INFO, "Test send with delay"); - PseudoTcpTestTransfer test = new PseudoTcpTestTransfer(); - test.setLocalMtu(1500); - test.setRemoteMtu(1500); - test.setDelay(50); - test.doTestTransfer(1000000); - } - - /** - * Test sending data with packet loss. Transmission should take much longer - * due to send back-off when loss occurs. - */ - @Test - public void testSendWithLoss() - { - //logger.log(Level.INFO, "Test send with loss"); - PseudoTcpTestTransfer test = new PseudoTcpTestTransfer(); - test.setLocalMtu(1500); - test.setRemoteMtu(1500); - test.setLoss(10); - test.doTestTransfer(100000); // less data so test runs faster - } - - /** - * Test sending data with a 50 ms RTT and 10% packet loss. Transmission - * should take much longer due to send back-off and slower detection of - * loss. - */ - @Test - public void testSendWithDelayAndLoss() - { - //logger.log(Level.INFO, "Test send with delay and loss"); - PseudoTcpTestTransfer test = new PseudoTcpTestTransfer(); - test.setLocalMtu(1500); - test.setRemoteMtu(1500); - test.setDelay(50); - test.setLoss(10); - test.doTestTransfer(100000); // less data so test runs faster - } - - /** - * Test sending data with 10% packet loss and Nagling disabled. Transmission - * should take about the same time as with Nagling enabled. - */ - @Test - public void testSendWithLossAndOptNaglingOff() - { - //logger.log(Level.INFO, "Test send with loss and OptNagling off"); - PseudoTcpTestTransfer test = new PseudoTcpTestTransfer(); - test.setLocalMtu(1500); - test.setRemoteMtu(1500); - test.setLoss(10); - test.setOptNagling(false); - test.doTestTransfer(100000); // less data so test runs faster - } - - /** - * Test sending data with 10% packet loss and Delayed ACK disabled. - * Transmission should be slightly faster than with it enabled. - */ - @Test - public void testSendWithLossAndOptAckDelayOff() - { - //logger.log(Level.INFO, "Test send with loss and OptAckDelay off"); - PseudoTcpTestTransfer test = new PseudoTcpTestTransfer(); - test.setLocalMtu(1500); - test.setRemoteMtu(1500); - test.setLoss(10); - test.setOptAckDelay(0); - test.doTestTransfer(100000); - } - - /** - * Test sending data with 50ms delay and Nagling disabled. - */ - @Test - public void testSendWithDelayAndOptNaglingOff() - { - //logger.log(Level.INFO, "Test send with delay and OptNagling off"); - PseudoTcpTestTransfer test = new PseudoTcpTestTransfer(); - test.setLocalMtu(1500); - test.setRemoteMtu(1500); - test.setDelay(50); - test.setOptNagling(false); - test.doTestTransfer(100000); // less data so test runs faster - } - - /** - * Test sending data with 50ms delay and Delayed ACK disabled. - */ - @Test - public void testSendWithDelayAndOptAckDelayOff() - { - //logger.log(Level.INFO, "Test send with delay and OptAckDelay off"); - PseudoTcpTestTransfer test = new PseudoTcpTestTransfer(); - test.setLocalMtu(1500); - test.setRemoteMtu(1500); - test.setDelay(50); - test.setOptAckDelay(0); - test.doTestTransfer(100000); // less data so test runs faster - } - - /** - * Test a large receive buffer with a sender that doesn't support scaling. - */ - @Test - public void testSendRemoteNoWindowScale() - { - //logger.log(Level.INFO, "Test send - remote no window scale"); - PseudoTcpTestTransfer test = new PseudoTcpTestTransfer(); - test.setLocalMtu(1500); - test.setRemoteMtu(1500); - test.setLocalOptRcvBuf(100000); - test.disableRemoteWindowScale(); - test.doTestTransfer(1000000); - } - - /** - * Test a large sender-side receive buffer with a receiver that doesn't - * support scaling. - */ - @Test - public void testSendLocalNoWindowScale() - { - //logger.log(Level.INFO, "Test send - local no window scale"); - PseudoTcpTestTransfer test = new PseudoTcpTestTransfer(); - test.setLocalMtu(1500); - test.setRemoteMtu(1500); - test.setRemoteOptRcvBuf(100000); - test.disableLocalWindowScale(); - test.doTestTransfer(1000000); - } - - /** - * Test when both sides use window scaling. - */ - @Test - public void testSendBothUseWindowScale() - { - //logger.log(Level.INFO, "Test send - both use window scale"); - PseudoTcpTestTransfer test = new PseudoTcpTestTransfer(); - test.setLocalMtu(1500); - test.setRemoteMtu(1500); - test.setRemoteOptRcvBuf(100000); - test.setLocalOptRcvBuf(100000); - test.doTestTransfer(1000000); - } - - /** - * Test using a large window scale value. - */ - @Test - public void testSendLargeInFlight() - { - //logger.log(Level.INFO, "Test send large in flight"); - PseudoTcpTestTransfer test = new PseudoTcpTestTransfer(); - test.setLocalMtu(1500); - test.setRemoteMtu(1500); - test.setRemoteOptRcvBuf(100000); - test.setLocalOptRcvBuf(100000); - test.setOptSndBuf(150000); - test.doTestTransfer(1000000); - } - - @Test - public void testSendBothUseLargeWindowScale() - { - //logger.log(Level.INFO, "Test send both use large window scale"); - PseudoTcpTestTransfer test = new PseudoTcpTestTransfer(); - test.setLocalMtu(1500); - test.setRemoteMtu(1500); - test.setRemoteOptRcvBuf(1000000); - test.setLocalOptRcvBuf(1000000); - test.doTestTransfer(10000000); - } - - /** - * Test using a small receive buffer. - */ - @Test - public void testSendSmallReceiveBuffer() - { - //logger.log(Level.INFO, "Test send small receive buffer"); - PseudoTcpTestTransfer test = new PseudoTcpTestTransfer(); - test.setLocalMtu(1500); - test.setRemoteMtu(1500); - test.setRemoteOptRcvBuf(10000); - test.setLocalOptRcvBuf(10000); - test.doTestTransfer(1000000); - } - - /** - * Test using a very small receive buffer. - */ - @Test - public void testSendVerySmallReceiveBuffer() - { - //logger.log(Level.INFO, "Test send very small receive buffer"); - PseudoTcpTestTransfer test = new PseudoTcpTestTransfer(); - test.setLocalMtu(1500); - test.setRemoteMtu(1500); - test.setRemoteOptRcvBuf(100); - test.setLocalOptRcvBuf(100); - test.doTestTransfer(100000); - } -} diff --git a/src/test/java/org/ice4j/pseudotcp/util/ByteFifoBufferTest.java b/src/test/java/org/ice4j/pseudotcp/util/ByteFifoBufferTest.java deleted file mode 100755 index b48460e4..00000000 --- a/src/test/java/org/ice4j/pseudotcp/util/ByteFifoBufferTest.java +++ /dev/null @@ -1,364 +0,0 @@ -/* - * ice4j, the OpenSource Java Solution for NAT and Firewall Traversal. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ice4j.pseudotcp.util; - -import java.nio.*; -import java.util.*; - -import static org.junit.jupiter.api.Assertions.*; - -import org.junit.jupiter.api.*; - -/** - * - * @author Pawel Domas - */ -public class ByteFifoBufferTest -{ - public ByteFifoBufferTest() - { - } - - /** - * Test of Length method, of class ByteFifoBuffer. - */ - @Test - public void testLength() - { - int expResult = 1000; - ByteFifoBuffer instance = new ByteFifoBuffer(expResult); - assertEquals(expResult, instance.length()); - int wSize = 100; - instance.write(getWData(wSize), wSize); - int result = instance.length(); - assertEquals(expResult, result); - } - - /** - * Test of Read method, of class ByteFifoBuffer. - */ - @Test - public void testRead() - { - int count = 1024; - byte[] wData = getWData(count); - ByteFifoBuffer instance = new ByteFifoBuffer(count); - instance.write(wData, count); - - byte[] readBuff = new byte[count]; - int result = instance.read(readBuff, count); - assertEquals(count, result); - assertArrayEquals(wData, readBuff); - - } - - /** - * Tests reading with an offset for destination buffer - */ - @Test - public void testReadWithOffset() - { - int count = 1024; - byte[] wData = getWData(count); - ByteFifoBuffer instance = new ByteFifoBuffer(count); - instance.write(wData, count); - - byte[] readBuff = new byte[count]; - int expResult = count / 2; - int result = instance.read(readBuff, count / 2); - assertEquals(expResult, result); - - result = instance.read(readBuff, count / 2, count / 2); - assertEquals(expResult, result); - - assertArrayEquals(wData, readBuff); - - } - - /** - * return some random array - * - * @param count array size - */ - private byte[] getWData(int count) - { - Random r = new Random(); - byte[] res = new byte[count]; - r.nextBytes(res); - return res; - } - - /** - * Test of GetWriteRemaining method, of class ByteFifoBuffer. - */ - @Test - public void testGetWriteRemaining() - { - int len = 100; - ByteFifoBuffer instance = new ByteFifoBuffer(len); - int expResult = len; - int result = instance.getWriteRemaining(); - assertEquals(expResult, result); - - int w_size = 23; - byte[] w_data = getWData(w_size); - instance.write(w_data, w_size); - - expResult = len - w_size; - result = instance.getWriteRemaining(); - assertEquals(expResult, result); - } - - /** - * Test of GetBuffered method, of class ByteFifoBuffer. - */ - @Test - public void testGetBuffered() - { - int len = 1000; - ByteFifoBuffer instance = new ByteFifoBuffer(len); - int w_len = 100; - byte[] w_data = getWData(w_len); - - instance.write(w_data, w_len); - - int expResult = w_len; - int result = instance.getBuffered(); - assertEquals(expResult, result); - int consume = 5; - expResult = w_len + consume; - instance.consumeWriteBuffer(consume); - result = instance.getBuffered(); - assertEquals(expResult, result); - } - - /** - * Test of Write method, of class ByteFifoBuffer. - */ - @Test - public void testWrite() - { - int len = 2048; - byte[] data = getWData(len); - ByteFifoBuffer instance = new ByteFifoBuffer(len); - int result = instance.write(data, len); - assertEquals(len, result); - - byte[] read = new byte[len]; - int readCount = instance.read(read, len); - assertEquals(result, readCount); - assertArrayEquals(data, read); - } - - @Test - public void testWriteWithOffset() - { - int len = 2048; - byte[] data = getWData(len); - ByteFifoBuffer instance = new ByteFifoBuffer(len); - int expResult = len / 2; - int result = instance.write(data, 0, len / 2); - assertEquals(expResult, result); - result = instance.write(data, len / 2, len / 2); - assertEquals(expResult, result); - - byte[] read = new byte[len]; - int readCount = instance.read(read, len); - assertEquals(len, readCount); - assertArrayEquals(data, read); - } - - /** - * Test of ConsumeWriteBuffer method, of class ByteFifoBuffer. - */ - @Test - public void testConsumeWriteBuffer() - { - int len = 100; - ByteFifoBuffer instance = new ByteFifoBuffer(len); - instance.consumeWriteBuffer(len / 2); - instance.consumeWriteBuffer(len / 2); - try - { - instance.consumeWriteBuffer(1); - fail(); - } - catch (BufferOverflowException e) - { - } - - instance = new ByteFifoBuffer(len); - instance.consumeWriteBuffer(95); - instance.consumeReadData(40); - instance.consumeWriteBuffer(20); - - } - - /** - * Test of SetCapacity method, of class ByteFifoBuffer. - */ - @Test - public void testSetCapacity() - { - int old_size = 100; - int new_size = 200; - ByteFifoBuffer instance = new ByteFifoBuffer(old_size); - boolean expResult = true; - instance.write(getWData(old_size), old_size); - boolean result = instance.setCapacity(new_size); - assertEquals(expResult, result); - - expResult = false; - instance.resetWritePosition(); - instance.write(getWData(new_size), new_size); - result = instance.setCapacity(old_size); - assertEquals(expResult, result); - - } - - /** - * Test of ConsumeReadData method, of class ByteFifoBuffer. - */ - @Test - public void testConsumeReadData() - { - int lCount = 100; - ByteFifoBuffer instance = new ByteFifoBuffer(lCount); - instance.write(getWData(lCount), lCount); - instance.consumeReadData(lCount / 2); - instance.consumeReadData(lCount / 2); - try - { - instance.consumeReadData(1); - fail(); - } - catch (BufferUnderflowException e) - { - } - - } - - /** - * Test of ReadOffset method, of class ByteFifoBuffer. - */ - @Test - public void testReadOffset() - { - int dst_buff_offset = 0; - int len = 100; - byte[] src_buff = getWData(len); - byte[] dst_buff = new byte[len]; - int offset = 0; - ByteFifoBuffer instance = new ByteFifoBuffer(len); - instance.write(src_buff, len); - int result = - instance.readOffset(dst_buff, dst_buff_offset, len, offset); - assertEquals(len, result); - assertArrayEquals(dst_buff, src_buff); - - } - - /** - * Test of WriteOffset method, of class ByteFifoBuffer. - */ - @Test - public void testWriteOffset() - { - int len = 200; - int dataLen = 100; - byte[] srcData = getWData(dataLen); - byte[] data = new byte[dataLen]; - int nOffset = 10; - ByteFifoBuffer instance = new ByteFifoBuffer(len); - int result = instance.writeOffset(srcData, dataLen, nOffset); - int readCount = instance.readOffset(data, 0, dataLen, nOffset); - assertEquals(result, readCount); - assertArrayEquals(srcData, data); - - byte[] halfFilled = new byte[dataLen * 2]; - System.arraycopy(srcData, 0, halfFilled, dataLen, dataLen); - byte[] halfFilledRead = new byte[dataLen * 2]; - instance.readOffset(halfFilledRead, dataLen, dataLen, nOffset); - assertArrayEquals(halfFilled, halfFilledRead); - - // case when w_pos+offset exceeds current backing array length - instance = new ByteFifoBuffer(len); - instance.write(srcData, dataLen); - instance.write(srcData, dataLen / 2);// current writePos = 150 - instance.read(data, dataLen);// curretn readPos = 100 - instance.writeOffset(srcData, dataLen, 50); - - instance = new ByteFifoBuffer(61440); - instance.writeOffset(getWData(1384), 1384, 31832); - - } - - @Test - public void testWriteReadWriteRead() - { - int len = 2000; - ByteFifoBuffer instance = new ByteFifoBuffer(len); - byte[] wrData = getWData(len * 2); - int written = 0; - byte[] readBuff = new byte[wrData.length]; - int read = 0; - do - { - int wrRemaining = instance.getWriteRemaining(); - if (wrRemaining > 0 && written < wrData.length) - { - int wrCount = instance.writeOffset(wrData, wrRemaining, 0); - instance.consumeWriteBuffer(wrCount); - written += wrCount; - } - int readAvailable = instance.getBuffered(); - if (readAvailable > 0) - { - int rCount = - instance.readOffset(readBuff, read, readAvailable, 0); - instance.consumeReadData(rCount); - read += rCount; - } - } - while ((read != wrData.length) || (written != wrData.length)); - } - - @Test - public void testSomeMultiTest() - { - int Alen = 16; - int Blen = 32; - int Clen = 64; - int Dlen = 256; - int len = Alen + Blen + Clen + Dlen; - ByteFifoBuffer fifo = new ByteFifoBuffer(len); - byte[] A = getWData(Alen); - /*byte[] B =*/ getWData(Blen); - /*byte[] C =*/ getWData(Clen); - /*byte[] D =*/ getWData(Dlen); - byte[] Aread = getWData(Alen); - /*byte[] Bread =*/ getWData(Blen); - /*byte[] Cread =*/ getWData(Clen); - /*byte[] Dread =*/ getWData(Dlen); - - fifo.writeOffset(A, Alen, 0); - fifo.consumeWriteBuffer(Alen); - fifo.readOffset(Aread, 0, Alen, 0); - assertArrayEquals(A, Aread); - } -} diff --git a/src/test/java/test/IcePseudoTcp.java b/src/test/java/test/IcePseudoTcp.java deleted file mode 100755 index 454f5c7c..00000000 --- a/src/test/java/test/IcePseudoTcp.java +++ /dev/null @@ -1,438 +0,0 @@ -/* - * ice4j, the OpenSource Java Solution for NAT and Firewall Traversal. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package test; - -import java.beans.*; -import java.io.*; -import java.net.*; -import java.util.logging.*; - -import org.ice4j.*; -import org.ice4j.ice.*; -import org.ice4j.ice.harvest.*; -import org.ice4j.pseudotcp.*; -import org.ice4j.security.*; - -/** - * Sample program which first uses ICE to discover UDP connectivity. After that - * selected cadidates are used by "remote" and "local" pseudoTCP peers to - * transfer some test data. - * - * @author Pawel Domas - */ -public class IcePseudoTcp -{ - /** - * The logger. - */ - private static final Logger logger = Logger.getLogger(IcePseudoTcp.class.getName()); - private static long startTime; - /** - * Local job thread variable - */ - private static LocalPseudoTcpJob localJob = null; - /** - * Remote job thread variable - */ - private static RemotePseudoTcpJob remoteJob = null; - /** - * Test data size - */ - private static final int TEST_BYTES_COUNT = 15000000; - /** - * Flag inidcates if STUN should be used - */ - private static final boolean USE_STUN = true; - /** - * Flag inidcates if TURN should be used - */ - private static final boolean USE_TURN = true; - /** - * Monitor object used to wait for remote agent to finish it's job - */ - private static final Object remoteAgentMonitor = new Object(); - /** - * Monitor object used to wait for local agent to finish it's job - */ - private static final Object localAgentMonitor = new Object(); - /** - * Timeout for ICE discovery - */ - private static long agentJobTimeout = 15000; - - protected static Agent createAgent(int pTcpPort) - throws Throwable - { - Agent agent = new Agent(); - // STUN - if (USE_STUN) - { - StunCandidateHarvester stunHarv = new StunCandidateHarvester( - new TransportAddress("sip-communicator.net", - 3478, Transport.UDP)); - StunCandidateHarvester stun6Harv = new StunCandidateHarvester( - new TransportAddress("ipv6.sip-communicator.net", - 3478, Transport.UDP)); - - agent.addCandidateHarvester(stunHarv); - agent.addCandidateHarvester(stun6Harv); - } - // TURN - if (USE_TURN) - { - String[] hostnames = new String[] - { - "130.79.90.150", - "2001:660:4701:1001:230:5ff:fe1a:805f" - }; - int port = 3478; - LongTermCredential longTermCredential = new LongTermCredential( - "guest", "anonymouspower!!"); - - for (String hostname : hostnames) - { - agent.addCandidateHarvester(new TurnCandidateHarvester( - new TransportAddress(hostname, port, Transport.UDP), longTermCredential)); - } - } - //STREAM - createStream(pTcpPort, "data", agent); - - return agent; - } - - private static IceMediaStream createStream(int pTcpPort, - String streamName, - Agent agent) - throws Throwable - { - IceMediaStream stream = agent.createMediaStream(streamName); - - long startTime = System.currentTimeMillis(); - - //udp component - agent.createComponent(stream, pTcpPort, pTcpPort, pTcpPort + 100); - - long endTime = System.currentTimeMillis(); - logger.info("UDP Component created in " + (endTime - startTime) + " ms"); - - return stream; - } - - private static final class LocalIceProcessingListener - implements PropertyChangeListener - { - /** - * System.exit()s as soon as ICE processing enters a final state. - * - * @param evt the {@link PropertyChangeEvent} containing the old and new - * states of ICE processing. - */ - public void propertyChange(PropertyChangeEvent evt) - { - long processingEndTime = System.currentTimeMillis(); - - Object iceProcessingState = evt.getNewValue(); - - logger.info("Local agent entered the " + iceProcessingState + " state."); - if (iceProcessingState == IceProcessingState.COMPLETED) - { - logger.info("Local - Total ICE processing time: " + (processingEndTime - startTime) + "ms"); - Agent agent = (Agent) evt.getSource(); - logger.info("Local: Create pseudo tcp stream"); - IceMediaStream dataStream = agent.getStream("data"); - Component udpComponent = dataStream.getComponents().get(0); - CandidatePair selectedPair = udpComponent.getSelectedPair(); - if (selectedPair != null) - { - LocalCandidate localCandidate - = selectedPair.getLocalCandidate(); - Candidate remoteCandidate - = selectedPair.getRemoteCandidate(); - logger.info("Local: " + localCandidate); - logger.info("Remote: " + remoteCandidate); - try - { - localJob = new LocalPseudoTcpJob(selectedPair.getDatagramSocket()); - } - catch (UnknownHostException ex) - { - logger.severe("Error while trying to create local pseudotcp thread " + ex); - } - } - else - { - logger.info("Failed to select any candidate pair"); - } - } - else - { - if (iceProcessingState == IceProcessingState.TERMINATED - || iceProcessingState == IceProcessingState.FAILED) - { - /* - * Though the process will be instructed to die, demonstrate - * that Agent instances are to be explicitly prepared for - * garbage collection. - */ - if ((localJob != null) - && (iceProcessingState == IceProcessingState.TERMINATED)) - { - localJob.start(); - } - synchronized (localAgentMonitor) - { - localAgentMonitor.notifyAll(); - } - } - } - } - } - - private static final class RemoteIceProcessingListener - implements PropertyChangeListener - { - /** - * System.exit()s as soon as ICE processing enters a final state. - * - * @param evt the {@link PropertyChangeEvent} containing the old and new - * states of ICE processing. - */ - public void propertyChange(PropertyChangeEvent evt) - { - long processingEndTime = System.currentTimeMillis(); - - Object iceProcessingState = evt.getNewValue(); - - logger.info("Remote agent entered the " + iceProcessingState + " state."); - if (iceProcessingState == IceProcessingState.COMPLETED) - { - logger.info("Remote: Total ICE processing time: " + (processingEndTime - startTime) + " ms "); - Agent agent = (Agent) evt.getSource(); - - logger.info("Remote: Create pseudo tcp stream"); - IceMediaStream dataStream = agent.getStream("data"); - Component udpComponent = dataStream.getComponents().get(0); - CandidatePair usedPair = udpComponent.getSelectedPair(); - if (usedPair != null) - { - LocalCandidate localCandidate - = usedPair.getLocalCandidate(); - Candidate remoteCandidate - = usedPair.getRemoteCandidate(); - logger.info("Remote: Local address " + localCandidate); - logger.info("Remote: Peer address " + remoteCandidate); - try - { - remoteJob = new RemotePseudoTcpJob( - usedPair.getDatagramSocket(), - remoteCandidate.getTransportAddress()); - } - catch (UnknownHostException ex) - { - logger.severe("Error while trying to create remote pseudotcp thread " + ex); - } - } - else - { - logger.severe("Remote: Failed to select any candidate pair"); - } - } - else - { - if (iceProcessingState == IceProcessingState.TERMINATED - || iceProcessingState == IceProcessingState.FAILED) - { - /* - * Though the process will be instructed to die, demonstrate - * that Agent instances are to be explicitly prepared for - * garbage collection. - */ - if ((remoteJob != null) - && (iceProcessingState - == IceProcessingState.TERMINATED)) - { - remoteJob.start(); - } - synchronized (remoteAgentMonitor) - { - remoteAgentMonitor.notifyAll(); - } - } - } - } - } - - public static void main(String[] args) throws Throwable - { - startTime = System.currentTimeMillis(); - - int localPort = 7999; - int remotePort = 6000; - - Agent localAgent = createAgent(localPort); - localAgent.setNominationStrategy(NominationStrategy.NOMINATE_HIGHEST_PRIO); - Agent remotePeer = createAgent(remotePort); - - localAgent.addStateChangeListener(new IcePseudoTcp.LocalIceProcessingListener()); - remotePeer.addStateChangeListener(new IcePseudoTcp.RemoteIceProcessingListener()); - - //let them fight ... fights forge character. - localAgent.setControlling(true); - remotePeer.setControlling(false); - - long endTime = System.currentTimeMillis(); - - Ice.transferRemoteCandidates(localAgent, remotePeer); - for (IceMediaStream stream : localAgent.getStreams()) - { - stream.setRemoteUfrag(remotePeer.getLocalUfrag()); - stream.setRemotePassword(remotePeer.getLocalPassword()); - } - - Ice.transferRemoteCandidates(remotePeer, localAgent); - - for (IceMediaStream stream : remotePeer.getStreams()) - { - stream.setRemoteUfrag(localAgent.getLocalUfrag()); - stream.setRemotePassword(localAgent.getLocalPassword()); - } - - logger.log(Level.INFO, "Total candidate gathering time: {0} ms", (endTime - startTime)); - logger.log(Level.INFO, "LocalAgent: {0}", localAgent); - - localAgent.startConnectivityEstablishment(); - - //if (START_CONNECTIVITY_ESTABLISHMENT_OF_REMOTE_PEER) - remotePeer.startConnectivityEstablishment(); - - - IceMediaStream dataStream = localAgent.getStream("data"); - - if (dataStream != null) - { - logger.info("Local data clist:" + dataStream.getCheckList()); - } - //wait for one of the agents to complete it's job - synchronized (remoteAgentMonitor) - { - remoteAgentMonitor.wait(agentJobTimeout); - } - if (remoteJob != null) - { - logger.finest("Remote thread join started"); - remoteJob.join(); - logger.finest("Remote thread joined"); - } - remotePeer.free(); - if (localJob != null) - { - logger.finest("Local thread join started"); - localJob.join(); - logger.finest("Local thread joined"); - } - localAgent.free(); - System.exit(0); - } - - private static class LocalPseudoTcpJob extends Thread implements Runnable - { - private DatagramSocket dgramSocket; - - public LocalPseudoTcpJob(DatagramSocket socket) - throws UnknownHostException - { - this.dgramSocket = socket; - } - - @Override - public void run() - { - logger.finest("Local pseudotcp worker started"); - try - { - logger.info("Local pseudotcp is using: " + dgramSocket.getLocalSocketAddress()+dgramSocket); - - PseudoTcpSocket socket = new PseudoTcpSocketFactory().createSocket(dgramSocket); - socket.setConversationID(1073741824); - socket.setMTU(1500); - socket.setDebugName("L"); - socket.accept(5000); - byte[] buffer = new byte[TEST_BYTES_COUNT]; - int read = 0; - while (read != TEST_BYTES_COUNT) - { - read += socket.getInputStream().read(buffer); - logger.finest("Local job read: " + read); - } - //TODO: close when all received data is acked - //socket.close(); - } - catch (IOException e) - { - throw new RuntimeException(e); - } - logger.finest("Local pseudotcp worker finished"); - } - } - - private static class RemotePseudoTcpJob extends Thread implements Runnable - { - private DatagramSocket dgramSocket; - private InetSocketAddress peerAddr; - - public RemotePseudoTcpJob(DatagramSocket socket, - InetSocketAddress peerAddr) - throws UnknownHostException - { - this.dgramSocket = socket; - this.peerAddr = peerAddr; - } - - @Override - public void run() - { - logger.finest("Remote pseudotcp worker started"); - try - { - logger.info("Remote pseudotcp is using: " + dgramSocket.getLocalSocketAddress() - + " and will communicate with: " + peerAddr); - PseudoTcpSocket socket = new PseudoTcpSocketFactory(). - createSocket(dgramSocket); - socket.setConversationID(1073741824); - socket.setMTU(1500); - socket.setDebugName("R"); - long start, end; - start = System.currentTimeMillis(); - socket.connect(peerAddr, 5000); - byte[] buffer = new byte[TEST_BYTES_COUNT]; - socket.getOutputStream().write(buffer); - socket.getOutputStream().flush(); - //Socket will be closed by the iceAgent - //socket.close(); - end = System.currentTimeMillis(); - logger.info("Transferred " + TEST_BYTES_COUNT + " bytes in " + ((end - start) / 1000) + " sec"); - } - catch (IOException e) - { - throw new RuntimeException(e); - } - logger.finest("Remote pseudotcp worker finished"); - } - } -} diff --git a/src/test/resources/logging.properties b/src/test/resources/logging.properties index 88c84e27..9b26aba7 100644 --- a/src/test/resources/logging.properties +++ b/src/test/resources/logging.properties @@ -51,7 +51,6 @@ java.util.logging.ConsoleHandler.formatter = org.ice4j.util.Ice4jLogFormatter # Things coming from ice4j org.ice4j.level = INFO -org.ice4j.pseudotcp.PseudoTCPBase.level = SEVERE test.level = INFO #org.ice4j.ice.checks.levell = FINEST #org.ice4j.ice.ConnectivityCheckClient.level = FINER