From nobody Wed Sep 17 16:38:55 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F2E5930CD89 for ; Thu, 28 Aug 2025 08:47:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756370848; cv=none; b=FUxS/k6TsYDkhqXpQTDv91wu56gynx3uYebdfQWD5Lth3fv3OQR6sogCZpwkR2K3I6mXQLPPSkq78vs2uEzYAptFa7qLQqhNvuymJ4OpoI9nOTAnKz/5QhjtWmAk+hsqUdEITkeKaxZjVXCNRWKU/2GaLJ9venOhbKdHTS5ZD1I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756370848; c=relaxed/simple; bh=nN6QL+A0/4wikzDdc81khLs4EMZYmnihmFxCsKOtjDU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=jAkSgEyKwgdUn/44IXOHvAwAKm6fmf5xZZ2fz1/dNeWnc0c+yNrVjLVtOSj+6h2oe1Q2HthFKTDdvYynYJxOcbd8jHz+LrAHOQdME//YqJ9tLJebPdv9FHGHzTWdQMJLj2YBXaaW4Ssflgc/5qsYLElRdYunXJXQPiYykH+l+qE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=S2N0ANmF; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="S2N0ANmF" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 2E326C4CEF4; Thu, 28 Aug 2025 08:47:24 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1756370847; bh=nN6QL+A0/4wikzDdc81khLs4EMZYmnihmFxCsKOtjDU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=S2N0ANmFHoqwK+qat8WI7gNHAe9sXdVCoau4f60DB+5GscKJOdRjzsD/1ZEjWUbdj ZA3jL0+SMwp3JwHDSPrtNbemotO3IRb/5UR+ab6GkVhsjQCPBDXDfQWZjwHwP/x2/2 1JQm+TIE7vIdcGN0lFcQI5UQZpZNtl1BUr5YVSkqcTfz4oJMZ5Gs6xUaK5uzSm2v7b lIj2VxUm8ajj/83AdsqrDjhNuhbcPw/HrfJxxju+YgcHXZwtk5xDhR7F1BPEpRp5Q5 gnPnaPnCtYo+7aaRmk1y5wxAfBsRx0gTGEoRRH1HjcKcXbPWsnxEValayJKcW8u82A w1/MFdJRpubIg== From: Geliang Tang To: mptcp@lists.linux.dev Cc: Geliang Tang , Gang Yan , Xiang Gao Subject: [PATCH mptcp-next v2 1/3] Add native MPTCP socket creation support Date: Thu, 28 Aug 2025 16:47:13 +0800 Message-ID: <4c3e6afca9f3cc88264c6d5d8e542c23ab99184d.1756369940.git.tanggeliang@kylinos.cn> X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: mptcp@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This patch introduces native-level support for creating MPTCP sockets by: 1. Extending the socket0 native method in both Unix and Windows implementations to accept an mptcp parameter 2. Adding new overloaded socket() and serverSocket() methods in Net.java that support explicit MPTCP enabling 3. Introducing convenience methods mptcpSocket() and mptcpServerSocket() for easier MPTCP socket creation When MPTCP is enabled and supported by the OS, the socket is created with the IPPROTO_MPTCP protocol for stream sockets. All existing call sites are updated to pass 'false' for the mptcp parameter to maintain backward compatibility. Co-Developed-by: Gang Yan Signed-off-by: Gang Yan Co-Developed-by: Xiang Gao Signed-off-by: Xiang Gao Signed-off-by: Geliang Tang --- .../share/classes/sun/nio/ch/Net.java | 19 ++++++++++++++----- src/java.base/unix/native/libnio/ch/Net.c | 14 ++++++++++++-- src/java.base/windows/native/libnio/ch/Net.c | 4 +++- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/java.base/share/classes/sun/nio/ch/Net.java b/src/java.bas= e/share/classes/sun/nio/ch/Net.java index 9ec7975a35c..20caa62859f 100644 --- a/src/java.base/share/classes/sun/nio/ch/Net.java +++ b/src/java.base/share/classes/sun/nio/ch/Net.java @@ -485,25 +485,34 @@ static FileDescriptor socket() throws IOException { return socket(UNSPEC, true); } =20 - static FileDescriptor socket(ProtocolFamily family, boolean stream) th= rows IOException { + static FileDescriptor socket(ProtocolFamily family, boolean stream, bo= olean mptcp) + throws IOException { boolean preferIPv6 =3D isIPv6Available() && (family !=3D StandardProtocolFamily.INET); - return IOUtil.newFD(socket0(preferIPv6, stream, false, FAST_LOOPBA= CK)); + return IOUtil.newFD(socket0(preferIPv6, stream, false, FAST_LOOPBA= CK, mptcp)); + } + + static FileDescriptor socket(ProtocolFamily family, boolean stream) th= rows IOException { + return socket(family, stream, false); } =20 static FileDescriptor serverSocket() { return serverSocket(UNSPEC); } =20 - static FileDescriptor serverSocket(ProtocolFamily family) { + static FileDescriptor serverSocket(ProtocolFamily family, boolean mptc= p) { boolean preferIPv6 =3D isIPv6Available() && (family !=3D StandardProtocolFamily.INET); - return IOUtil.newFD(socket0(preferIPv6, true, true, FAST_LOOPBACK)= ); + return IOUtil.newFD(socket0(preferIPv6, true, true, FAST_LOOPBACK,= mptcp)); + } + + static FileDescriptor serverSocket(ProtocolFamily family) { + return serverSocket(family, false); } =20 // Due to oddities SO_REUSEADDR on Windows reuse is ignored private static native int socket0(boolean preferIPv6, boolean stream, = boolean reuse, - boolean fastLoopback); + boolean fastLoopback, boolean mptcp); =20 public static void bind(FileDescriptor fd, InetAddress addr, int port) throws IOException diff --git a/src/java.base/unix/native/libnio/ch/Net.c b/src/java.base/unix= /native/libnio/ch/Net.c index 28c1814f422..1c53c197fd8 100644 --- a/src/java.base/unix/native/libnio/ch/Net.c +++ b/src/java.base/unix/native/libnio/ch/Net.c @@ -255,13 +255,23 @@ Java_sun_nio_ch_Net_canUseIPv6OptionsWithIPv4LocalAdd= ress0(JNIEnv* env, jclass c =20 JNIEXPORT jint JNICALL Java_sun_nio_ch_Net_socket0(JNIEnv *env, jclass cl, jboolean preferIPv6, - jboolean stream, jboolean reuse, jboolean igno= red) + jboolean stream, jboolean reuse, jboolean igno= red, + jboolean mptcp) { int fd; int type =3D (stream ? SOCK_STREAM : SOCK_DGRAM); int domain =3D (ipv6_available() && preferIPv6) ? AF_INET6 : AF_INET; + int protocol =3D 0; =20 - fd =3D socket(domain, type, 0); +#if defined(__linux__) && defined(IPPROTO_MPTCP) + if (stream =3D=3D JNI_TRUE && mptcp =3D=3D JNI_TRUE) { + protocol =3D IPPROTO_MPTCP; + } +#else + (void)mptcp; /* Avoid compile warning when MPTCP is not supported */ +#endif + + fd =3D socket(domain, type, protocol); if (fd < 0) { return handleSocketError(env, errno); } diff --git a/src/java.base/windows/native/libnio/ch/Net.c b/src/java.base/w= indows/native/libnio/ch/Net.c index 814f502c48a..5b9ecdd2c77 100644 --- a/src/java.base/windows/native/libnio/ch/Net.c +++ b/src/java.base/windows/native/libnio/ch/Net.c @@ -152,11 +152,13 @@ Java_sun_nio_ch_Net_canUseIPv6OptionsWithIPv4LocalAdd= ress0(JNIEnv* env, jclass c =20 JNIEXPORT jint JNICALL Java_sun_nio_ch_Net_socket0(JNIEnv *env, jclass cl, jboolean preferIPv6, - jboolean stream, jboolean reuse, jboolean fast= Loopback) + jboolean stream, jboolean reuse, jboolean fast= Loopback, + jboolean mptcp) { SOCKET s; int domain =3D (preferIPv6) ? AF_INET6 : AF_INET; =20 + (void)mptcp; /* not support MPTCP yet */ s =3D socket(domain, (stream ? SOCK_STREAM : SOCK_DGRAM), 0); if (s !=3D INVALID_SOCKET) { SetHandleInformation((HANDLE)s, HANDLE_FLAG_INHERIT, 0); --=20 2.48.1 From nobody Wed Sep 17 16:38:55 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 178EB30CD89 for ; Thu, 28 Aug 2025 08:47:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756370850; cv=none; b=E3BhHIT/cgLzv2S4T5RC8KNYD42DWTMF9u1tDXeShoTxkDFVeI1/7mFFbRXXJu86otpUb9kvsjaI8ABH2F6lMBIYOmrixglPzISnX2b5GWDTrcnsE1r71XSG387wNwH8KmjakZbHb7OfT3GgfL+OK0iYi1Nj4qv66j1cidGc2Ds= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756370850; c=relaxed/simple; bh=6A+RfvBwp+D+o6xjRzYPJsOrZd6D1xBmPUoJStqa3vE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=YigH8TytFkR7lhueqphkcHW4XArqROHg8tC87qkZD4htF7UmUatWxwkZIlydG8ozwsI5lnB1FV+LWzVwbYYprkb1grr4T/QT05mZmu5JBQ6Tss5HH+pVMmhdguB/4+pOKI56hwwfRDXpMH9xlv9pMbRte6hZqMo/tCCZtCSAuqI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=QhSrih69; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="QhSrih69" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 24318C4CEEB; Thu, 28 Aug 2025 08:47:27 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1756370849; bh=6A+RfvBwp+D+o6xjRzYPJsOrZd6D1xBmPUoJStqa3vE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=QhSrih694H2Pq4LqeZKD1z1vSfvZHYm9fqZWlaI7d3rrCsRuaZoE1+MsUaqZujd9q z+8z87RYiLG8DUVa9J02FnM6KnoyrYOhERWR06ehGKZ5RrSL/orBy+QpC238IhXrrG Us3ZRW64n809eqfNBnPDPNLXIThIjBI3xHpfHD28QTj8+7ylxzvL5HUOksBF3ze8w3 8m78cnkclb1BSoS7Be6A/cB+I426sFQ9yJTkMxesjtsZ3f4e3jMJWqQqfVdgOJxn2D 8u5qwcqDZeroIlgNKDWBvaVCIRJv6q3gKAGKeNTLC2jMBB1OVvBiVbvEeOWW/Fggmn d4lY5Fv205DYg== From: Geliang Tang To: mptcp@lists.linux.dev Cc: Geliang Tang , Gang Yan Subject: [PATCH mptcp-next v2 2/3] Add MPTCP support to Java Socket/ServerSocket APIs Date: Thu, 28 Aug 2025 16:47:14 +0800 Message-ID: X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: mptcp@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This patch integrates MPTCP support into the Java socket API by: 1. Adding new constructors in Socket and ServerSocket that accept an mptcp parameter to enable MPTCP explicitly 2. Extending NioSocketImpl to track the MPTCP state and use the appropriate native methods for socket creation 3. Propagating the MPTCP flag through the socket creation chain via new createImpl() factory methods 4. Ensuring backward compatibility by keeping existing constructors unchanged The implementation allows Java applications to use MPTCP seamlessly while maintaining full compatibility with existing TCP-based code. Co-Developed-by: Gang Yan Signed-off-by: Gang Yan Signed-off-by: Geliang Tang --- .../share/classes/java/net/ServerSocket.java | 52 +++++++++++++-- .../share/classes/java/net/Socket.java | 65 +++++++++++++++++-- .../share/classes/java/net/SocketImpl.java | 4 +- .../classes/sun/nio/ch/NioSocketImpl.java | 11 +++- 4 files changed, 115 insertions(+), 17 deletions(-) diff --git a/src/java.base/share/classes/java/net/ServerSocket.java b/src/j= ava.base/share/classes/java/net/ServerSocket.java index 945693ef65e..bc3c81a3663 100644 --- a/src/java.base/share/classes/java/net/ServerSocket.java +++ b/src/java.base/share/classes/java/net/ServerSocket.java @@ -107,7 +107,7 @@ protected ServerSocket(SocketImpl impl) { * @throws IOException IO error when opening the socket. */ public ServerSocket() throws IOException { - this.impl =3D createImpl(); + this.impl =3D createImpl(false); } =20 /** @@ -212,12 +212,52 @@ public ServerSocket(int port, int backlog) throws IOE= xception { */ @SuppressWarnings("this-escape") public ServerSocket(int port, int backlog, InetAddress bindAddr) throw= s IOException { + this(port, backlog, bindAddr, false); + } + + /** + * Create a server using mptcp (Multipath TCP) or TCP protocol = with + * the specified port, listen backlog, and local IP address to bind to. + * The bindAddr argument can be used on a multi-homed host for a + * ServerSocket that will only accept connect requests to one of its a= ddresses. + * If bindAddr is null, it will default accepting + * connections on any/all local addresses. + * The port must be between 0 and 65535, inclusive. + * A port number of {@code 0} means that the port number is + * automatically allocated, typically from an ephemeral port range. + * This port number can then be retrieved by calling + * {@link #getLocalPort getLocalPort}. + * + * The {@code backlog} argument is the requested maximum number of + * pending connections on the socket. Its exact semantics are implemen= tation + * specific. In particular, an implementation may impose a maximum len= gth + * or may choose to ignore the parameter altogether. The value provided + * should be greater than {@code 0}. If it is less than or equal to + * {@code 0}, then an implementation specific default will be used. + * + * The {@code mptcp} argument is used to control whether to create a s= ocket + * with MPTCP or TCP protocol. + * + * @param port the port number, or {@code 0} to use a port + * number that is automatically allocated. + * @param backlog requested maximum length of the queue of incoming + * connections. + * @param bindAddr the local InetAddress the server will bind to + * @param mptcp create a socket with MPTCP or TCP protocol. + * + * @throws IOException if an I/O error occurs when opening the socket. + * @throws IllegalArgumentException if the port parameter is outsi= de + * the specified range of valid port values, which is betw= een + * 0 and 65535, inclusive. + */ + @SuppressWarnings("this-escape") + public ServerSocket(int port, int backlog, InetAddress bindAddr, boole= an mptcp) throws IOException { if (port < 0 || port > 0xFFFF) throw new IllegalArgumentException("Port value out of range: "= + port); if (backlog < 1) backlog =3D 50; =20 - this.impl =3D createImpl(); + this.impl =3D createImpl(mptcp); try { bind(new InetSocketAddress(bindAddr, port), backlog); } catch (IOException e) { @@ -229,13 +269,15 @@ public ServerSocket(int port, int backlog, InetAddres= s bindAddr) throws IOExcept /** * Create a SocketImpl for a server socket. The SocketImpl is created * without an underlying socket. + * + * @param mptcp create a socket with MPTCP or TCP protocol. */ - private static SocketImpl createImpl() { + private static SocketImpl createImpl(boolean mptcp) { SocketImplFactory factory =3D ServerSocket.factory; if (factory !=3D null) { return factory.createSocketImpl(); } else { - return SocketImpl.createPlatformSocketImpl(true); + return SocketImpl.createPlatformSocketImpl(true, mptcp); } } =20 @@ -556,7 +598,7 @@ private SocketImpl platformImplAccept() throws IOExcept= ion { assert impl instanceof PlatformSocketImpl; =20 // create a new platform SocketImpl and accept the connection - SocketImpl psi =3D SocketImpl.createPlatformSocketImpl(false); + SocketImpl psi =3D SocketImpl.createPlatformSocketImpl(false, fals= e); implAccept(psi); return psi; } diff --git a/src/java.base/share/classes/java/net/Socket.java b/src/java.ba= se/share/classes/java/net/Socket.java index 692c3395f78..c58ad348fd6 100644 --- a/src/java.base/share/classes/java/net/Socket.java +++ b/src/java.base/share/classes/java/net/Socket.java @@ -168,7 +168,7 @@ private static boolean isOutputShutdown(int s) { * @since 1.1 */ public Socket() { - this.impl =3D createImpl(); + this.impl =3D createImpl(false); } =20 /** @@ -207,7 +207,7 @@ public Socket(Proxy proxy) { checkAddress (epoint.getAddress(), "Socket"); } // create a SOCKS or HTTP SocketImpl that delegates to a platf= orm SocketImpl - SocketImpl delegate =3D SocketImpl.createPlatformSocketImpl(fa= lse); + SocketImpl delegate =3D SocketImpl.createPlatformSocketImpl(fa= lse, false); impl =3D (type =3D=3D Proxy.Type.SOCKS) ? new SocksSocketImpl(= p, delegate) : new HttpConnectSocketImpl(= p, delegate, this); } else { @@ -215,7 +215,7 @@ public Socket(Proxy proxy) { // create a platform or custom SocketImpl for the DIRECT c= ase SocketImplFactory factory =3D Socket.factory; if (factory =3D=3D null) { - impl =3D SocketImpl.createPlatformSocketImpl(false); + impl =3D SocketImpl.createPlatformSocketImpl(false, fa= lse); } else { impl =3D factory.createSocketImpl(); } @@ -403,6 +403,39 @@ public Socket(String host, int port, boolean stream) t= hrows IOException { (SocketAddress) null, stream); } =20 + /** + * Creates a stream socket and connects it to the specified port + * number on the named host using Multipath TCP (MPTCP) or TCP + * protocol. + *

+ * If the specified host is {@code null} it is the equivalent of + * specifying the address as + * {@link java.net.InetAddress#getByName InetAddress.getByName}{@code = (null)}. + * In other words, it is equivalent to specifying an address of the + * loopback interface.

+ *

+ * If the application has specified a {@linkplain SocketImplFactory cl= ient + * socket implementation factory}, that factory's + * {@linkplain SocketImplFactory#createSocketImpl() createSocketImpl} + * method is called to create the actual socket implementation. Otherw= ise + * a system-default socket implementation is created. + * + * @param host the host name, or {@code null} for the loopbac= k address. + * @param port the port number. + * @param stream must be true, false is not allowed. + * @param mptcp create a socket with MPTCP or TCP protocol. + * @throws IOException if an I/O error occurs when creating the s= ocket. + * @throws IllegalArgumentException if the stream parameter is {@c= ode false} + * or if the port parameter is outside the specified range= of valid + * port values, which is between 0 and 65535, inclusive. + */ + @SuppressWarnings("this-escape") + public Socket(String host, int port, boolean stream, boolean mptcp) th= rows IOException { + this(host !=3D null ? new InetSocketAddress(host, port) : + new InetSocketAddress(InetAddress.getByName(null), port), + (SocketAddress) null, stream, mptcp); + } + /** * Creates a socket and connects it to the specified port number at * the specified IP address. @@ -442,6 +475,22 @@ public Socket(InetAddress host, int port, boolean stre= am) throws IOException { */ private Socket(SocketAddress address, SocketAddress localAddr, boolean= stream) throws IOException + { + this(address, localAddr, stream, false); + } + + /** + * Initialize a new Socket that is connected to the given remote addre= ss. + * The MPTCP or TCP protocol socket is optionally bound to a local add= ress + * before connecting. + * + * @param address the remote address to connect to + * @param localAddr the local address to bind to, can be null + * @param stream true for a stream socket, false for a datagram socket + * @param mptcp create a socket with MPTCP or TCP protocol + */ + private Socket(SocketAddress address, SocketAddress localAddr, boolean= stream, boolean mptcp) + throws IOException { Objects.requireNonNull(address); if (!stream) { @@ -451,7 +500,7 @@ private Socket(SocketAddress address, SocketAddress loc= alAddr, boolean stream) assert address instanceof InetSocketAddress; =20 // create the SocketImpl and the underlying socket - SocketImpl impl =3D createImpl(); + SocketImpl impl =3D createImpl(mptcp); impl.create(stream); =20 this.impl =3D impl; @@ -471,14 +520,16 @@ private Socket(SocketAddress address, SocketAddress l= ocalAddr, boolean stream) /** * Create a new SocketImpl for a connecting/client socket. The SocketI= mpl * is created without an underlying socket. + * + * @param mptcp create a socket with MPTCP or TCP protocol. */ - private static SocketImpl createImpl() { + private static SocketImpl createImpl(boolean mptcp) { SocketImplFactory factory =3D Socket.factory; if (factory !=3D null) { return factory.createSocketImpl(); } else { // create a SOCKS SocketImpl that delegates to a platform Sock= etImpl - SocketImpl delegate =3D SocketImpl.createPlatformSocketImpl(fa= lse); + SocketImpl delegate =3D SocketImpl.createPlatformSocketImpl(fa= lse, mptcp); return new SocksSocketImpl(delegate); } } @@ -498,7 +549,7 @@ private SocketImpl getImpl() throws SocketException { } SocketImpl impl =3D this.impl; if (impl =3D=3D null) { - this.impl =3D impl =3D createImpl(); + this.impl =3D impl =3D createImpl(false); } try { impl.create(true); diff --git a/src/java.base/share/classes/java/net/SocketImpl.java b/src/jav= a.base/share/classes/java/net/SocketImpl.java index 15c12457aac..f55034fb720 100644 --- a/src/java.base/share/classes/java/net/SocketImpl.java +++ b/src/java.base/share/classes/java/net/SocketImpl.java @@ -48,8 +48,8 @@ public abstract class SocketImpl implements SocketOptions= { * Creates an instance of platform's SocketImpl */ @SuppressWarnings("unchecked") - static S createPlatformSoc= ketImpl(boolean server) { - return (S) new NioSocketImpl(server); + static S createPlatformSoc= ketImpl(boolean server, boolean mptcp) { + return (S) new NioSocketImpl(server, mptcp); } =20 /** diff --git a/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java b/sr= c/java.base/share/classes/sun/nio/ch/NioSocketImpl.java index dd81b356738..2916560f1b6 100644 --- a/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java +++ b/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java @@ -86,6 +86,9 @@ public final class NioSocketImpl extends SocketImpl imple= ments PlatformSocketImp // true if this is a SocketImpl for a ServerSocket private final boolean server; =20 + // true if this is a SocketImpl for a MptcpServerSocket + private final boolean mptcp; + // Lock held when reading (also used when accepting or connecting) private final ReentrantLock readLock =3D new ReentrantLock(); =20 @@ -128,9 +131,11 @@ public final class NioSocketImpl extends SocketImpl im= plements PlatformSocketImp /** * Creates an instance of this SocketImpl. * @param server true if this is a SocketImpl for a ServerSocket + * @param mptcp enable MPTCP */ - public NioSocketImpl(boolean server) { + public NioSocketImpl(boolean server, boolean mptcp) { this.server =3D server; + this.mptcp =3D mptcp; } =20 /** @@ -468,9 +473,9 @@ protected void create(boolean stream) throws IOExceptio= n { throw new IOException("Already created"); FileDescriptor fd; if (server) { - fd =3D Net.serverSocket(); + fd =3D Net.serverSocket(Net.UNSPEC, mptcp); } else { - fd =3D Net.socket(); + fd =3D Net.socket(Net.UNSPEC, true, mptcp); } Runnable closer =3D closerFor(fd); this.fd =3D fd; --=20 2.48.1 From nobody Wed Sep 17 16:38:55 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A67AB1C01 for ; Thu, 28 Aug 2025 08:47:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756370853; cv=none; b=quJTeviJE/txs0Y9e+GJ5wQQeUGzBnPi4UI7tQwvtHFjl60GAhCUvxGgsJij5+d8X6tptvkcsHQnT8a5iLDfzL1zXlsZU6S/WrX0K8yV+So3rb27DoZaoFTbz4slNgd9qpMY0r1MJDJ7HlrnEXSs2GD/L/bQw34eXyDKbs7qVc8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756370853; c=relaxed/simple; bh=Jbit38OgaSNes+cq9bsPdRsjQnPdKCKhDdp4KVU19e8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=dPLkWe64yH4qZHbolFnMUcGoLq3tLxJArvEb1/17Vy8eTpHPPxJPwo2PCI1EGUqugoZMaZz2KqSwf03cS8+v0ekXVmt100UTpdo57AEDVxdWJMK/3OOCD2Ygnd9BB+k+FFWCKBBUxmn2spTccNEF1IHmnBBMMpgnyzkAgc7R6ac= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=FOJeYv2F; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="FOJeYv2F" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 2FD1EC4CEF5; Thu, 28 Aug 2025 08:47:29 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1756370851; bh=Jbit38OgaSNes+cq9bsPdRsjQnPdKCKhDdp4KVU19e8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=FOJeYv2FFZNeYrYV4NWaQ8Bp8StxpQoyfWviw4PWFwPT+iJC0Dza4YgZDcNIKuK7x dlkkN+/pccn/+W7RjecWstXDl4TOswKLZeEH6es4wCmI1Eim8R8/jvCPxnCDtep+aQ 1inCGIiCmVKMqCyjBqdiAdv1r8+5LOntfDhuDFgX603BbrfzGVIC++A98p0TzOpu6g B0YtGBJoXVWxlUc4m/CXDioiEA5drJf9zNOjhkMZVKgFlhoWbdE93F4KX0+ETRzYP3 FRexgw7qvmKw7FbfVjdM9825dDVL4ughjVQGVJ37rDzlJw0mxBmkgIlPV8K0RpIyvG XYiV8n3u4jPeg== From: Geliang Tang To: mptcp@lists.linux.dev Cc: Geliang Tang , Gang Yan Subject: [PATCH mptcp-next v2 3/3] Add test cases for MPTCP socket functionality Date: Thu, 28 Aug 2025 16:47:15 +0800 Message-ID: X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: mptcp@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This patch adds comprehensive test coverage for the MPTCP socket implementation by introducing two test programs: 1. MPTCPServer.java - an echo server that uses MPTCP-enabled ServerSocket 2. MPTCPClient.java - a client that connects to the server using MPTCP Socket The tests verify: - MPTCP socket creation and connection establishment - Bidirectional communication over MPTCP subflows - Proper stream handling and error conditions - Graceful shutdown and resource cleanup These tests demonstrate the usage of the new MPTCP-enabled constructors and provide a functional validation of the MPTCP implementation. Co-Developed-by: Gang Yan Signed-off-by: Gang Yan Signed-off-by: Geliang Tang --- .../java/net/ServerSocket/MPTCPServer.java | 58 +++++++++++++++++ test/jdk/java/net/Socket/MPTCPClient.java | 62 +++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 test/jdk/java/net/ServerSocket/MPTCPServer.java create mode 100644 test/jdk/java/net/Socket/MPTCPClient.java diff --git a/test/jdk/java/net/ServerSocket/MPTCPServer.java b/test/jdk/jav= a/net/ServerSocket/MPTCPServer.java new file mode 100644 index 00000000000..a28e4963943 --- /dev/null +++ b/test/jdk/java/net/ServerSocket/MPTCPServer.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License versi= on + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.*; +import java.net.*; + +public class MPTCPServer { + public static void main(String[] args) { + final int PORT =3D 12345; + + try (ServerSocket serverSocket =3D new ServerSocket(PORT, 50, null, true= )) { + System.out.println("Server started, waiting for client connection ..."); + + Socket clientSocket =3D serverSocket.accept(); + System.out.println("Client connected: " + clientSocket.getInetAddress()= ); + + BufferedReader in =3D new BufferedReader( + new InputStreamReader(clientSocket.getInputStream())); + PrintWriter out =3D new PrintWriter( + clientSocket.getOutputStream(), true); + + String inputLine; + while ((inputLine =3D in.readLine()) !=3D null) { + System.out.println("Received from client: " + inputLine); + out.println("Server response: " + inputLine); + + if ("exit".equalsIgnoreCase(inputLine)) { + break; + } + } + + clientSocket.close(); + System.out.println("Connection closed"); + } catch (IOException e) { + System.err.println("Server exception: " + e.getMessage()); + } + } +} diff --git a/test/jdk/java/net/Socket/MPTCPClient.java b/test/jdk/java/net/= Socket/MPTCPClient.java new file mode 100644 index 00000000000..18d815c39e7 --- /dev/null +++ b/test/jdk/java/net/Socket/MPTCPClient.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License versi= on + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.*; +import java.net.*; + +public class MPTCPClient { + public static void main(String[] args) { + final String HOST =3D "localhost"; + final int PORT =3D 12345; + =20 + try (Socket socket =3D new Socket(HOST, PORT, true, true)) { + System.out.println("Connected to server"); + =20 + BufferedReader in =3D new BufferedReader( + new InputStreamReader(socket.getInputStream())); + PrintWriter out =3D new PrintWriter( + socket.getOutputStream(), true); + =20 + BufferedReader stdIn =3D new BufferedReader( + new InputStreamReader(System.in)); + =20 + String userInput; + System.out.println("Enter message (type 'exit' to quit):"); + while ((userInput =3D stdIn.readLine()) !=3D null) { + out.println(userInput); + =20 + System.out.println("Server response: " + in.readLine()); + =20 + if ("exit".equalsIgnoreCase(userInput)) { + break; + } + } + =20 + System.out.println("Connection closed"); + } catch (UnknownHostException e) { + System.err.println("Host not found: " + HOST); + } catch (IOException e) { + System.err.println("Client I/O error: " + e.getMessage()); + } + } +} --=20 2.48.1