jdk 源码分析(19)java net包简单分析
jdk 源码分析(18)java net包只能简单分析,因为代码走到后面都变成了native方法,我去openJDK,以及其他语言的实现都没有找到底层怎么实现的,如果你知道,告诉我一声。
这里只能简单分析了。
1)通信代码:
服务端:
int port = 8919;
Socket socket =null;
ServerSocket server=null;
try {
server = new ServerSocket(port);
while(true) {
socket = server.accept();
//SocketInputStream
Reader reader = new InputStreamReader(socket.getInputStream());
char chars[] = new char[1024];
int len;
StringBuilder builder = new StringBuilder();
while ((len = reader.read(chars)) != -1) {
builder.append(new String(chars, 0, len));
}
System.out.println("Receive from client message=: " + builder);
reader.close();
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
socket.close();
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
客户端代码:
String host = "127.0.0.1";
int port = 8919;
try {
Socket client = new Socket(host, port);
Writer writer = new OutputStreamWriter(client.getOutputStream());
writer.write("Hello From Client");
writer.flush();
writer.close();
client.close();
} catch (IOException e) {
e.printStackTrace();
}
服务端
1)new ServerSocke
当new ServerSocket时候如果没有带入addr 将使用0.0.0.0作为默认的ip
最后new的socket 是一个native方法
PlainSocketImpl.java
static native int socket0(boolean stream, boolean v6Only)
或者TwoStacksPlainSocketImpl
native void socketCreate(boolean isServer) throws IOException;
上面两个类到底使用哪里实现类呢。下面的代码告诉我们有
useDualStackImpl决定。
PlainSocketImpl() {
if (useDualStackImpl) {
impl = new DualStackPlainSocketImpl(exclusiveBind);
} else {
impl = new TwoStacksPlainSocketImpl(exclusiveBind);
}
}
这个值由系统决定。如果系统版本>=6.0或者IPV64 用
DualStackPlainSocketImpl。
static {
java.security.AccessController.doPrivileged( new PrivilegedAction<Object>() {
public Object run() {
version = 0;
try {
version = Float.parseFloat(System.getProperties().getProperty("os.version"));
preferIPv4Stack = Boolean.parseBoolean(
System.getProperties().getProperty("java.net.preferIPv4Stack"));
exclBindProp = System.getProperty("sun.net.useExclusiveBind");
} catch (NumberFormatException e ) {
assert false : e;
}
return null; // nothing to return
} });
// (version >= 6.0) implies Vista or greater.
if (version >= 6.0 && !preferIPv4Stack) {
useDualStackImpl = true;
}
if (exclBindProp != null) {
// sun.net.useExclusiveBind is true
exclusiveBind = exclBindProp.length() == 0 ? true
: Boolean.parseBoolean(exclBindProp);
} else if (version < 6.0) {
exclusiveBind = false;
}
}
我的系统win7 所以有
DualStackPlainSocketImpl决定。
2)accpet()
接着accpet 也是一个native方法
void socketAccept(SocketImpl s) throws IOExce2ption {
//如果
timeout没有设置就直接进入阻塞,一直等到数据过来。会阻塞是整个线程if (timeout <= 0) {
newfd = accept0(nativefd, isaa);
} else {
configureBlocking(nativefd, false);
try {
//否则等待有限时间,如果没有就退出
waitForNewConnection(nativefd, timeout);
newfd = accept0(nativefd, isaa);
if (newfd != -1) {
configureBlocking(newfd, true);
}
} finally {
configureBlocking(nativefd, true);
}
}
}
这里就是io不好的地方,会一直阻塞等待,而且如果同时来了两个线程,一次只能处理一个请求。另一个只能等待了。如果一般收到一个请求后会new 一个新的线程来处理,在新的线程里完成数据读写操作。,或者线程池来处理接收的数据。
3)读写数据。
socket.getInputStream()和socket.getOutputStream()
底层定义了SocketInputStream和SocketOutputStream不过read和write也是native 的。
客服端
客户new Socket()后,会去和指定的ip和端口连接,这个又是一个native 的方法,而且会一直尝试去连接。阻塞线程。
void socketConnect(InetAddress address, int port, int timeout)
throws IOException {
int nativefd = checkAndReturnNativeFD();
if (address == null)
throw new NullPointerException("inet address argument is null.");
int connectResult;
//不带超时的连接,
if (timeout <= 0) {
connectResult = connect0(nativefd, address, port);
} else {
//
带超时的连接,configureBlocking(nativefd, false);
try {
connectResult = connect0(nativefd, address, port);
if (connectResult == WOULDBLOCK) {
waitForConnect(nativefd, timeout);
}
} finally {
configureBlocking(nativefd, true);
}
}
/*
* We need to set the local port field. If bind was called
* previous to the connect (by the client) then localport field
* will already be set.
*/
if (localport == 0)
localport = localPort0(nativefd);
}
net里的网络连接是阻塞的,如果服务器端使用,一般每获取一个请求后启动一个新的线程,然后由新的线程处理数据。不再服务器接收请求的那里直接处理。当然也可以像nio那样,将一个线程专门处理接收,然后启动一个线程池,专门接收数据,然后一个线程池处理数据,一个线程池会写数据。