forked from kangjianwei/LearningJDK
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Socket.java
2362 lines (2108 loc) · 92.3 KB
/
Socket.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
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Copyright (c) 1995, 2016, 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 java.net;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.channels.SocketChannel;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Collections;
import java.util.Set;
import sun.net.ApplicationProxy;
/**
* This class implements client sockets (also called just "sockets").
* A socket is an endpoint for communication between two machines.
* <p>
* The actual work of the socket is performed by an instance of the {@code SocketImpl} class.
* An application, by changing the socket factory that creates the socket implementation,
* can configure itself to create sockets appropriate to the local firewall.
*
* @author unascribed
* @see java.net.Socket#setSocketImplFactory(java.net.SocketImplFactory)
* @see java.net.SocketImpl
* @see java.nio.channels.SocketChannel
* @since 1.0
*/
/*
* 面向连接的Socket(使用TCP Socket),用在客户端与服务端
*
* Socket用于在客户端和服务端之间进行通信,
* 该类的实例位于客户端时,我将其称为:[客户端Socket],
* 该类的实例位于服务端时,我将其称为:[服务端Socket],
*
* 对于[服务端Socket],又分为两类:
* 其中一类用来监听客户端的连接请求,我将其称为[服务端Socket(监听)],
* 还有一类用来与[客户端Socket]进行通信,我将其称为[服务端Socket(通信)],
* 对于同一个监听地址,[服务端Socket(监听)]只有一个,而[服务端Socket(通信)]会有多个。
*
* 注:当存在反向代理时,[服务端Socket(通信)]会与代理端的Socket通信。
*
* 特别注意的是,当前语境下,[服务端Socket]与ServerSocket要做区分。
*
*
* Linux上的TCP通信方法(本类中实现的API与底层API略有区别):
*
* 服务端 客户端
*
* sokcet() sokcet()
* ↓ ↓
* bind() bind()-可选
* ↓ ↓
* listen() ↓
* ↓ ↓
* accept() ↓
* ↓ ↓
* 阻塞,等待客户端连接 ↓
* ↓ ↓
* █ ←←←←←←←←←←←←← connect()
* ↓ ↓
* ↓ 客户端发出请求 ↓
* read() ←←←←←←←←←←←←← write()
* ↓ ↓
* 服务端处理请求 ↓
* 服务端做出响应 ↓
* ↓ ↓
* ↓ 客户端接收响应 ↓
* write() →→→→→→→→→→→→ read()
* ↓ ↓
* close() close()
*
*
* 注:客户端与服务端是一个相对的概念,没有绝对的客户端或绝对的服务端
*/
public class Socket implements Closeable {
/**
* The implementation of this Socket.
*/
/*
* [客户端Socket]和[服务端Socket(通信)]的"Socket委托",用来与远端通信
*
* 注:[客户端Socket]与[服务端Socket(通信)]互为远端
*/ SocketImpl impl;
/**
* Are we using an older SocketImpl?
*/
// 是否在使用旧式的"Socket委托"(JDK1.5起始,该值均为false)
private boolean oldImpl = false;
/**
* The factory for all client sockets.
*/
// "Socket委托"的工厂
private static SocketImplFactory factory = null;
/**
* Various states of this socket.
*/
private boolean created = false; // 指示[客户端Socket]/[服务端Socket(通信)]是否已创建
private boolean bound = false; // 指示[客户端Socket]/[服务端Socket(通信)]是否已绑定
private boolean connected = false; // 指示[客户端Socket]/[服务端Socket(通信)]是否已连接
private boolean closed = false; // 指示socket连接是否已关闭
private boolean shutIn = false; // 是否关闭了读取功能
private boolean shutOut = false; // 是否关闭了写入功能
private static Set<SocketOption<?>> options; // Socket配置参数
private static boolean optionsSet = false; // 懒加载,标记是否初始化了Socket配置参数
private Object closeLock = new Object();
/*▼ 构造器 ████████████████████████████████████████████████████████████████████████████████┓ */
/*
* ▶ 1
*
* 构造一个[客户端Socket],并对其执行【bind】和【connect】操作
*
* 【bind】过程是可选的,只有指定了非空的localAddr才显式执行【bind】过程
*
* address : [服务端Socket]地址
* localAddr: 客户端待绑定的本地地址
* stream : 创建TCP(true)/UDP(false)连接
*/
private Socket(SocketAddress address, SocketAddress localAddr, boolean stream) throws IOException {
// 初始化Socket的委托,并为其关联客户端Socket
setImpl();
// backward compatibility
if(address == null) {
throw new NullPointerException();
}
try {
// 创建[客户端Socket]文件,并记下其文件描述符
createImpl(stream);
// 如果设置了本地socket地址,则将其绑定到[客户端Socket]上
if(localAddr != null) {
// 对[客户端Socket]执行【bind】操作
bind(localAddr);
}
connect(address);
} catch(IOException | IllegalArgumentException | SecurityException e) {
try {
close();
} catch(IOException ce) {
e.addSuppressed(ce);
}
throw e;
}
}
/**
* Creates a stream socket and connects it to the specified port
* number on the named host.
* <p>
* 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. </p>
* <p>
* If the application has specified a server socket factory, that
* factory's {@code createSocketImpl} method is called to create
* the actual socket implementation. Otherwise a "plain" socket is created.
* <p>
* If there is a security manager, its
* {@code checkConnect} method is called
* with the host address and {@code port}
* as its arguments. This could result in a SecurityException.
*
* @param host the host name, or {@code null} for the loopback address.
* @param port the port number.
*
* @throws UnknownHostException if the IP address of
* the host could not be determined.
* @throws IOException if an I/O error occurs when creating the socket.
* @throws SecurityException if a security manager exists and its
* {@code checkConnect} method doesn't allow the operation.
* @throws IllegalArgumentException if the port parameter is outside
* the specified range of valid port values, which is between
* 0 and 65535, inclusive.
* @see java.net.Socket#setSocketImplFactory(java.net.SocketImplFactory)
* @see java.net.SocketImpl
* @see java.net.SocketImplFactory#createSocketImpl()
* @see SecurityManager#checkConnect
*/
/*
* ▶ 1-1
*
* 构造一个[客户端Socket](TCP),并对其执行【connect】操作
*
* host: 待连接的远端域名/地址;如果host为null,则默认连接到本地(环回地址)
* port: 待连接的远端端口
*/
public Socket(String host, int port) throws UnknownHostException, IOException {
this(host != null ? new InetSocketAddress(host, port) : new InetSocketAddress(InetAddress.getByName(null), port), null, true);
}
/**
* Creates a stream socket and connects it to the specified port
* number at the specified IP address.
* <p>
* If the application has specified a socket factory, that factory's
* {@code createSocketImpl} method is called to create the
* actual socket implementation. Otherwise a "plain" socket is created.
* <p>
* If there is a security manager, its
* {@code checkConnect} method is called
* with the host address and {@code port}
* as its arguments. This could result in a SecurityException.
*
* @param address the IP address.
* @param port the port number.
*
* @throws IOException if an I/O error occurs when creating the socket.
* @throws SecurityException if a security manager exists and its
* {@code checkConnect} method doesn't allow the operation.
* @throws IllegalArgumentException if the port parameter is outside
* the specified range of valid port values, which is between
* 0 and 65535, inclusive.
* @throws NullPointerException if {@code address} is null.
* @see java.net.Socket#setSocketImplFactory(java.net.SocketImplFactory)
* @see java.net.SocketImpl
* @see java.net.SocketImplFactory#createSocketImpl()
* @see SecurityManager#checkConnect
*/
/*
* ▶ 1-2
*
* 构造一个[客户端Socket](TCP),并对其执行【connect】操作
*
* address: 待连接的远端IP;如果address为null,则抛出异常
* port : 待连接的远端端口
*/
public Socket(InetAddress address, int port) throws IOException {
this(address != null ? new InetSocketAddress(address, port) : null, null, true);
}
/**
* Creates a socket and connects it to the specified remote host on
* the specified remote port. The Socket will also bind() to the local
* address and port supplied.
* <p>
* 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. </p>
* <p>
* A local port number of {@code zero} will let the system pick up a
* free port in the {@code bind} operation.</p>
* <p>
* If there is a security manager, its
* {@code checkConnect} method is called
* with the host address and {@code port}
* as its arguments. This could result in a SecurityException.
*
* @param host the name of the remote host, or {@code null} for the loopback address.
* @param port the remote port
* @param localAddr the local address the socket is bound to, or
* {@code null} for the {@code anyLocal} address.
* @param localPort the local port the socket is bound to, or
* {@code zero} for a system selected free port.
*
* @throws IOException if an I/O error occurs when creating the socket.
* @throws SecurityException if a security manager exists and its
* {@code checkConnect} method doesn't allow the connection
* to the destination, or if its {@code checkListen} method
* doesn't allow the bind to the local port.
* @throws IllegalArgumentException if the port parameter or localPort
* parameter is outside the specified range of valid port values,
* which is between 0 and 65535, inclusive.
* @see SecurityManager#checkConnect
* @since 1.1
*/
/*
* ▶ 1-3
*
* 构造一个[客户端Socket](TCP),并对其执行【bind】和【connect】操作
*
* host : 待连接的远端域名/地址;如果host为null,则默认连接到本地(环回地址)
* port : 待连接的远端端口
* localAddr: 客户端待绑定的本地地址
*/
public Socket(String host, int port, InetAddress localAddr, int localPort) throws IOException {
this(host != null ? new InetSocketAddress(host, port) : new InetSocketAddress(InetAddress.getByName(null), port), new InetSocketAddress(localAddr, localPort), true);
}
/**
* Creates a socket and connects it to the specified remote address on
* the specified remote port. The Socket will also bind() to the local
* address and port supplied.
* <p>
* If the specified local address is {@code null} it is the equivalent of
* specifying the address as the AnyLocal address
* (see {@link java.net.InetAddress#isAnyLocalAddress InetAddress.isAnyLocalAddress}{@code ()}).
* <p>
* A local port number of {@code zero} will let the system pick up a
* free port in the {@code bind} operation.</p>
* <p>
* If there is a security manager, its
* {@code checkConnect} method is called
* with the host address and {@code port}
* as its arguments. This could result in a SecurityException.
*
* @param address the remote address
* @param port the remote port
* @param localAddr the local address the socket is bound to, or
* {@code null} for the {@code anyLocal} address.
* @param localPort the local port the socket is bound to or
* {@code zero} for a system selected free port.
*
* @throws IOException if an I/O error occurs when creating the socket.
* @throws SecurityException if a security manager exists and its
* {@code checkConnect} method doesn't allow the connection
* to the destination, or if its {@code checkListen} method
* doesn't allow the bind to the local port.
* @throws IllegalArgumentException if the port parameter or localPort
* parameter is outside the specified range of valid port values,
* which is between 0 and 65535, inclusive.
* @throws NullPointerException if {@code address} is null.
* @see SecurityManager#checkConnect
* @since 1.1
*/
/*
* ▶ 1-4
*
* 构造一个[客户端Socket](TCP),并对其执行【bind】和【connect】操作
*
* address : 待连接的远端IP;如果address为null,则抛出异常
* port : 待连接的远端端口
* localAddr: 客户端待绑定的本地地址
*/
public Socket(InetAddress address, int port, InetAddress localAddr, int localPort) throws IOException {
this(address != null ? new InetSocketAddress(address, port) : null, new InetSocketAddress(localAddr, localPort), true);
}
/**
* Creates a stream socket and connects it to the specified port
* number on the named host.
* <p>
* 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. </p>
* <p>
* If the stream argument is {@code true}, this creates a
* stream socket. If the stream argument is {@code false}, it
* creates a datagram socket.
* <p>
* If the application has specified a server socket factory, that
* factory's {@code createSocketImpl} method is called to create
* the actual socket implementation. Otherwise a "plain" socket is created.
* <p>
* If there is a security manager, its
* {@code checkConnect} method is called
* with the host address and {@code port}
* as its arguments. This could result in a SecurityException.
* <p>
* If a UDP socket is used, TCP/IP related socket options will not apply.
*
* @param host the host name, or {@code null} for the loopback address.
* @param port the port number.
* @param stream a {@code boolean} indicating whether this is
* a stream socket or a datagram socket.
*
* @throws IOException if an I/O error occurs when creating the socket.
* @throws SecurityException if a security manager exists and its
* {@code checkConnect} method doesn't allow the operation.
* @throws IllegalArgumentException if the port parameter is outside
* the specified range of valid port values, which is between
* 0 and 65535, inclusive.
* @see java.net.Socket#setSocketImplFactory(java.net.SocketImplFactory)
* @see java.net.SocketImpl
* @see java.net.SocketImplFactory#createSocketImpl()
* @see SecurityManager#checkConnect
*
* @deprecated Use DatagramSocket instead for UDP transport.
*/
/*
* ▶ 1-5
*
* 构造一个[客户端Socket],并对其执行【connect】操作
*
* host : 待连接的远端域名/地址;如果host为null,则默认连接到本地(环回地址)
* port : 待连接的远端端口
* stream: 创建TCP(true)/UDP(false)连接
*
* ※ 该方法已过时
*/
@Deprecated
public Socket(String host, int port, boolean stream) throws IOException {
this(host != null ? new InetSocketAddress(host, port) : new InetSocketAddress(InetAddress.getByName(null), port), null, stream);
}
/**
* Creates a socket and connects it to the specified port number at
* the specified IP address.
* <p>
* If the stream argument is {@code true}, this creates a
* stream socket. If the stream argument is {@code false}, it
* creates a datagram socket.
* <p>
* If the application has specified a server socket factory, that
* factory's {@code createSocketImpl} method is called to create
* the actual socket implementation. Otherwise a "plain" socket is created.
*
* <p>If there is a security manager, its
* {@code checkConnect} method is called
* with {@code host.getHostAddress()} and {@code port}
* as its arguments. This could result in a SecurityException.
* <p>
* If UDP socket is used, TCP/IP related socket options will not apply.
*
* @param host the IP address.
* @param port the port number.
* @param stream if {@code true}, create a stream socket;
* otherwise, create a datagram socket.
*
* @throws IOException if an I/O error occurs when creating the socket.
* @throws SecurityException if a security manager exists and its
* {@code checkConnect} method doesn't allow the operation.
* @throws IllegalArgumentException if the port parameter is outside
* the specified range of valid port values, which is between
* 0 and 65535, inclusive.
* @throws NullPointerException if {@code host} is null.
* @see java.net.Socket#setSocketImplFactory(java.net.SocketImplFactory)
* @see java.net.SocketImpl
* @see java.net.SocketImplFactory#createSocketImpl()
* @see SecurityManager#checkConnect
*
* @deprecated Use DatagramSocket instead for UDP transport.
*/
/*
* ▶ 1-6
*
* 构造一个[客户端Socket],并对其执行【bind】和【connect】操作
*
* address: 待连接的远端IP;如果address为null,则抛出异常
* port : 待连接的远端端口
* stream : 创建TCP(true)/UDP(false)连接
*
* ※ 该方法已过时
*/
@Deprecated
public Socket(InetAddress address, int port, boolean stream) throws IOException {
this(address != null ? new InetSocketAddress(address, port) : null, new InetSocketAddress(0), stream);
}
/**
* Creates an unconnected socket, with the
* system-default type of SocketImpl.
*
* @revised 1.4
* @since 1.1
*/
// ▶ 2 构造一个未连接的Socket(会初始化其"Socket委托")
public Socket() {
// 初始化"Socket委托",并为其关联客户端Socket
setImpl();
}
/**
* Creates an unconnected socket, specifying the type of proxy, if any,
* that should be used regardless of any other settings.
* <P>
* If there is a security manager, its {@code checkConnect} method
* is called with the proxy host address and port number
* as its arguments. This could result in a SecurityException.
* <P>
* Examples:
* <UL> <LI>{@code Socket s = new Socket(Proxy.NO_PROXY);} will create
* a plain socket ignoring any other proxy configuration.</LI>
* <LI>{@code Socket s = new Socket(new Proxy(Proxy.Type.SOCKS, new InetSocketAddress("socks.mydom.com", 1080)));}
* will create a socket connecting through the specified SOCKS proxy
* server.</LI>
* </UL>
*
* @param proxy a {@link java.net.Proxy Proxy} object specifying what kind
* of proxying should be used.
*
* @throws IllegalArgumentException if the proxy is of an invalid type
* or {@code null}.
* @throws SecurityException if a security manager is present and
* permission to connect to the proxy is
* denied.
* @see java.net.ProxySelector
* @see java.net.Proxy
* @since 1.5
*/
/*
* ▶ 3 使用指定的代理构造一个未连接的Socket(会初始化其"Socket委托")
*
* 该Socket使用的"Socket委托"可能是:
* PlainSocketImpl - 无代理
* SocksSocketImpl - Socket代理
* HttpConnectSocketImpl(p) - HTTP Socket代理
*/
public Socket(Proxy proxy) {
// Create a copy of Proxy as a security measure
if(proxy == null) {
throw new IllegalArgumentException("Invalid Proxy");
}
// 通过工厂方法,创建代理对象
Proxy p = (proxy == Proxy.NO_PROXY) ? Proxy.NO_PROXY : ApplicationProxy.create(proxy);
// 获取代理类型
Proxy.Type type = p.type();
// 如果使用了有效的代理
if(type == Proxy.Type.SOCKS || type == Proxy.Type.HTTP) {
// 获取代理地址
InetSocketAddress epoint = (InetSocketAddress) p.address();
if(epoint.getAddress() != null) {
checkAddress(epoint.getAddress(), "Socket");
}
SecurityManager security = System.getSecurityManager();
if(security != null) {
if(epoint.isUnresolved()) {
epoint = new InetSocketAddress(epoint.getHostName(), epoint.getPort());
}
if(epoint.isUnresolved()) {
security.checkConnect(epoint.getHostName(), epoint.getPort());
} else {
security.checkConnect(epoint.getAddress().getHostAddress(), epoint.getPort());
}
}
impl = (type == Proxy.Type.SOCKS) ? new SocksSocketImpl(p) // 创建Socket(V4 & V5)实现
: new HttpConnectSocketImpl(p); // 创建HTTP Socket实现
// 为"Socket委托"关联本地Socket
impl.setSocket(this);
// 如果没有使用代理(NO_PROXY等同于没有代理)
} else {
if(p == Proxy.NO_PROXY) {
if(factory == null) {
impl = new PlainSocketImpl(); // 创建普通的Socket实现
// 为"Socket委托"关联本地Socket
impl.setSocket(this);
} else {
// 初始化客户端Socket的委托,并为其关联客户端Socket
setImpl();
}
} else {
throw new IllegalArgumentException("Invalid Proxy");
}
}
}
/**
* Creates an unconnected Socket with a user-specified SocketImpl.
*
* @param impl an instance of a <B>SocketImpl</B> the subclass wishes to use on the Socket.
*
* @throws SocketException if there is an error in the underlying protocol, such as a TCP error.
* @since 1.1
*/
/*
* ▶ 4 使用指定的"Socket委托"构造一个未连接的Socket
*
* 注:此构造器往往用来创建[服务端Socket(通信)]
*/
protected Socket(SocketImpl impl) throws SocketException {
this.impl = impl;
if(impl != null) {
checkOldImpl();
// 为"Socket委托"关联客户端Socket
this.impl.setSocket(this);
}
}
/*▲ 构造器 ████████████████████████████████████████████████████████████████████████████████┛ */
/*▼ Socket操作 ████████████████████████████████████████████████████████████████████████████████┓ */
/**
* Binds the socket to a local address.
* <P>
* If the address is {@code null}, then the system will pick up
* an ephemeral port and a valid local address to bind the socket.
*
* @param bindpoint the {@code SocketAddress} to bind to
*
* @throws IOException if the bind operation fails, or if the socket
* is already bound.
* @throws IllegalArgumentException if bindpoint is a
* SocketAddress subclass not supported by this socket
* @throws SecurityException if a security manager exists and its
* {@code checkListen} method doesn't allow the bind
* to the local port.
* @see #isBound
* @since 1.4
*/
/*
* 对[客户端Socket]执行【bind】操作。
*
* bindpoint: 待绑定的地址(ip+port)
*
* 注:[服务端Socket(通信)]会在accept期间完成绑定,故不会也不应再调用此方法
*/
public void bind(SocketAddress bindpoint) throws IOException {
if(isClosed()) {
throw new SocketException("Socket is closed");
}
if(!oldImpl && isBound()) {
throw new SocketException("Already bound");
}
if(bindpoint != null && (!(bindpoint instanceof InetSocketAddress))) {
throw new IllegalArgumentException("Unsupported address type");
}
// 对本地地址进行强转
InetSocketAddress epoint = (InetSocketAddress) bindpoint;
if(epoint != null && epoint.isUnresolved()) {
throw new SocketException("Unresolved address");
}
if(epoint == null) {
epoint = new InetSocketAddress(0);
}
InetAddress addr = epoint.getAddress(); // 获取待绑定的本地IP
int port = epoint.getPort(); // 获取待绑定的本地端口号
checkAddress(addr, "bind");
SecurityManager security = System.getSecurityManager();
if(security != null) {
security.checkListen(port);
}
/*
* 获取[客户端Socket]的"Socket委托"
*
* 注:[服务端Socket(通信)]会在accept期间完成绑定
*/
SocketImpl impl = getImpl();
// 为指定的"Socket委托"绑定IP与端口号
impl.bind(addr, port);
bound = true;
}
/**
* Connects this socket to the server.
*
* @param endpoint the {@code SocketAddress}
*
* @throws IOException if an error occurs during the connection
* @throws java.nio.channels.IllegalBlockingModeException if this socket has an associated channel,
* and the channel is in non-blocking mode
* @throws IllegalArgumentException if endpoint is null or is a
* SocketAddress subclass not supported by this socket
* @spec JSR-51
* @since 1.4
*/
/*
* 对本地Socket执行【connect】操作,以便连接到远端Socket;如果远端还未就绪,则立即返回。
*
* 本地Socket 远端Socket
* 1.不存在代理 [客户端Socket] [服务端Socket(通信)]
* 2.存在正向代理 [客户端Socket] 代理端Socket
* 3.存在反向代理 [服务端Socket(通信)] 代理端Socket
*
* endpoint: 在情形1和情形2下,该参数为[服务端Socket(通信)]地址;在情形3下,该参数为代理端Socket地址。
*
* 注:当不存在代理时,直接调用此方法完成[客户端Socket]到[服务端Socket(通信)]的连接;
* 当存在正向代理时,依然需要按照不存在代理时的情形调用此方法,但是在系统内部,实际完成的是[客户端Socket]到代理端Socket的连接;
* 当存在反向代理时,该方法会被【间接调用】,以完成[服务端Socket(通信)]到代理端Socket的连接
*/
public void connect(SocketAddress endpoint) throws IOException {
connect(endpoint, 0);
}
/**
* Connects this socket to the server with a specified timeout value.
* A timeout of zero is interpreted as an infinite timeout. The connection
* will then block until established or an error occurs.
*
* @param endpoint the {@code SocketAddress}
* @param timeout the timeout value to be used in milliseconds.
*
* @throws IOException if an error occurs during the connection
* @throws SocketTimeoutException if timeout expires before connecting
* @throws java.nio.channels.IllegalBlockingModeException if this socket has an associated channel,
* and the channel is in non-blocking mode
* @throws IllegalArgumentException if endpoint is null or is a
* SocketAddress subclass not supported by this socket
* @spec JSR-51
* @since 1.4
*/
/*
* 对本地Socket执行【connect】操作,以便连接到远端Socket;允许指定超时,以便等待远端就绪。
*
* 本地Socket 远端Socket
* 1.不存在代理 [客户端Socket] [服务端Socket(通信)]
* 2.存在正向代理 [客户端Socket] 代理端Socket
* 3.存在反向代理 [服务端Socket(通信)] 代理端Socket
*
* endpoint: 远端地址;在情形1和情形2下,该参数为[服务端Socket(通信)]地址;在情形3下,该参数为代理端Socket地址
* timeout : 超时时间,即允许连接等待的时间
*
* 注:当不存在代理时,直接调用此方法完成[客户端Socket]到[服务端Socket(通信)]的连接;
* 当存在正向代理时,依然需要按照不存在代理时的情形调用此方法,但是在系统内部,实际完成的是[客户端Socket]到代理端Socket的连接;
* 当存在反向代理时,该方法会被【间接调用】,以完成[服务端Socket(通信)]到代理端Socket的连接
*/
public void connect(SocketAddress endpoint, int timeout) throws IOException {
if(endpoint == null) {
throw new IllegalArgumentException("connect: The address can't be null");
}
if(timeout<0) {
throw new IllegalArgumentException("connect: timeout can't be negative");
}
if(isClosed()) {
throw new SocketException("Socket is closed");
}
if(!oldImpl && isConnected()) {
throw new SocketException("already connected");
}
if(!(endpoint instanceof InetSocketAddress)) {
throw new IllegalArgumentException("Unsupported address type");
}
// 记录远端的Socket地址(ip + port)
InetSocketAddress epoint = (InetSocketAddress) endpoint;
// 获取远端的IP
InetAddress addr = epoint.getAddress();
// 获取远端的端口号
int port = epoint.getPort();
checkAddress(addr, "connect");
SecurityManager security = System.getSecurityManager();
if(security != null) {
if(epoint.isUnresolved()) {
security.checkConnect(epoint.getHostName(), port);
} else {
security.checkConnect(addr.getHostAddress(), port);
}
}
/*
* 如果[客户端Socket]/[服务端Socket(通信)]还未创建
* 在"正常"使用中,created为false出现于存在反向代理的情形下
*/
if(!created) {
// 创建[客户端Socket]/[服务端Socket(通信)]文件,并记下其文件描述符;true指示创建的是TCP Socket
createImpl(true);
}
// 如果不是使用旧式的"Socket委托"(JDK1.5起始,oldImpl总是为false)
if(!oldImpl) {
impl.connect(epoint, timeout);
// 处理旧式"Socket委托"
} else if(timeout == 0) {
if(epoint.isUnresolved()) {
impl.connect(addr.getHostName(), port);
} else {
impl.connect(addr, port);
}
} else {
throw new UnsupportedOperationException("SocketImpl.connect(addr, timeout)");
}
connected = true;
/*
* If the socket was not bound before the connect, it is now because
* the kernel will have picked an ephemeral port & a local address
*/
bound = true;
}
/*▲ Socket操作 ████████████████████████████████████████████████████████████████████████████████┛ */
/*▼ 数据传输 ████████████████████████████████████████████████████████████████████████████████┓ */
/**
* Returns an input stream for this socket.
*
* <p> If this socket has an associated channel then the resulting input
* stream delegates all of its operations to the channel. If the channel
* is in non-blocking mode then the input stream's {@code read} operations
* will throw an {@link java.nio.channels.IllegalBlockingModeException}.
*
* <p>Under abnormal conditions the underlying connection may be
* broken by the remote host or the network software (for example
* a connection reset in the case of TCP connections). When a
* broken connection is detected by the network software the
* following applies to the returned input stream :-
*
* <ul>
*
* <li><p>The network software may discard bytes that are buffered
* by the socket. Bytes that aren't discarded by the network
* software can be read using {@link java.io.InputStream#read read}.
*
* <li><p>If there are no bytes buffered on the socket, or all
* buffered bytes have been consumed by
* {@link java.io.InputStream#read read}, then all subsequent
* calls to {@link java.io.InputStream#read read} will throw an
* {@link java.io.IOException IOException}.
*
* <li><p>If there are no bytes buffered on the socket, and the
* socket has not been closed using {@link #close close}, then
* {@link java.io.InputStream#available available} will
* return {@code 0}.
*
* </ul>
*
* <p> Closing the returned {@link java.io.InputStream InputStream}
* will close the associated socket.
*
* @return an input stream for reading bytes from this socket.
*
* @throws IOException if an I/O error occurs when creating the
* input stream, the socket is closed, the socket is
* not connected, or the socket input has been shutdown
* using {@link #shutdownInput()}
* @revised 1.4
* @spec JSR-51
*/
// 获取Socket输入流,从中读取数据
public InputStream getInputStream() throws IOException {
if(isClosed()) {
throw new SocketException("Socket is closed");
}
if(!isConnected()) {
throw new SocketException("Socket is not connected");
}
if(isInputShutdown()) {
throw new SocketException("Socket input is shutdown");
}
InputStream is = null;
try {
is = AccessController.doPrivileged(new PrivilegedExceptionAction<>() {
public InputStream run() throws IOException {
return impl.getInputStream();
}
});
} catch(PrivilegedActionException e) {
throw (IOException) e.getException();
}
return is;
}
/**
* Returns an output stream for this socket.
*
* <p> If this socket has an associated channel then the resulting output
* stream delegates all of its operations to the channel. If the channel
* is in non-blocking mode then the output stream's {@code write}
* operations will throw an {@link
* java.nio.channels.IllegalBlockingModeException}.
*
* <p> Closing the returned {@link java.io.OutputStream OutputStream}
* will close the associated socket.
*
* @return an output stream for writing bytes to this socket.
*
* @throws IOException if an I/O error occurs when creating the
* output stream or if the socket is not connected.
* @revised 1.4
* @spec JSR-51
*/
// 获取Socket输出流,向其写入数据
public OutputStream getOutputStream() throws IOException {
if(isClosed()) {
throw new SocketException("Socket is closed");
}
if(!isConnected()) {
throw new SocketException("Socket is not connected");
}
if(isOutputShutdown()) {
throw new SocketException("Socket output is shutdown");
}
OutputStream os = null;
try {
os = AccessController.doPrivileged(new PrivilegedExceptionAction<>() {
public OutputStream run() throws IOException {
return impl.getOutputStream();
}
});
} catch(PrivilegedActionException e) {
throw (IOException) e.getException();
}
return os;
}
/*▲ 数据传输 ████████████████████████████████████████████████████████████████████████████████┛ */
/*▼ 关闭 ████████████████████████████████████████████████████████████████████████████████┓ */
/**
* Closes this socket.
* <p>
* Any thread currently blocked in an I/O operation upon this socket
* will throw a {@link SocketException}.
* <p>
* Once a socket has been closed, it is not available for further networking
* use (i.e. can't be reconnected or rebound). A new socket needs to be
* created.
*
* <p> Closing this socket will also close the socket's
* {@link java.io.InputStream InputStream} and
* {@link java.io.OutputStream OutputStream}.
*
* <p> If this socket has an associated channel then the channel is closed
* as well.
*
* @throws IOException if an I/O error occurs when closing this socket.
* @revised 1.4
* @spec JSR-51
* @see #isClosed
*/
// 关闭socket连接
public synchronized void close() throws IOException {
synchronized(closeLock) {
if(isClosed()) {
return;
}
if(created) {
impl.close();
}
closed = true;
}
}
/**
* Places the input stream for this socket at "end of stream".