提交 a1f890af authored 作者: Thomas Mueller's avatar Thomas Mueller

A workaround for a Windows socket problem has been implemented.

上级 43c91a62
...@@ -18,7 +18,8 @@ Change Log ...@@ -18,7 +18,8 @@ Change Log
<h1>Change Log</h1> <h1>Change Log</h1>
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul><li>The Recover tool did not convert correctly convert CLOB data with non-ASCII characters. <ul><li>A workaround for a Windows socket problem has been implemented. Thanks a lot to Sergi Vladykin.
</li><li>The Recover tool did not convert correctly convert CLOB data with non-ASCII characters.
</li><li>Tools: the method run(String... args) has been renamed to runTool(String... args) </li><li>Tools: the method run(String... args) has been renamed to runTool(String... args)
so it can't be confused with run(). so it can't be confused with run().
</li><li>Server.startWebServer(Connection) was not working as expected. </li><li>Server.startWebServer(Connection) was not working as expected.
......
...@@ -496,6 +496,14 @@ public class SysProperties { ...@@ -496,6 +496,14 @@ public class SysProperties {
*/ */
public static final boolean SHARE_LINKED_CONNECTIONS = getBooleanSetting("h2.shareLinkedConnections", true); public static final boolean SHARE_LINKED_CONNECTIONS = getBooleanSetting("h2.shareLinkedConnections", true);
/**
* System property <code>h2.socketConnectRetry</code> (default: 16).<br />
* The number of times to retry opening a socket. Windows sometimes fails
* to open a socket, see bug
* http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6213296
*/
public static final int SOCKET_CONNECT_RETRY = getIntSetting("h2.socketConnectRetry", 16);
/** /**
* System property <code>h2.socketConnectTimeout</code> (default: 2000).<br /> * System property <code>h2.socketConnectTimeout</code> (default: 2000).<br />
* The timeout in milliseconds to connect to a server. * The timeout in milliseconds to connect to a server.
......
...@@ -102,7 +102,7 @@ public class NetUtils { ...@@ -102,7 +102,7 @@ public class NetUtils {
* @return the socket * @return the socket
*/ */
public static Socket createSocket(InetAddress address, int port, boolean ssl) throws IOException { public static Socket createSocket(InetAddress address, int port, boolean ssl) throws IOException {
for (;;) { for (int i = 0;; i++) {
try { try {
if (ssl) { if (ssl) {
return SecureSocketFactory.createSocket(address, port); return SecureSocketFactory.createSocket(address, port);
...@@ -112,9 +112,17 @@ public class NetUtils { ...@@ -112,9 +112,17 @@ public class NetUtils {
SysProperties.SOCKET_CONNECT_TIMEOUT); SysProperties.SOCKET_CONNECT_TIMEOUT);
return socket; return socket;
} catch (BindException e) { } catch (BindException e) {
// Workaround for Windows problem with frequent connections: if (i >= SysProperties.SOCKET_CONNECT_RETRY) {
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6213296 throw e;
// trying to connect again }
// wait a bit and retry
try {
// sleep at most 256 ms
long sleep = Math.min(256, i * i);
Thread.sleep(sleep);
} catch (InterruptedException e2) {
// ignore
}
} }
} }
} }
......
/** /*
* * Copyright 2004-2010 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: Sergi Vladykin
*/ */
package org.h2.test.unit; package org.h2.test.unit;
...@@ -9,126 +12,114 @@ import java.net.Socket; ...@@ -9,126 +12,114 @@ import java.net.Socket;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import org.h2.engine.Constants;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.util.NetUtils; import org.h2.util.NetUtils;
/** /**
* Test the network utilities.
*
* @author Sergi Vladykin * @author Sergi Vladykin
*/ */
public class TestNetUtils extends TestBase { public class TestNetUtils extends TestBase {
private static final int PORT = 9111;
/** /**
* @param args * Run just this test.
*
* @param a ignored
*/ */
public static void main(String[] args) throws Exception { public static void main(String... a) throws Exception {
TestBase.createCaller().init().test(); TestBase.createCaller().init().test();
} }
@Override
public void test() throws Exception { public void test() throws Exception {
testFrequentConnections(false); testFrequentConnections(false, 1000);
testFrequentConnections(true); testFrequentConnections(true, 100);
testFrequentConnections(false, 1000);
testFrequentConnections(true, 100);
} }
private void testFrequentConnections(boolean ssl) throws Exception { private void testFrequentConnections(boolean ssl, int count) throws Exception {
final ServerSocket serverSock = NetUtils.createServerSocket(Constants.DEFAULT_TCP_PORT, ssl); final ServerSocket serverSocket = NetUtils.createServerSocket(PORT, ssl);
final AtomicInteger counter = new AtomicInteger(count);
Thread serverThread = new Thread() { Thread serverThread = new Thread() {
@Override
public void run() { public void run() {
while (!isInterrupted()) { while (!isInterrupted()) {
try { try {
Socket socket = serverSock.accept(); Socket socket = serverSocket.accept();
System.out.println("opened " + counter);
socket.close(); socket.close();
} catch (Exception e) { } catch (Exception e) {
// ignore // ignore
} }
} }
System.out.println("stopped ");
} }
}; };
serverThread.start(); serverThread.start();
// System.out.println("Server started.");
AtomicInteger counter = new AtomicInteger();
try { try {
Set<ConnectWorker> workers = new HashSet<ConnectWorker>(); Set<ConnectWorker> workers = new HashSet<ConnectWorker>();
for (int i = 0; i < 10; i++) { for (int i = 0; i < 1; i++) {
workers.add(new ConnectWorker(ssl, workers, counter)); workers.add(new ConnectWorker(ssl, counter));
} }
// ensure the server is started
Thread.sleep(100);
for (ConnectWorker worker : workers) { for (ConnectWorker worker : workers) {
worker.start(); worker.start();
} }
// System.out.println("Workers started.");
Exception exception = null;
for (ConnectWorker worker : workers) { for (ConnectWorker worker : workers) {
worker.join(); worker.join();
if (exception == null) { Exception e = worker.getException();
exception = worker.getException(); if (e != null) {
// if (exception != null) { e.printStackTrace();
// System.out.println("Exception set.");
// }
} }
} }
// System.out.println("All joined.");
if (exception != null) {
throw exception;
}
} finally { } finally {
serverThread.interrupt();
try { try {
serverSock.close(); serverSocket.close();
} catch (Exception e) { } catch (Exception e) {
// ignore // ignore
} }
// System.out.println("Server stopped."); serverThread.interrupt();
serverThread.join();
} }
} }
/** /**
* * A worker thread to test connecting.
*/ */
private class ConnectWorker extends Thread { private class ConnectWorker extends Thread {
private static final int MAX_CONNECT_COUNT = 10000;
private final boolean ssl; private final boolean ssl;
private final Set<ConnectWorker> workers;
private final AtomicInteger counter; private final AtomicInteger counter;
private Exception exception;
private volatile Exception exception; public ConnectWorker(boolean ssl, AtomicInteger counter) {
public ConnectWorker(boolean ssl, Set<ConnectWorker> workers, AtomicInteger counter) {
this.ssl = ssl; this.ssl = ssl;
this.workers = workers;
this.counter = counter; this.counter = counter;
} }
@Override
public void run() { public void run() {
try { try {
while (!isInterrupted() && counter.incrementAndGet() < MAX_CONNECT_COUNT) { while (!isInterrupted() && counter.decrementAndGet() > 0) {
Socket sock = NetUtils.createSocket("127.0.0.1", Constants.DEFAULT_TCP_PORT, ssl); Socket socket = NetUtils.createLoopbackSocket(PORT, ssl);
// System.out.println(COUNTER.get());
try { try {
sock.close(); socket.close();
} catch (IOException e) { } catch (IOException e) {
// ignore // ignore
} }
} }
} catch (Exception e) { } catch (Exception e) {
this.exception = e; exception = new Exception("count: " + counter, e);
for (ConnectWorker worker : workers) {
worker.interrupt();
}
} }
} }
/**
* @return the exception
*/
public Exception getException() { public Exception getException() {
return exception; return exception;
} }
} }
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论