All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bitcoinj.net.BlockingClient Maven / Gradle / Ivy

There is a newer version: 0.15-cm06
Show newest version
/*
 * Copyright 2013 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.bitcoinj.net;

import com.google.common.util.concurrent.*;
import org.bitcoinj.core.*;
import org.slf4j.*;

import javax.annotation.*;
import javax.net.*;
import java.io.*;
import java.net.*;
import java.nio.*;
import java.util.*;

import static com.google.common.base.Preconditions.*;

/**
 * 

Creates a simple connection to a server using a {@link StreamConnection} to process data.

* *

Generally, using {@link NioClient} and {@link NioClientManager} should be preferred over {@link BlockingClient} * and {@link BlockingClientManager}, unless you wish to connect over a proxy or use some other network settings that * cannot be set using NIO.

*/ public class BlockingClient implements MessageWriteTarget { private static final org.slf4j.Logger log = LoggerFactory.getLogger(BlockingClient.class); private static final int BUFFER_SIZE_LOWER_BOUND = 4096; private static final int BUFFER_SIZE_UPPER_BOUND = 65536; private Socket socket; private volatile boolean vCloseRequested = false; private SettableFuture connectFuture; /** *

Creates a new client to the given server address using the given {@link StreamConnection} to decode the data. * The given connection MUST be unique to this object. This does not block while waiting for the connection to * open, but will call either the {@link StreamConnection#connectionOpened()} or * {@link StreamConnection#connectionClosed()} callback on the created network event processing thread.

* * @param connectTimeoutMillis The connect timeout set on the connection (in milliseconds). 0 is interpreted as no * timeout. * @param socketFactory An object that creates {@link Socket} objects on demand, which may be customised to control * how this client connects to the internet. If not sure, use SocketFactory.getDefault() * @param clientSet A set which this object will add itself to after initialization, and then remove itself from */ public BlockingClient(final SocketAddress serverAddress, final StreamConnection connection, final int connectTimeoutMillis, final SocketFactory socketFactory, @Nullable final Set clientSet) throws IOException { connectFuture = SettableFuture.create(); // Try to fit at least one message in the network buffer, but place an upper and lower limit on its size to make // sure it doesnt get too large or have to call read too often. connection.setWriteTarget(this); socket = socketFactory.createSocket(); final Context context = Context.get(); Thread t = new Thread() { @Override public void run() { Context.propagate(context); if (clientSet != null) clientSet.add(BlockingClient.this); try { socket.connect(serverAddress, connectTimeoutMillis); connection.connectionOpened(); connectFuture.set(serverAddress); InputStream stream = socket.getInputStream(); runReadLoop(stream, connection); } catch (Exception e) { if (!vCloseRequested) { log.error("Error trying to open/read from connection: {}: {}", serverAddress, e.getMessage()); connectFuture.setException(e); } } finally { try { socket.close(); } catch (IOException e1) { // At this point there isn't much we can do, and we can probably assume the channel is closed } if (clientSet != null) clientSet.remove(BlockingClient.this); connection.connectionClosed(); } } }; t.setName("BlockingClient network thread for " + serverAddress); t.setDaemon(true); t.start(); } /** * A blocking call that never returns, except by throwing an exception. It reads bytes from the input stream * and feeds them to the provided {@link StreamConnection}, for example, a {@link Peer}. */ public static void runReadLoop(InputStream stream, StreamConnection connection) throws Exception { ByteBuffer dbuf = ByteBuffer.allocateDirect(Math.min(Math.max(connection.getMaxMessageSize(), BUFFER_SIZE_LOWER_BOUND), BUFFER_SIZE_UPPER_BOUND)); byte[] readBuff = new byte[dbuf.capacity()]; while (true) { // TODO Kill the message duplication here checkState(dbuf.remaining() > 0 && dbuf.remaining() <= readBuff.length); int read = stream.read(readBuff, 0, Math.max(1, Math.min(dbuf.remaining(), stream.available()))); if (read == -1) return; dbuf.put(readBuff, 0, read); // "flip" the buffer - setting the limit to the current position and setting position to 0 dbuf.flip(); // Use connection.receiveBytes's return value as a double-check that it stopped reading at the right // location int bytesConsumed = connection.receiveBytes(dbuf); checkState(dbuf.position() == bytesConsumed); // Now drop the bytes which were read by compacting dbuf (resetting limit and keeping relative // position) dbuf.compact(); } } /** * Closes the connection to the server, triggering the {@link StreamConnection#connectionClosed()} * event on the network-handling thread where all callbacks occur. */ @Override public void closeConnection() { // Closes the channel, triggering an exception in the network-handling thread triggering connectionClosed() try { vCloseRequested = true; socket.close(); } catch (IOException e) { throw new RuntimeException(e); } } @Override public synchronized void writeBytes(byte[] message) throws IOException { try { OutputStream stream = socket.getOutputStream(); stream.write(message); stream.flush(); } catch (IOException e) { log.error("Error writing message to connection, closing connection", e); closeConnection(); throw e; } } /** Returns a future that completes once connection has occurred at the socket level or with an exception if failed to connect. */ public ListenableFuture getConnectFuture() { return connectFuture; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy