-
Notifications
You must be signed in to change notification settings - Fork 668
/
ExtendedSocketOptions.java
421 lines (353 loc) · 17.1 KB
/
ExtendedSocketOptions.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
/*
* Copyright (c) 2014, 2018, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 version
* 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.
*/
package jdk.net;
import java.io.FileDescriptor;
import java.net.SocketException;
import java.net.SocketOption;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import jdk.internal.misc.JavaIOFileDescriptorAccess;
import jdk.internal.misc.SharedSecrets;
/**
* Defines extended socket options, beyond those defined in {@link java.net.StandardSocketOptions}.
* These options may be platform specific.
*
* @since 1.8
*/
// 扩展的Socket参数, 这里的Socket参数可能是依赖平台实现的
public final class ExtendedSocketOptions {
/**
* Service level properties. When a security manager is installed,
* setting or getting this option requires a {@link NetworkPermission}
* {@code ("setOption.SO_FLOW_SLA")} or {@code "getOption.SO_FLOW_SLA"}
* respectively.
*/
public static final SocketOption<SocketFlow> SO_FLOW_SLA = new ExtSocketOption<>("SO_FLOW_SLA", SocketFlow.class);
/**
* Disable Delayed Acknowledgements.
*
* <p>
* This socket option can be used to reduce or disable delayed
* acknowledgments (ACKs). When {@code TCP_QUICKACK} is enabled, ACKs are
* sent immediately, rather than delayed if needed in accordance to normal
* TCP operation. This option is not permanent, it only enables a switch to
* or from {@code TCP_QUICKACK} mode. Subsequent operations of the TCP
* protocol will once again disable/enable {@code TCP_QUICKACK} mode
* depending on internal protocol processing and factors such as delayed ACK
* timeouts occurring and data transfer, therefore this option needs to be
* set with {@code setOption} after each operation of TCP on a given socket.
*
* <p>
* The value of this socket option is a {@code Boolean} that represents
* whether the option is enabled or disabled. The socket option is specific
* to stream-oriented sockets using the TCP/IP protocol. The exact semantics
* of this socket option are socket type and system dependent.
*
* @since 10
*/
public static final SocketOption<Boolean> TCP_QUICKACK = new ExtSocketOption<>("TCP_QUICKACK", Boolean.class);
/**
* Keep-Alive idle time.
*
* <p>
* The value of this socket option is an {@code Integer} that is the number
* of seconds of idle time before keep-alive initiates a probe. The socket
* option is specific to stream-oriented sockets using the TCP/IP protocol.
* The exact semantics of this socket option are system dependent.
*
* <p>
* When the {@link java.net.StandardSocketOptions#SO_KEEPALIVE
* SO_KEEPALIVE} option is enabled, TCP probes a connection that has been
* idle for some amount of time. The default value for this idle period is
* system dependent, but is typically 2 hours. The {@code TCP_KEEPIDLE}
* option can be used to affect this value for a given socket.
*
* @since 11
*/
public static final SocketOption<Integer> TCP_KEEPIDLE = new ExtSocketOption<>("TCP_KEEPIDLE", Integer.class);
/**
* Keep-Alive retransmission interval time.
*
* <p>
* The value of this socket option is an {@code Integer} that is the number
* of seconds to wait before retransmitting a keep-alive probe. The socket
* option is specific to stream-oriented sockets using the TCP/IP protocol.
* The exact semantics of this socket option are system dependent.
*
* <p>
* When the {@link java.net.StandardSocketOptions#SO_KEEPALIVE
* SO_KEEPALIVE} option is enabled, TCP probes a connection that has been
* idle for some amount of time. If the remote system does not respond to a
* keep-alive probe, TCP retransmits the probe after some amount of time.
* The default value for this retransmission interval is system dependent,
* but is typically 75 seconds. The {@code TCP_KEEPINTERVAL} option can be
* used to affect this value for a given socket.
*
* @since 11
*/
public static final SocketOption<Integer> TCP_KEEPINTERVAL = new ExtSocketOption<>("TCP_KEEPINTERVAL", Integer.class);
/**
* Keep-Alive retransmission maximum limit.
*
* <p>
* The value of this socket option is an {@code Integer} that is the maximum
* number of keep-alive probes to be sent. The socket option is specific to
* stream-oriented sockets using the TCP/IP protocol. The exact semantics of
* this socket option are system dependent.
*
* <p>
* When the {@link java.net.StandardSocketOptions#SO_KEEPALIVE
* SO_KEEPALIVE} option is enabled, TCP probes a connection that has been
* idle for some amount of time. If the remote system does not respond to a
* keep-alive probe, TCP retransmits the probe a certain number of times
* before a connection is considered to be broken. The default value for
* this keep-alive probe retransmit limit is system dependent, but is
* typically 8. The {@code TCP_KEEPCOUNT} option can be used to affect this
* value for a given socket.
*
* @since 11
*/
public static final SocketOption<Integer> TCP_KEEPCOUNT = new ExtSocketOption<>("TCP_KEEPCOUNT", Integer.class);
private static final PlatformSocketOptions platformSocketOptions = PlatformSocketOptions.get();
private static final boolean flowSupported = platformSocketOptions.flowSupported();
private static final boolean quickAckSupported = platformSocketOptions.quickAckSupported();
private static final boolean keepAliveOptSupported = platformSocketOptions.keepAliveOptionsSupported();
// 获取平台支持的socket扩展参数
private static final Set<SocketOption<?>> extendedOptions = options();
// FileDescriptor后门
private static final JavaIOFileDescriptorAccess fdAccess = SharedSecrets.getJavaIOFileDescriptorAccess();
static {
/* Registers the extended socket options with the base module. */
sun.net.ext.ExtendedSocketOptions.register(new sun.net.ext.ExtendedSocketOptions(extendedOptions) {
// 返回socket指定参数的值
@Override
public Object getOption(FileDescriptor socket, SocketOption<?> option) throws SocketException {
SecurityManager sm = System.getSecurityManager();
if(sm != null) {
sm.checkPermission(new NetworkPermission("getOption." + option.name()));
}
if(socket == null || !socket.valid()) {
throw new SocketException("socket closed");
}
if(option == SO_FLOW_SLA) {
assert flowSupported;
SocketFlow flow = SocketFlow.create();
getFlowOption(socket, flow);
return flow;
} else if(option == TCP_QUICKACK) {
return getQuickAckOption(socket);
} else if(option == TCP_KEEPCOUNT) {
return getTcpkeepAliveProbes(socket);
} else if(option == TCP_KEEPIDLE) {
return getTcpKeepAliveTime(socket);
} else if(option == TCP_KEEPINTERVAL) {
return getTcpKeepAliveIntvl(socket);
} else {
throw new InternalError("Unexpected option " + option);
}
}
// 为socket的指定参数设置值
@Override
public void setOption(FileDescriptor socket, SocketOption<?> option, Object value) throws SocketException {
SecurityManager sm = System.getSecurityManager();
if(sm != null) {
sm.checkPermission(new NetworkPermission("setOption." + option.name()));
}
if(socket == null || !socket.valid()) {
throw new SocketException("socket closed");
}
if(option == SO_FLOW_SLA) {
assert flowSupported;
SocketFlow flow = checkValueType(value, option.type());
setFlowOption(socket, flow);
} else if(option == TCP_QUICKACK) {
setQuickAckOption(socket, (boolean) value);
} else if(option == TCP_KEEPCOUNT) {
setTcpkeepAliveProbes(socket, (Integer) value);
} else if(option == TCP_KEEPIDLE) {
setTcpKeepAliveTime(socket, (Integer) value);
} else if(option == TCP_KEEPINTERVAL) {
setTcpKeepAliveIntvl(socket, (Integer) value);
} else {
throw new InternalError("Unexpected option " + option);
}
}
});
}
private ExtendedSocketOptions() {
}
// 返回平台支持的socket扩展参数
static Set<SocketOption<?>> options() {
Set<SocketOption<?>> options = new HashSet<>();
if(flowSupported) {
options.add(SO_FLOW_SLA);
}
if(quickAckSupported) {
options.add(TCP_QUICKACK);
}
if(keepAliveOptSupported) {
options.addAll(Set.of(TCP_KEEPCOUNT, TCP_KEEPIDLE, TCP_KEEPINTERVAL));
}
return Collections.unmodifiableSet(options);
}
@SuppressWarnings("unchecked")
private static <T> T checkValueType(Object value, Class<?> type) {
if(!type.isAssignableFrom(value.getClass())) {
String s = "Found: " + value.getClass() + ", Expected: " + type;
throw new IllegalArgumentException(s);
}
return (T) value;
}
private static void getFlowOption(FileDescriptor socket, SocketFlow flow) throws SocketException {
int status = platformSocketOptions.getFlowOption(fdAccess.get(socket), flow);
flow.status(status); // augment the given flow with the status
}
private static void setFlowOption(FileDescriptor socket, SocketFlow flow) throws SocketException {
int status = platformSocketOptions.setFlowOption(fdAccess.get(socket), flow.priority(), flow.bandwidth());
flow.status(status); // augment the given flow with the status
}
private static Object getQuickAckOption(FileDescriptor socket) throws SocketException {
return platformSocketOptions.getQuickAck(fdAccess.get(socket));
}
private static void setQuickAckOption(FileDescriptor socket, boolean enable) throws SocketException {
platformSocketOptions.setQuickAck(fdAccess.get(socket), enable);
}
private static int getTcpkeepAliveProbes(FileDescriptor socket) throws SocketException {
return platformSocketOptions.getTcpkeepAliveProbes(fdAccess.get(socket));
}
private static void setTcpkeepAliveProbes(FileDescriptor socket, int value) throws SocketException {
platformSocketOptions.setTcpkeepAliveProbes(fdAccess.get(socket), value);
}
private static int getTcpKeepAliveIntvl(FileDescriptor socket) throws SocketException {
return platformSocketOptions.getTcpKeepAliveIntvl(fdAccess.get(socket));
}
private static void setTcpKeepAliveIntvl(FileDescriptor socket, int value) throws SocketException {
platformSocketOptions.setTcpKeepAliveIntvl(fdAccess.get(socket), value);
}
private static int getTcpKeepAliveTime(FileDescriptor socket) throws SocketException {
return platformSocketOptions.getTcpKeepAliveTime(fdAccess.get(socket));
}
private static void setTcpKeepAliveTime(FileDescriptor socket, int value) throws SocketException {
platformSocketOptions.setTcpKeepAliveTime(fdAccess.get(socket), value);
}
// windows平台的socket扩展参数
static class PlatformSocketOptions {
private static final PlatformSocketOptions instance = create();
protected PlatformSocketOptions() {
}
static PlatformSocketOptions get() {
return instance;
}
int getFlowOption(int fd, SocketFlow f) throws SocketException {
throw new UnsupportedOperationException("unsupported socket option");
}
int setFlowOption(int fd, int priority, long bandwidth) throws SocketException {
throw new UnsupportedOperationException("unsupported socket option");
}
boolean flowSupported() {
return false;
}
boolean quickAckSupported() {
return false;
}
boolean keepAliveOptionsSupported() {
return false;
}
boolean getQuickAck(int fd) throws SocketException {
throw new UnsupportedOperationException("unsupported TCP_QUICKACK option");
}
void setQuickAck(int fd, boolean on) throws SocketException {
throw new UnsupportedOperationException("unsupported TCP_QUICKACK option");
}
int getTcpkeepAliveProbes(int socket) throws SocketException {
throw new UnsupportedOperationException("unsupported TCP_KEEPCNT option");
}
void setTcpkeepAliveProbes(int socket, final int value) throws SocketException {
throw new UnsupportedOperationException("unsupported TCP_KEEPCNT option");
}
int getTcpKeepAliveTime(int socket) throws SocketException {
throw new UnsupportedOperationException("unsupported TCP_KEEPIDLE option");
}
void setTcpKeepAliveTime(int socket, final int value) throws SocketException {
throw new UnsupportedOperationException("unsupported TCP_KEEPIDLE option");
}
int getTcpKeepAliveIntvl(int socket) throws SocketException {
throw new UnsupportedOperationException("unsupported TCP_KEEPINTVL option");
}
void setTcpKeepAliveIntvl(int socket, final int value) throws SocketException {
throw new UnsupportedOperationException("unsupported TCP_KEEPINTVL option");
}
@SuppressWarnings("unchecked")
private static PlatformSocketOptions newInstance(String cn) {
Class<PlatformSocketOptions> c;
try {
c = (Class<PlatformSocketOptions>) Class.forName(cn);
return c.getConstructor(new Class<?>[]{}).newInstance();
} catch(ReflectiveOperationException x) {
throw new AssertionError(x);
}
}
// 根据当前平台类型,返回对应的平台参数集
private static PlatformSocketOptions create() {
String osname = AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run() {
return System.getProperty("os.name");
}
});
if("SunOS".equals(osname)) {
return newInstance("jdk.net.SolarisSocketOptions");
} else if("Linux".equals(osname)) {
return newInstance("jdk.net.LinuxSocketOptions");
} else if(osname.startsWith("Mac")) {
return newInstance("jdk.net.MacOSXSocketOptions");
} else {
return new PlatformSocketOptions();
}
}
}
private static class ExtSocketOption<T> implements SocketOption<T> {
private final String name;
private final Class<T> type;
ExtSocketOption(String name, Class<T> type) {
this.name = name;
this.type = type;
}
@Override
public String name() {
return name;
}
@Override
public Class<T> type() {
return type;
}
@Override
public String toString() {
return name;
}
}
}