diff --git twisted/internet/address.py twisted/internet/address.py
index b9192d4..8e5a483 100644
--- twisted/internet/address.py
+++ twisted/internet/address.py
@@ -19,7 +19,7 @@ class _IPAddress(object, util.FancyEqMixin):
 
     @ivar type: A string describing the type of transport, either 'TCP' or
         'UDP'.
-    @ivar host: A string containing the dotted-quad IP address.
+    @ivar host: A string containing the native IP address.
     @ivar port: An integer representing the port number.
     """
 
@@ -43,10 +43,12 @@ class _IPAddress(object, util.FancyEqMixin):
         return hash((self.type, self.host, self.port))
 
 
-
+    
 class IPv4Address(_IPAddress):
     """
     Object representing an IPv4 socket endpoint.
+
+    @ivar host: A string containing the dotted-quad IPv4 address. 
     """
     def __init__(self, type, host, port, _bwHack=None):
         _IPAddress.__init__(self, type, host, port)
@@ -59,7 +61,10 @@ class IPv4Address(_IPAddress):
 class IPv6Address(_IPAddress):
     """
     Object representing an IPv6 socket endpoint.
+
+    @ivar host: A string containing the hexadecimal formatted IPv6 address. 
     """
+    pass
 
 
 
@@ -85,8 +90,8 @@ class UNIXAddress(object, util.FancyEqMixin):
     if getattr(os.path, 'samefile', None) is not None:
         def __eq__(self, other):
             """
-            overriding L{util.FancyEqMixin} to ensure the os level samefile check
-            is done if the name attributes do not match.
+            overriding L{util.FancyEqMixin} to ensure the os level samefile
+            check is done if the name attributes do not match.
             """
             res = super(UNIXAddress, self).__eq__(other)
             if res == False:
diff --git twisted/internet/endpoints.py twisted/internet/endpoints.py
index 79d7672..e80e1cc 100644
--- twisted/internet/endpoints.py
+++ twisted/internet/endpoints.py
@@ -25,6 +25,7 @@ from twisted.python.filepath import FilePath
 
 __all__ = ["clientFromString", "serverFromString",
            "TCP4ServerEndpoint", "TCP4ClientEndpoint",
+           "TCP6ServerEndpoint", "TCP6ClientEndpoint",
            "UNIXServerEndpoint", "UNIXClientEndpoint",
            "SSL4ServerEndpoint", "SSL4ClientEndpoint"]
 
@@ -169,9 +170,9 @@ class _WrappingFactory(ClientFactory):
 
 
 
-class TCP4ServerEndpoint(object):
+class _TCPServerEndpoint(object):
     """
-    TCP server endpoint with an IPv4 configuration
+    TCP server endpoint with a default (IPv4) configuration
 
     @ivar _reactor: An L{IReactorTCP} provider.
 
@@ -212,9 +213,40 @@ class TCP4ServerEndpoint(object):
 
 
 
-class TCP4ClientEndpoint(object):
+class TCP4ServerEndpoint(_TCPServerEndpoint):
+    """
+    TCP server endpoint with an IPv4 configuration
+
+    @type _interface: str
+    @ivar _interface: the hostname to bind to, defaults to '' (all)
+    """
+    def __init__(self, reactor, port, backlog=50, interface=''):
+        """
+        @param interface: the hostname to bind to, defaults to '' (all)
+        """
+        _TCPServerEndpoint.__init__(self, reactor, port, backlog, interface)
+
+
+
+class TCP6ServerEndpoint(_TCPServerEndpoint):
+    """
+    TCP server endpoint with an IPv6 configuration
+
+    @type _interface: str
+    @ivar _interface: the hostname to bind to, defaults to '::' (all)
+    """
+
+    def __init__(self, reactor, port, backlog=50, interface='::'):
+        """
+        @param interface: the hostname to bind to, defaults to '::' (all)
+        """
+        _TCPServerEndpoint.__init__(self, reactor, port, backlog, interface)
+
+
+
+class _TCPClientEndpoint(object):
     """
-    TCP client endpoint with an IPv4 configuration.
+    TCP client endpoint with a default (IPv4) configuration.
 
     @ivar _reactor: An L{IReactorTCP} provider.
 
@@ -271,6 +303,22 @@ class TCP4ClientEndpoint(object):
 
 
 
+class TCP4ClientEndpoint(_TCPClientEndpoint):
+    """
+    TCP client endpoint with an IPv4 configuration
+    """
+    pass
+
+
+
+class TCP6ClientEndpoint(_TCPClientEndpoint):
+    """
+    TCP client endpoint with an IPv6 configuration
+    """
+    pass
+
+
+
 class SSL4ServerEndpoint(object):
     """
     SSL secured TCP server endpoint with an IPv4 configuration.
diff --git twisted/internet/tcp.py twisted/internet/tcp.py
index 1c81697..31b032b 100644
--- twisted/internet/tcp.py
+++ twisted/internet/tcp.py
@@ -320,10 +320,12 @@ class BaseClient(_TLSClientMixin, Connection):
         else:
             reactor.callLater(0, self.failIfNotConnected, error)
 
+
     def stopConnecting(self):
         """Stop attempt to connect."""
         self.failIfNotConnected(error.UserError())
 
+
     def failIfNotConnected(self, err):
         """
         Generic method called when the attemps to connect failed. It basically
@@ -348,6 +350,7 @@ class BaseClient(_TLSClientMixin, Connection):
         else:
             del self.socket, self.fileno
 
+
     def createInternetSocket(self):
         """(internal) Create a non-blocking socket using
         self.addressFamily, self.socketType.
@@ -357,6 +360,7 @@ class BaseClient(_TLSClientMixin, Connection):
         fdesc._setCloseOnExec(s.fileno())
         return s
 
+
     def resolveAddress(self):
         if abstract.isIPAddress(self.addr[0]):
             self._setRealAddress(self.addr[0])
@@ -364,10 +368,12 @@ class BaseClient(_TLSClientMixin, Connection):
             d = self.reactor.resolve(self.addr[0])
             d.addCallbacks(self._setRealAddress, self.failIfNotConnected)
 
+
     def _setRealAddress(self, address):
         self.realAddress = (address, self.addr[1])
         self.doConnect()
 
+
     def doConnect(self):
         """I connect the socket.
 
@@ -383,7 +389,6 @@ class BaseClient(_TLSClientMixin, Connection):
             self.failIfNotConnected(error.getConnectError((err, strerror(err))))
             return
 
-
         # doConnect gets called twice.  The first time we actually need to
         # start the connection attempt.  The second time we don't really
         # want to (SO_ERROR above will have taken care of any errors, and if
@@ -418,6 +423,7 @@ class BaseClient(_TLSClientMixin, Connection):
         self.stopWriting()
         self._connectDone()
 
+
     def _connectDone(self):
         self.protocol = self.connector.buildProtocol(self.getPeer())
         self.connected = 1
@@ -426,6 +432,7 @@ class BaseClient(_TLSClientMixin, Connection):
         self.startReading()
         self.protocol.makeConnection(self)
 
+
     def connectionLost(self, reason):
         if not self.connected:
             self.failIfNotConnected(error.ConnectError(string=reason))
@@ -434,50 +441,102 @@ class BaseClient(_TLSClientMixin, Connection):
             self.connector.connectionLost(reason)
 
 
+
 class Client(BaseClient):
-    """A TCP client."""
+    """
+    A TCP client.
+
+    @type _addressType: L{IPv4Address} or L{IPv6Address}
+    @ivar _addressType: The Twisted _IPAddress implementation for this client
+    """
+
+    _addressType = address.IPv4Address
 
     def __init__(self, host, port, bindAddress, connector, reactor=None):
         # BaseClient.__init__ is invoked later
         self.connector = connector
         self.addr = (host, port)
+        self.bindAddress = bindAddress
+        self.reactor = reactor
+        # Do outstanding initialization when real address is resolved
+        self.resolveAddress()
+
+
+    def resolveAddress(self):
+        if abstract.isIPAddress(self.addr[0]) or abstract.isIPv6Address(self.addr[0]):
+            self._setRealAddress(self.addr[0])
+        else:
+            d = self.reactor.resolve(self.addr[0])
+            d.addCallbacks(self._setRealAddress, self.failIfNotConnected)
+    
 
-        whenDone = self.resolveAddress
+    def _setRealAddress(self, addr):
+        """
+        Sets the address family to IPv6 if passed an IPv6 literal.
+        Sets the real IP address for this client.
+        Once the IP is set the socket is created with the correct address
+        family.
+
+        @type addr: str
+        @param addr: A string formatted IPv4 or IPv6 literal.
+        """
+        if abstract.isIPv6Address(addr):
+            self.addressFamily = socket.AF_INET6
+            self._addressType = address.IPv6Address
+        self.realAddress = (addr, self.addr[1])
+        self.initConnection()
+
+
+    def initConnection(self):
+        """
+        Initialize connection by creating appropriate socket.
+        """
         err = None
         skt = None
-
+        result = True
+     
         try:
             skt = self.createInternetSocket()
         except socket.error, se:
             err = error.ConnectBindError(se.args[0], se.args[1])
-            whenDone = None
-        if whenDone and bindAddress is not None:
+            result = None
+        if result and self.bindAddress is not None:
             try:
-                skt.bind(bindAddress)
+                skt.bind(self.bindAddress)
             except socket.error, se:
                 err = error.ConnectBindError(se.args[0], se.args[1])
-                whenDone = None
-        self._finishInit(whenDone, skt, err, reactor)
+                result = None
+        if result:
+            Connection.__init__(self, skt, None, self.reactor)
+            self.doWrite = self.doConnect
+            self.doRead = self.doConnect
+            self.doConnect()
+        else:
+            self.reactor.callLater(0, self.failIfNotConnected, error)
+
 
     def getHost(self):
-        """Returns an IPv4Address.
+        """Returns an L{IPv4Address} or L{IPv6Address}.
 
         This indicates the address from which I am connecting.
         """
-        return address.IPv4Address('TCP', *self.socket.getsockname())
+        return self._addressType('TCP', *self.socket.getsockname()[:2])
+
 
     def getPeer(self):
-        """Returns an IPv4Address.
+        """Returns an L{IPv4Address} or L{IPv6Address}.
 
         This indicates the address that I am connected to.
         """
-        return address.IPv4Address('TCP', *self.realAddress)
+        return self._addressType('TCP', *self.realAddress)
+
 
     def __repr__(self):
         s = '<%s to %s at %x>' % (self.__class__, self.addr, unsignedID(self))
         return s
 
 
+
 class Server(_TLSServerMixin, Connection):
     """
     Serverside socket-stream connection class.
@@ -612,11 +671,13 @@ class Port(base.BasePort, _SocketCloser):
 
     def __repr__(self):
         if self._realPortNumber is not None:
-            return "<%s of %s on %s>" % (self.__class__, self.factory.__class__,
-                                         self._realPortNumber)
+            return "<%s of %s on %s (%s)>" % (self.__class__, 
+                self.factory.__class__, self._realPortNumber, 
+                self._addressType)
         else:
             return "<%s of %s (not listening)>" % (self.__class__, self.factory.__class__)
 
+
     def createInternetSocket(self):
         s = base.BasePort.createInternetSocket(self)
         if platformType == "posix" and sys.platform != "cygwin":
@@ -644,8 +705,9 @@ class Port(base.BasePort, _SocketCloser):
         # reflect what the OS actually assigned us.
         self._realPortNumber = skt.getsockname()[1]
 
-        log.msg("%s starting on %s" % (
-                self._getLogPrefix(self.factory), self._realPortNumber))
+        log.msg("%s starting on %s (%s)" % (
+                self._getLogPrefix(self.factory), self._realPortNumber, 
+                self._addressType))
 
         # The order of the next 6 lines is kind of bizarre.  If no one
         # can explain it, perhaps we should re-arrange them.
@@ -795,14 +857,17 @@ class Port(base.BasePort, _SocketCloser):
 
 
 class Connector(base.BaseConnector):
+    _addressType = address.IPv4Address
+
     def __init__(self, host, port, factory, timeout, bindAddress, reactor=None):
-        self.host = host
         if isinstance(port, types.StringTypes):
             try:
                 port = socket.getservbyname(port, 'tcp')
             except socket.error, e:
                 raise error.ServiceNameUnknownError(string="%s (%r)" % (e, port))
-        self.port = port
+        self.host, self.port = host, port
+        if abstract.isIPv6Address(host):
+            self._addressType = address.IPv6Address
         self.bindAddress = bindAddress
         base.BaseConnector.__init__(self, factory, timeout, reactor)
 
@@ -810,4 +875,4 @@ class Connector(base.BaseConnector):
         return Client(self.host, self.port, self.bindAddress, self, self.reactor)
 
     def getDestination(self):
-        return address.IPv4Address('TCP', self.host, self.port)
+        return self._addressType('TCP', self.host, self.port)
diff --git twisted/internet/test/connectionmixins.py twisted/internet/test/connectionmixins.py
index 2a592f8..2abb80b 100644
--- twisted/internet/test/connectionmixins.py
+++ twisted/internet/test/connectionmixins.py
@@ -5,14 +5,23 @@
 Various helpers for tests for connection-oriented transports.
 """
 
+import socket
+
 from gc import collect
 from weakref import ref
 
+from zope.interface import implements
+from zope.interface.verify import verifyObject
+
 from twisted.python import context, log
+from twisted.python.failure import Failure
 from twisted.python.reflect import fullyQualifiedName
 from twisted.python.log import ILogContext, msg, err
-from twisted.internet.defer import Deferred, gatherResults
-from twisted.internet.protocol import ServerFactory, Protocol
+from twisted.internet.defer import Deferred, gatherResults, succeed
+from twisted.internet.interfaces import (
+    IConnector, IResolverSimple, IReactorFDSet)
+from twisted.internet.protocol import ClientFactory, Protocol, ServerFactory
+from twisted.test.test_tcp import ClosingProtocol
 
 def serverFactoryFor(protocol):
     """
@@ -29,6 +38,45 @@ def serverFactoryFor(protocol):
 # ServerFactory is good enough for client endpoints, too.
 factoryFor = serverFactoryFor
 
+
+
+def findFreePort(interface='127.0.0.1', family=socket.AF_INET,
+                 type=socket.SOCK_STREAM):
+    """
+    Ask the platform to allocate a free port on the specified interface,
+    then release the socket and return the address which was allocated.
+
+    @param interface: The local address to try to bind the port on.
+    @type interface: C{str}
+
+    @param type: The socket type which will use the resulting port.
+
+    @return: A two-tuple of address and port, like that returned by
+        L{socket.getsockname}.
+    """
+    probe = socket.socket(family, type)
+    try:
+        probe.bind((interface, 0))
+        return probe.getsockname()
+    finally:
+        probe.close()
+
+
+
+def _getWriters(reactor):
+    """
+    Like L{IReactorFDSet.getWriters}, but with support for IOCP reactor as well.
+    """
+    if IReactorFDSet.providedBy(reactor):
+        return reactor.getWriters()
+    elif 'IOCP' in reactor.__class__.__name__:
+        return reactor.handles
+    else:
+        # Cannot tell what is going on.
+        raise Exception("Cannot find writers on %r" % (reactor,))
+
+
+
 class _AcceptOneClient(ServerFactory):
     """
     This factory fires a L{Deferred} with a protocol instance shortly after it
@@ -50,6 +98,56 @@ class _AcceptOneClient(ServerFactory):
 
 
 
+class _SimplePullProducer(object):
+    """
+    A pull producer which writes one byte whenever it is resumed.  For use by
+    L{test_unregisterProducerAfterDisconnect}.
+    """
+    def __init__(self, consumer):
+        self.consumer = consumer
+
+
+    def stopProducing(self):
+        pass
+
+
+    def resumeProducing(self):
+        log.msg("Producer.resumeProducing")
+        self.consumer.write('x')
+
+
+
+class Stop(ClientFactory):
+    """
+    A client factory which stops a reactor when a connection attempt fails.
+    """
+    def __init__(self, reactor):
+        self.reactor = reactor
+
+
+    def clientConnectionFailed(self, connector, reason):
+        self.reactor.stop()
+
+
+
+class FakeResolver:
+    """
+    A resolver implementation based on a C{dict} mapping names to addresses.
+    """
+    implements(IResolverSimple)
+
+    def __init__(self, names):
+        self.names = names
+
+
+    def getHostByName(self, name, timeout):
+        try:
+            return succeed(self.names[name])
+        except KeyError:
+            return fail(DNSLookupError("FakeResolver couldn't find " + name))
+
+
+
 class ClosingLaterProtocol(Protocol):
     """
     ClosingLaterProtocol exchanges one byte with its peer and then disconnects
@@ -254,3 +352,209 @@ class LogObserverMixin(object):
         log.addObserver(loggedMessages.append)
         self.addCleanup(log.removeObserver, loggedMessages.append)
         return loggedMessages
+
+
+
+class TCPClientTestsMixin(object):
+    """
+    This mixin defines tests applicable to TCP client implementations.
+    """
+    def test_interface(self):
+        """
+        L{IReactorTCP.connectTCP} returns an object providing L{IConnector}.
+        """
+        reactor = self.buildReactor()
+        connector = reactor.connectTCP(self.interface, self.port, 
+            ClientFactory())
+        self.assertTrue(verifyObject(IConnector, connector))
+
+    
+    def test_clientConnectionFailedStopsReactor(self):
+        """
+        The reactor can be stopped by a client factory's
+        C{clientConnectionFailed} method.
+        """
+        host, port = findFreePort(self.interface, self.family)[:2]
+        reactor = self.buildReactor()
+        reactor.connectTCP(host, port, Stop(reactor))
+        self.runReactor(reactor)
+
+
+    def test_addresses(self):
+        """
+        A client's transport's C{getHost} and C{getPeer} return L{IPv4Address}
+        instances which give the dotted-quad string form of the local and
+        remote endpoints of the connection respectively.
+        """
+        host, port = findFreePort(self.interface, self.family)[:2]
+        reactor = self.buildReactor()
+        reactor.installResolver(FakeResolver({'localhost': self.interface}))
+
+        server = reactor.listenTCP(
+            0, serverFactoryFor(Protocol), interface=host)
+        serverAddress = server.getHost()
+
+        addresses = {'host': None, 'peer': None}
+        class CheckAddress(Protocol):
+            def makeConnection(self, transport):
+                addresses['host'] = transport.getHost()
+                addresses['peer'] = transport.getPeer()
+                reactor.stop()
+
+        clientFactory = Stop(reactor)
+        clientFactory.protocol = CheckAddress
+        reactor.connectTCP(
+            'localhost', server.getHost().port, clientFactory,
+            bindAddress=(self.interface, port))
+
+        self.runReactor(reactor)
+
+        self.assertEqual(
+            addresses['host'],
+            self.addressClass('TCP', self.interface, port))
+        self.assertEqual(
+            addresses['peer'],
+            self.addressClass('TCP', self.interface, serverAddress.port))
+
+
+    def test_connectEvent(self):
+        """
+        This test checks that we correctly get notifications event for a
+        client. This ought to prevent a regression under Windows using the GTK2
+        reactor. See #3925.
+        """
+        reactor = self.buildReactor()
+
+        server = reactor.listenTCP(0, serverFactoryFor(Protocol), 
+            interface=self.interface)
+        connected = []
+
+        class CheckConnection(Protocol):
+            def connectionMade(self):
+                connected.append(self)
+                reactor.stop()
+
+        clientFactory = Stop(reactor)
+        clientFactory.protocol = CheckConnection
+        reactor.connectTCP(
+            self.interface, server.getHost().port, clientFactory)
+
+        reactor.run()
+
+        self.assertTrue(connected)
+
+
+    def test_unregisterProducerAfterDisconnect(self):
+        """
+        If a producer is unregistered from a L{ITCPTransport} provider after the
+        transport has been disconnected (by the peer) and after
+        L{ITCPTransport.loseConnection} has been called, the transport is not
+        re-added to the reactor as a writer as would be necessary if the
+        transport were still connected.
+        """
+        reactor = self.buildReactor()
+        port = reactor.listenTCP(0, serverFactoryFor(ClosingProtocol),
+            interface=self.interface)
+
+        finished = Deferred()
+        finished.addErrback(log.err)
+        finished.addCallback(lambda ign: reactor.stop())
+
+        writing = []
+
+        class ClientProtocol(Protocol):
+            """
+            Protocol to connect, register a producer, try to lose the
+            connection, wait for the server to disconnect from us, and
+            then unregister the producer.
+            """
+            def connectionMade(self):
+                log.msg("ClientProtocol.connectionMade")
+                self.transport.registerProducer(
+                    _SimplePullProducer(self.transport), False)
+                self.transport.loseConnection()
+
+            def connectionLost(self, reason):
+                log.msg("ClientProtocol.connectionLost")
+                self.unregister()
+                writing.append(self.transport in _getWriters(reactor))
+                finished.callback(None)
+
+            def unregister(self):
+                log.msg("ClientProtocol unregister")
+                self.transport.unregisterProducer()
+
+        clientFactory = ClientFactory()
+        clientFactory.protocol = ClientProtocol
+        reactor.connectTCP(self.interface, port.getHost().port, clientFactory)
+        self.runReactor(reactor)
+        self.assertFalse(
+            writing[0], "Transport was writing after unregisterProducer.")
+
+
+    def test_disconnectWhileProducing(self):
+        """
+        If L{ITCPTransport.loseConnection} is called while a producer
+        is registered with the transport, the connection is closed
+        after the producer is unregistered.
+        """
+        reactor = self.buildReactor()
+
+        # For some reason, pyobject/pygtk will not deliver the close
+        # notification that should happen after the unregisterProducer call in
+        # this test.  The selectable is in the write notification set, but no
+        # notification ever arrives.  Probably for the same reason #5233 led
+        # win32eventreactor to be broken.
+        skippedReactors = ["Glib2Reactor", "Gtk2Reactor"]
+        reactorClassName = reactor.__class__.__name__
+        if reactorClassName in skippedReactors and platform.isWindows():
+            raise SkipTest(
+                "A pygobject/pygtk bug disables this functionality on Windows.")
+
+        class Producer:
+            def resumeProducing(self):
+                log.msg("Producer.resumeProducing")
+        
+        port = reactor.listenTCP(0, serverFactoryFor(Protocol),
+            interface=self.interface)
+
+        finished = Deferred()
+        finished.addErrback(log.err)
+        finished.addCallback(lambda ign: reactor.stop())
+
+        class ClientProtocol(Protocol):
+            """
+            Protocol to connect, register a producer, try to lose the
+            connection, unregister the producer, and wait for the connection to
+            actually be lost.
+            """
+            def connectionMade(self):
+                log.msg("ClientProtocol.connectionMade")
+                self.transport.registerProducer(Producer(), False)
+                self.transport.loseConnection()
+                # Let the reactor tick over, in case synchronously calling
+                # loseConnection and then unregisterProducer is the same as
+                # synchronously calling unregisterProducer and then
+                # loseConnection (as it is in several reactors).
+                reactor.callLater(0, reactor.callLater, 0, self.unregister)
+
+            def unregister(self):
+                log.msg("ClientProtocol unregister")
+                self.transport.unregisterProducer()
+                # This should all be pretty quick.  Fail the test
+                # if we don't get a connectionLost event really
+                # soon.
+                reactor.callLater(
+                    1.0, finished.errback,
+                    Failure(Exception("Connection was not lost")))
+
+            def connectionLost(self, reason):
+                log.msg("ClientProtocol.connectionLost")
+                finished.callback(None)
+
+        clientFactory = ClientFactory()
+        clientFactory.protocol = ClientProtocol
+        reactor.connectTCP(self.interface, port.getHost().port, clientFactory)
+        self.runReactor(reactor)
+        # If the test failed, we logged an error already and trial
+        # will catch it.
diff --git twisted/internet/test/test_tcp.py twisted/internet/test/test_tcp.py
index af9ad0e..8f7b6c2 100644
--- twisted/internet/test/test_tcp.py
+++ twisted/internet/test/test_tcp.py
@@ -25,7 +25,9 @@ from twisted.internet.interfaces import (
 from twisted.internet.address import IPv4Address, IPv6Address
 from twisted.internet.defer import (
     Deferred, DeferredList, succeed, fail, maybeDeferred, gatherResults)
-from twisted.internet.endpoints import TCP4ServerEndpoint, TCP4ClientEndpoint
+from twisted.internet.endpoints import (
+    TCP4ServerEndpoint, TCP4ClientEndpoint,
+    TCP6ServerEndpoint, TCP6ClientEndpoint)
 from twisted.internet.protocol import ServerFactory, ClientFactory, Protocol
 from twisted.internet.interfaces import (
     IPushProducer, IPullProducer, IHalfCloseableProtocol)
@@ -33,7 +35,8 @@ from twisted.internet.protocol import ClientCreator
 from twisted.internet.tcp import Connection, Server
 
 from twisted.internet.test.connectionmixins import (
-    LogObserverMixin, ConnectionTestsMixin, serverFactoryFor)
+    LogObserverMixin, ConnectionTestsMixin, serverFactoryFor,
+    TCPClientTestsMixin, findFreePort, Stop, FakeResolver)
 from twisted.internet.test.test_core import ObjectModelIntegrationMixin
 from twisted.test.test_tcp import MyClientFactory, MyServerFactory
 from twisted.test.test_tcp import ClosingProtocol
@@ -82,31 +85,8 @@ def getLinkLocalIPv6Address():
 
 
 
-def findFreePort(interface='127.0.0.1', family=socket.AF_INET,
-                 type=socket.SOCK_STREAM):
-    """
-    Ask the platform to allocate a free port on the specified interface,
-    then release the socket and return the address which was allocated.
-
-    @param interface: The local address to try to bind the port on.
-    @type interface: C{str}
-
-    @param type: The socket type which will use the resulting port.
-
-    @return: A two-tuple of address and port, like that returned by
-        L{socket.getsockname}.
-    """
-    probe = socket.socket(family, type)
-    try:
-        probe.bind((interface, 0))
-        return probe.getsockname()
-    finally:
-        probe.close()
-
-
-
 def connect(client, (host, port)):
-    if '%' in host:
+    if '%' in host or ':' in host:
         address = socket.getaddrinfo(host, port)[0][4]
     else:
         address = (host, port)
@@ -114,70 +94,6 @@ def connect(client, (host, port)):
 
 
 
-class Stop(ClientFactory):
-    """
-    A client factory which stops a reactor when a connection attempt fails.
-    """
-    def __init__(self, reactor):
-        self.reactor = reactor
-
-
-    def clientConnectionFailed(self, connector, reason):
-        self.reactor.stop()
-
-
-
-class FakeResolver:
-    """
-    A resolver implementation based on a C{dict} mapping names to addresses.
-    """
-    implements(IResolverSimple)
-
-    def __init__(self, names):
-        self.names = names
-
-
-    def getHostByName(self, name, timeout):
-        try:
-            return succeed(self.names[name])
-        except KeyError:
-            return fail(DNSLookupError("FakeResolver couldn't find " + name))
-
-
-
-class _SimplePullProducer(object):
-    """
-    A pull producer which writes one byte whenever it is resumed.  For use by
-    L{test_unregisterProducerAfterDisconnect}.
-    """
-    def __init__(self, consumer):
-        self.consumer = consumer
-
-
-    def stopProducing(self):
-        pass
-
-
-    def resumeProducing(self):
-        log.msg("Producer.resumeProducing")
-        self.consumer.write('x')
-
-
-
-def _getWriters(reactor):
-    """
-    Like L{IReactorFDSet.getWriters}, but with support for IOCP reactor as well.
-    """
-    if IReactorFDSet.providedBy(reactor):
-        return reactor.getWriters()
-    elif 'IOCP' in reactor.__class__.__name__:
-        return reactor.handles
-    else:
-        # Cannot tell what is going on.
-        raise Exception("Cannot find writers on %r" % (reactor,))
-
-
-
 class FakeSocket(object):
     """
     A fake for L{socket.socket} objects.
@@ -399,16 +315,23 @@ class TCPConnectionTests(TestCase):
         test_tlsAfterStartTLS.skip = "No SSL support available"
 
 
-class TCPClientTestsBuilder(ReactorBuilder, ConnectionTestsMixin):
+class TCP4ClientTestsBuilder(ReactorBuilder, ConnectionTestsMixin,
+                             TCPClientTestsMixin):
     """
     Builder defining tests relating to L{IReactorTCP.connectTCP}.
     """
+    def setUp(self):
+        self.interface = '127.0.0.1'
+        self.port = 1234
+        self.family = socket.AF_INET
+        self.addressClass = IPv4Address
+
     def serverEndpoint(self, reactor):
         """
         Create a L{TCP4ServerEndpoint} listening on localhost on a
         TCP/IP-selected port.
         """
-        return TCP4ServerEndpoint(reactor, 0, interface='127.0.0.1')
+        return TCP4ServerEndpoint(reactor, 0, interface=self.interface)
 
 
     def clientEndpoint(self, reactor, serverAddress):
@@ -419,27 +342,7 @@ class TCPClientTestsBuilder(ReactorBuilder, ConnectionTestsMixin):
         @type serverAddress: L{IPv4Address}
         """
         return TCP4ClientEndpoint(
-            reactor, '127.0.0.1', serverAddress.port)
-
-
-    def test_interface(self):
-        """
-        L{IReactorTCP.connectTCP} returns an object providing L{IConnector}.
-        """
-        reactor = self.buildReactor()
-        connector = reactor.connectTCP("127.0.0.1", 1234, ClientFactory())
-        self.assertTrue(verifyObject(IConnector, connector))
-
-
-    def test_clientConnectionFailedStopsReactor(self):
-        """
-        The reactor can be stopped by a client factory's
-        C{clientConnectionFailed} method.
-        """
-        host, port = findFreePort()
-        reactor = self.buildReactor()
-        reactor.connectTCP(host, port, Stop(reactor))
-        self.runReactor(reactor)
+            reactor, self.interface, serverAddress.port)
 
 
     def test_addresses(self):
@@ -448,175 +351,47 @@ class TCPClientTestsBuilder(ReactorBuilder, ConnectionTestsMixin):
         instances which give the dotted-quad string form of the local and
         remote endpoints of the connection respectively.
         """
-        host, port = findFreePort()
-        reactor = self.buildReactor()
-
-        server = reactor.listenTCP(
-            0, serverFactoryFor(Protocol), interface=host)
-        serverAddress = server.getHost()
-
-        addresses = {'host': None, 'peer': None}
-        class CheckAddress(Protocol):
-            def makeConnection(self, transport):
-                addresses['host'] = transport.getHost()
-                addresses['peer'] = transport.getPeer()
-                reactor.stop()
+        TCPClientTestsMixin.test_addresses(self)
 
-        clientFactory = Stop(reactor)
-        clientFactory.protocol = CheckAddress
-        reactor.connectTCP(
-            'localhost', server.getHost().port, clientFactory,
-            bindAddress=('127.0.0.1', port))
 
-        reactor.installResolver(FakeResolver({'localhost': '127.0.0.1'}))
-        self.runReactor(reactor)
-
-        self.assertEqual(
-            addresses['host'],
-            IPv4Address('TCP', '127.0.0.1', port))
-        self.assertEqual(
-            addresses['peer'],
-            IPv4Address('TCP', '127.0.0.1', serverAddress.port))
 
+class TCP6ClientTestsBuilder(ReactorBuilder, ConnectionTestsMixin,
+                             TCPClientTestsMixin):
+    """
+    Builder defining tests relating to L{IReactorTCP.connectTCP}.
+    """
+    def setUp(self):
+        self.interface = '::1'
+        self.port = 1234
+        self.family = socket.AF_INET6
+        self.addressClass = IPv6Address
 
-    def test_connectEvent(self):
+    def serverEndpoint(self, reactor):
         """
-        This test checks that we correctly get notifications event for a
-        client. This ought to prevent a regression under Windows using the GTK2
-        reactor. See #3925.
+        Create a L{TCP6ServerEndpoint} listening on localhost on a
+        TCP/IP-selected port.
         """
-        reactor = self.buildReactor()
-
-        server = reactor.listenTCP(0, serverFactoryFor(Protocol))
-        connected = []
-
-        class CheckConnection(Protocol):
-            def connectionMade(self):
-                connected.append(self)
-                reactor.stop()
-
-        clientFactory = Stop(reactor)
-        clientFactory.protocol = CheckConnection
-        reactor.connectTCP(
-            '127.0.0.1', server.getHost().port, clientFactory)
-
-        reactor.run()
+        return TCP6ServerEndpoint(reactor, 0, interface=self.interface)
 
-        self.assertTrue(connected)
 
-
-    def test_unregisterProducerAfterDisconnect(self):
-        """
-        If a producer is unregistered from a L{ITCPTransport} provider after the
-        transport has been disconnected (by the peer) and after
-        L{ITCPTransport.loseConnection} has been called, the transport is not
-        re-added to the reactor as a writer as would be necessary if the
-        transport were still connected.
+    def clientEndpoint(self, reactor, serverAddress):
         """
-        reactor = self.buildReactor()
-        port = reactor.listenTCP(0, serverFactoryFor(ClosingProtocol))
-
-        finished = Deferred()
-        finished.addErrback(log.err)
-        finished.addCallback(lambda ign: reactor.stop())
-
-        writing = []
-
-        class ClientProtocol(Protocol):
-            """
-            Protocol to connect, register a producer, try to lose the
-            connection, wait for the server to disconnect from us, and
-            then unregister the producer.
-            """
-            def connectionMade(self):
-                log.msg("ClientProtocol.connectionMade")
-                self.transport.registerProducer(
-                    _SimplePullProducer(self.transport), False)
-                self.transport.loseConnection()
+        Create a L{TCP6ClientEndpoint} which will connect to localhost
+        on the port given by C{serverAddress}.
 
-            def connectionLost(self, reason):
-                log.msg("ClientProtocol.connectionLost")
-                self.unregister()
-                writing.append(self.transport in _getWriters(reactor))
-                finished.callback(None)
-
-            def unregister(self):
-                log.msg("ClientProtocol unregister")
-                self.transport.unregisterProducer()
-
-        clientFactory = ClientFactory()
-        clientFactory.protocol = ClientProtocol
-        reactor.connectTCP('127.0.0.1', port.getHost().port, clientFactory)
-        self.runReactor(reactor)
-        self.assertFalse(
-            writing[0], "Transport was writing after unregisterProducer.")
+        @type serverAddress: L{IPv4Address}
+        """
+        return TCP6ClientEndpoint(
+            reactor, self.interface, serverAddress.port)
 
 
-    def test_disconnectWhileProducing(self):
+    def test_addresses(self):
         """
-        If L{ITCPTransport.loseConnection} is called while a producer
-        is registered with the transport, the connection is closed
-        after the producer is unregistered.
+        A client's transport's C{getHost} and C{getPeer} return L{IPv6Address}
+        instances which give the hexadecimal string form of the local and
+        remote endpoints of the connection respectively.
         """
-        reactor = self.buildReactor()
-
-        # For some reason, pyobject/pygtk will not deliver the close
-        # notification that should happen after the unregisterProducer call in
-        # this test.  The selectable is in the write notification set, but no
-        # notification ever arrives.  Probably for the same reason #5233 led
-        # win32eventreactor to be broken.
-        skippedReactors = ["Glib2Reactor", "Gtk2Reactor"]
-        reactorClassName = reactor.__class__.__name__
-        if reactorClassName in skippedReactors and platform.isWindows():
-            raise SkipTest(
-                "A pygobject/pygtk bug disables this functionality on Windows.")
-
-        class Producer:
-            def resumeProducing(self):
-                log.msg("Producer.resumeProducing")
-
-        port = reactor.listenTCP(0, serverFactoryFor(Protocol))
-
-        finished = Deferred()
-        finished.addErrback(log.err)
-        finished.addCallback(lambda ign: reactor.stop())
-
-        class ClientProtocol(Protocol):
-            """
-            Protocol to connect, register a producer, try to lose the
-            connection, unregister the producer, and wait for the connection to
-            actually be lost.
-            """
-            def connectionMade(self):
-                log.msg("ClientProtocol.connectionMade")
-                self.transport.registerProducer(Producer(), False)
-                self.transport.loseConnection()
-                # Let the reactor tick over, in case synchronously calling
-                # loseConnection and then unregisterProducer is the same as
-                # synchronously calling unregisterProducer and then
-                # loseConnection (as it is in several reactors).
-                reactor.callLater(0, reactor.callLater, 0, self.unregister)
-
-            def unregister(self):
-                log.msg("ClientProtocol unregister")
-                self.transport.unregisterProducer()
-                # This should all be pretty quick.  Fail the test
-                # if we don't get a connectionLost event really
-                # soon.
-                reactor.callLater(
-                    1.0, finished.errback,
-                    Failure(Exception("Connection was not lost")))
-
-            def connectionLost(self, reason):
-                log.msg("ClientProtocol.connectionLost")
-                finished.callback(None)
-
-        clientFactory = ClientFactory()
-        clientFactory.protocol = ClientProtocol
-        reactor.connectTCP('127.0.0.1', port.getHost().port, clientFactory)
-        self.runReactor(reactor)
-        # If the test failed, we logged an error already and trial
-        # will catch it.
+        TCPClientTestsMixin.test_addresses(self)
 
 
 
@@ -699,7 +474,8 @@ class TCPPortTestsBuilder(ReactorBuilder, ObjectModelIntegrationMixin,
         """
         Get the message expected to be logged when a TCP port starts listening.
         """
-        return "%s starting on %d" % (factory, port.getHost().port)
+        return "%s starting on %d (<class 'twisted.internet.address.IPv4Address'>)" % (
+            factory, port.getHost().port)
 
 
     def getExpectedConnectionLostLogMsg(self, port):
@@ -1385,7 +1161,8 @@ class WriteSequenceTests(ReactorBuilder):
         self.assertEquals(producer.actions, ["resume", "resume"])
 
 
-globals().update(TCPClientTestsBuilder.makeTestCaseClasses())
+globals().update(TCP4ClientTestsBuilder.makeTestCaseClasses())
+globals().update(TCP6ClientTestsBuilder.makeTestCaseClasses())
 globals().update(TCPPortTestsBuilder.makeTestCaseClasses())
 globals().update(TCPConnectionTestsBuilder.makeTestCaseClasses())
 globals().update(WriteSequenceTests.makeTestCaseClasses())
diff --git twisted/internet/test/test_tls.py twisted/internet/test/test_tls.py
index f77af0d..a8e3e97 100644
--- twisted/internet/test/test_tls.py
+++ twisted/internet/test/test_tls.py
@@ -273,7 +273,7 @@ class TLSPortTestsBuilder(TLSMixin, ContextGeneratingMixin,
         """
         Get the message expected to be logged when a TLS port starts listening.
         """
-        return "%s (TLS) starting on %d" % (factory, port.getHost().port)
+        return "%s (TLS) starting on %d (<class 'twisted.internet.address.IPv4Address'>)" % (factory, port.getHost().port)
 
 
     def getExpectedConnectionLostLogMsg(self, port):
diff --git twisted/test/test_tcp.py twisted/test/test_tcp.py
index ecc9fcf..9ebe967 100644
--- twisted/test/test_tcp.py
+++ twisted/test/test_tcp.py
@@ -678,6 +678,14 @@ class FactoryTestCase(unittest.TestCase):
 
 
 class ConnectorTestCase(unittest.TestCase):
+    """
+    TCP Connector Test cases
+
+    @type interface: str
+    @ivar interface: A string representing an IPv4 literal used for this test.
+    """
+
+    interface="127.0.0.1"
 
     def test_connectorIdentity(self):
         """
@@ -688,7 +696,7 @@ class ConnectorTestCase(unittest.TestCase):
         C{clientConnectionLost} method.
         """
         serverFactory = ClosingFactory()
-        tcpPort = reactor.listenTCP(0, serverFactory, interface="127.0.0.1")
+        tcpPort = reactor.listenTCP(0, serverFactory, interface=self.interface)
         serverFactory.port = tcpPort
         self.addCleanup(serverFactory.cleanUp)
         portNumber = tcpPort.getHost().port
@@ -702,11 +710,11 @@ class ConnectorTestCase(unittest.TestCase):
                                        seenFailures.append(reason)))
         clientFactory.startedConnecting = seenConnectors.append
 
-        connector = reactor.connectTCP("127.0.0.1", portNumber, clientFactory)
+        connector = reactor.connectTCP(self.interface, portNumber, clientFactory)
         self.assertTrue(interfaces.IConnector.providedBy(connector))
         dest = connector.getDestination()
         self.assertEqual(dest.type, "TCP")
-        self.assertEqual(dest.host, "127.0.0.1")
+        self.assertEqual(dest.host, self.interface)
         self.assertEqual(dest.port, portNumber)
 
         d = loopUntil(lambda: clientFactory.stopped)
@@ -724,7 +732,7 @@ class ConnectorTestCase(unittest.TestCase):
         L{error.UserError} as the reason.
         """
         serverFactory = MyServerFactory()
-        tcpPort = reactor.listenTCP(0, serverFactory, interface="127.0.0.1")
+        tcpPort = reactor.listenTCP(0, serverFactory, interface=self.interface)
         self.addCleanup(tcpPort.stopListening)
         portNumber = tcpPort.getHost().port
 
@@ -733,7 +741,7 @@ class ConnectorTestCase(unittest.TestCase):
 
         clientFactory = ClientStartStopFactory()
         clientFactory.startedConnecting = startedConnecting
-        reactor.connectTCP("127.0.0.1", portNumber, clientFactory)
+        reactor.connectTCP(self.interface, portNumber, clientFactory)
 
         d = loopUntil(lambda: clientFactory.stopped)
         def check(ignored):
@@ -748,7 +756,7 @@ class ConnectorTestCase(unittest.TestCase):
         a new connection attempt to be made.
         """
         serverFactory = ClosingFactory()
-        tcpPort = reactor.listenTCP(0, serverFactory, interface="127.0.0.1")
+        tcpPort = reactor.listenTCP(0, serverFactory, interface=self.interface)
         serverFactory.port = tcpPort
         self.addCleanup(serverFactory.cleanUp)
         portNumber = tcpPort.getHost().port
@@ -758,7 +766,7 @@ class ConnectorTestCase(unittest.TestCase):
         def clientConnectionLost(connector, reason):
             connector.connect()
         clientFactory.clientConnectionLost = clientConnectionLost
-        reactor.connectTCP("127.0.0.1", portNumber, clientFactory)
+        reactor.connectTCP(self.interface, portNumber, clientFactory)
 
         d = loopUntil(lambda: clientFactory.failed)
         def reconnectFailed(ignored):
@@ -770,6 +778,18 @@ class ConnectorTestCase(unittest.TestCase):
 
 
 
+class Connector6TestCase(ConnectorTestCase):
+    """
+    TCPv6 Connector Test cases.
+
+    @type interface: str
+    @ivar interface: A string representing an IPv6 literal used for this test.
+    """
+
+    interface="::1"
+
+
+
 class CannotBindTestCase(unittest.TestCase):
     """
     Tests for correct behavior when a reactor cannot bind to the required TCP
