disapproval of revision 'bd09bb36a90e766b3a406d78055d427a6200dd41'
This commit is contained in:
@ -2,7 +2,6 @@ package net.i2p.client.streaming;
|
||||
|
||||
import java.net.ConnectException;
|
||||
|
||||
import java.net.SocketTimeoutException;
|
||||
import net.i2p.I2PException;
|
||||
|
||||
/**
|
||||
@ -10,40 +9,26 @@ import net.i2p.I2PException;
|
||||
*
|
||||
*/
|
||||
public interface I2PServerSocket {
|
||||
/**
|
||||
* Closes the socket.
|
||||
*/
|
||||
public void close() throws I2PException;
|
||||
|
||||
/**
|
||||
* Closes the socket.
|
||||
*/
|
||||
public void close() throws I2PException;
|
||||
/**
|
||||
* Waits for the next socket connecting. If a remote user tried to make a
|
||||
* connection and the local application wasn't .accept()ing new connections,
|
||||
* they should get refused (if .accept() doesnt occur in some small period)
|
||||
*
|
||||
* @return a connected I2PSocket
|
||||
*
|
||||
* @throws I2PException if there is a problem with reading a new socket
|
||||
* from the data available (aka the I2PSession closed, etc)
|
||||
* @throws ConnectException if the I2PServerSocket is closed
|
||||
*/
|
||||
public I2PSocket accept() throws I2PException, ConnectException;
|
||||
|
||||
/**
|
||||
* Waits for the next socket connecting. If a remote user tried to make a
|
||||
* connection and the local application wasn't .accept()ing new connections,
|
||||
* they should get refused (if .accept() doesnt occur in some small period)
|
||||
*
|
||||
* @return a connected I2PSocket
|
||||
*
|
||||
* @throws I2PException if there is a problem with reading a new socket
|
||||
* from the data available (aka the I2PSession closed, etc)
|
||||
* @throws ConnectException if the I2PServerSocket is closed
|
||||
* @throws SocketTimeoutException
|
||||
*/
|
||||
public I2PSocket accept() throws I2PException, ConnectException, SocketTimeoutException;
|
||||
|
||||
/**
|
||||
* Set Sock Option accept timeout
|
||||
* @param x
|
||||
*/
|
||||
public void setSoTimeout(long x);
|
||||
|
||||
/**
|
||||
* Get Sock Option accept timeout
|
||||
* @return timeout
|
||||
*/
|
||||
public long getSoTimeout();
|
||||
|
||||
/**
|
||||
* Access the manager which is coordinating the server socket
|
||||
*/
|
||||
public I2PSocketManager getManager();
|
||||
/**
|
||||
* Access the manager which is coordinating the server socket
|
||||
*/
|
||||
public I2PSocketManager getManager();
|
||||
}
|
||||
|
@ -17,159 +17,134 @@ import net.i2p.util.Log;
|
||||
*
|
||||
*/
|
||||
class I2PServerSocketImpl implements I2PServerSocket {
|
||||
|
||||
private final static Log _log = new Log(I2PServerSocketImpl.class);
|
||||
private I2PSocketManager mgr;
|
||||
/** list of sockets waiting for the client to accept them */
|
||||
private List pendingSockets = Collections.synchronizedList(new ArrayList(4));
|
||||
/** have we been closed */
|
||||
private volatile boolean closing = false;
|
||||
/** lock on this when accepting a pending socket, and wait on it for notification of acceptance */
|
||||
private Object socketAcceptedLock = new Object();
|
||||
/** lock on this when adding a new socket to the pending list, and wait on it accordingly */
|
||||
private Object socketAddedLock = new Object();
|
||||
|
||||
/**
|
||||
* Set Sock Option accept timeout stub, does nothing
|
||||
* @param x
|
||||
*/
|
||||
public void setSoTimeout(long x) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Sock Option accept timeout stub, does nothing
|
||||
* @return timeout
|
||||
*/
|
||||
public long getSoTimeout() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public I2PServerSocketImpl(I2PSocketManager mgr) {
|
||||
this.mgr = mgr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for the next socket connecting. If a remote user tried to make a
|
||||
* connection and the local application wasn't .accept()ing new connections,
|
||||
* they should get refused (if .accept() doesnt occur in some small period -
|
||||
* currently 5 seconds)
|
||||
*
|
||||
* @return a connected I2PSocket
|
||||
*
|
||||
* @throws I2PException if there is a problem with reading a new socket
|
||||
* from the data available (aka the I2PSession closed, etc)
|
||||
* @throws ConnectException if the I2PServerSocket is closed
|
||||
*/
|
||||
public I2PSocket accept() throws I2PException, ConnectException {
|
||||
if(_log.shouldLog(Log.DEBUG)) {
|
||||
_log.debug("accept() called, pending: " + pendingSockets.size());
|
||||
}
|
||||
I2PSocket ret = null;
|
||||
|
||||
while((ret == null) && (!closing)) {
|
||||
while(pendingSockets.size() <= 0) {
|
||||
if(closing) {
|
||||
throw new ConnectException("I2PServerSocket closed");
|
||||
}
|
||||
try {
|
||||
synchronized(socketAddedLock) {
|
||||
socketAddedLock.wait();
|
||||
}
|
||||
} catch(InterruptedException ie) {
|
||||
}
|
||||
}
|
||||
synchronized(pendingSockets) {
|
||||
if(pendingSockets.size() > 0) {
|
||||
ret = (I2PSocket)pendingSockets.remove(0);
|
||||
}
|
||||
}
|
||||
if(ret != null) {
|
||||
synchronized(socketAcceptedLock) {
|
||||
socketAcceptedLock.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(_log.shouldLog(Log.DEBUG)) {
|
||||
_log.debug("TIMING: handed out accept result " + ret.hashCode());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the socket available and wait until the client app accepts it, or until
|
||||
* the given timeout elapses. This doesn't have any limits on the queue size -
|
||||
* perhaps it should add some choking (e.g. after 5 waiting for accept, refuse)
|
||||
*
|
||||
* @param timeoutMs how long to wait until accept
|
||||
* @return true if the socket was accepted, false if the timeout expired
|
||||
* or the socket was closed
|
||||
*/
|
||||
public boolean addWaitForAccept(I2PSocket s, long timeoutMs) {
|
||||
if(_log.shouldLog(Log.DEBUG)) {
|
||||
_log.debug("addWaitForAccept [new socket arrived [" + s.toString() + "], pending: " + pendingSockets.size());
|
||||
}
|
||||
if(closing) {
|
||||
if(_log.shouldLog(Log.WARN)) {
|
||||
_log.warn("Already closing the socket");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Clock clock = I2PAppContext.getGlobalContext().clock();
|
||||
long start = clock.now();
|
||||
long end = start + timeoutMs;
|
||||
pendingSockets.add(s);
|
||||
synchronized(socketAddedLock) {
|
||||
socketAddedLock.notifyAll();
|
||||
}
|
||||
|
||||
// keep looping until the socket has been grabbed by the accept()
|
||||
// (or the expiration passes, or the socket is closed)
|
||||
while(pendingSockets.contains(s)) {
|
||||
long now = clock.now();
|
||||
if(now >= end) {
|
||||
if(_log.shouldLog(Log.INFO)) {
|
||||
_log.info("Expired while waiting for accept (time elapsed =" + (now - start) + "ms) for socket " + s.toString());
|
||||
}
|
||||
pendingSockets.remove(s);
|
||||
return false;
|
||||
}
|
||||
if(closing) {
|
||||
if(_log.shouldLog(Log.WARN)) {
|
||||
_log.warn("Server socket closed while waiting for accept");
|
||||
}
|
||||
pendingSockets.remove(s);
|
||||
return false;
|
||||
}
|
||||
long remaining = end - now;
|
||||
try {
|
||||
synchronized(socketAcceptedLock) {
|
||||
socketAcceptedLock.wait(remaining);
|
||||
}
|
||||
} catch(InterruptedException ie) {
|
||||
}
|
||||
}
|
||||
long now = clock.now();
|
||||
if(_log.shouldLog(Log.DEBUG)) {
|
||||
_log.info("Socket accepted after " + (now - start) + "ms for socket " + s.toString());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
closing = true;
|
||||
// let anyone .accept()ing know to fsck off
|
||||
synchronized(socketAddedLock) {
|
||||
socketAddedLock.notifyAll();
|
||||
}
|
||||
// let anyone addWaitForAccept()ing know to fsck off
|
||||
synchronized(socketAcceptedLock) {
|
||||
socketAcceptedLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
public I2PSocketManager getManager() {
|
||||
return mgr;
|
||||
}
|
||||
private final static Log _log = new Log(I2PServerSocketImpl.class);
|
||||
private I2PSocketManager mgr;
|
||||
/** list of sockets waiting for the client to accept them */
|
||||
private List pendingSockets = Collections.synchronizedList(new ArrayList(4));
|
||||
|
||||
/** have we been closed */
|
||||
private volatile boolean closing = false;
|
||||
|
||||
/** lock on this when accepting a pending socket, and wait on it for notification of acceptance */
|
||||
private Object socketAcceptedLock = new Object();
|
||||
/** lock on this when adding a new socket to the pending list, and wait on it accordingly */
|
||||
private Object socketAddedLock = new Object();
|
||||
|
||||
public I2PServerSocketImpl(I2PSocketManager mgr) {
|
||||
this.mgr = mgr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for the next socket connecting. If a remote user tried to make a
|
||||
* connection and the local application wasn't .accept()ing new connections,
|
||||
* they should get refused (if .accept() doesnt occur in some small period -
|
||||
* currently 5 seconds)
|
||||
*
|
||||
* @return a connected I2PSocket
|
||||
*
|
||||
* @throws I2PException if there is a problem with reading a new socket
|
||||
* from the data available (aka the I2PSession closed, etc)
|
||||
* @throws ConnectException if the I2PServerSocket is closed
|
||||
*/
|
||||
public I2PSocket accept() throws I2PException, ConnectException {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("accept() called, pending: " + pendingSockets.size());
|
||||
|
||||
I2PSocket ret = null;
|
||||
|
||||
while ( (ret == null) && (!closing) ){
|
||||
while (pendingSockets.size() <= 0) {
|
||||
if (closing) throw new ConnectException("I2PServerSocket closed");
|
||||
try {
|
||||
synchronized(socketAddedLock) {
|
||||
socketAddedLock.wait();
|
||||
}
|
||||
} catch (InterruptedException ie) {}
|
||||
}
|
||||
synchronized (pendingSockets) {
|
||||
if (pendingSockets.size() > 0) {
|
||||
ret = (I2PSocket)pendingSockets.remove(0);
|
||||
}
|
||||
}
|
||||
if (ret != null) {
|
||||
synchronized (socketAcceptedLock) {
|
||||
socketAcceptedLock.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("TIMING: handed out accept result " + ret.hashCode());
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the socket available and wait until the client app accepts it, or until
|
||||
* the given timeout elapses. This doesn't have any limits on the queue size -
|
||||
* perhaps it should add some choking (e.g. after 5 waiting for accept, refuse)
|
||||
*
|
||||
* @param timeoutMs how long to wait until accept
|
||||
* @return true if the socket was accepted, false if the timeout expired
|
||||
* or the socket was closed
|
||||
*/
|
||||
public boolean addWaitForAccept(I2PSocket s, long timeoutMs) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("addWaitForAccept [new socket arrived [" + s.toString() + "], pending: " + pendingSockets.size());
|
||||
|
||||
if (closing) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Already closing the socket");
|
||||
return false;
|
||||
}
|
||||
|
||||
Clock clock = I2PAppContext.getGlobalContext().clock();
|
||||
long start = clock.now();
|
||||
long end = start + timeoutMs;
|
||||
pendingSockets.add(s);
|
||||
synchronized (socketAddedLock) {
|
||||
socketAddedLock.notifyAll();
|
||||
}
|
||||
|
||||
// keep looping until the socket has been grabbed by the accept()
|
||||
// (or the expiration passes, or the socket is closed)
|
||||
while (pendingSockets.contains(s)) {
|
||||
long now = clock.now();
|
||||
if (now >= end) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Expired while waiting for accept (time elapsed =" + (now - start) + "ms) for socket " + s.toString());
|
||||
pendingSockets.remove(s);
|
||||
return false;
|
||||
}
|
||||
if (closing) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Server socket closed while waiting for accept");
|
||||
pendingSockets.remove(s);
|
||||
return false;
|
||||
}
|
||||
long remaining = end - now;
|
||||
try {
|
||||
synchronized (socketAcceptedLock) {
|
||||
socketAcceptedLock.wait(remaining);
|
||||
}
|
||||
} catch (InterruptedException ie) {}
|
||||
}
|
||||
long now = clock.now();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.info("Socket accepted after " + (now-start) + "ms for socket " + s.toString());
|
||||
return true;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
closing = true;
|
||||
// let anyone .accept()ing know to fsck off
|
||||
synchronized (socketAddedLock) {
|
||||
socketAddedLock.notifyAll();
|
||||
}
|
||||
// let anyone addWaitForAccept()ing know to fsck off
|
||||
synchronized (socketAcceptedLock) {
|
||||
socketAcceptedLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
public I2PSocketManager getManager() { return mgr; }
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.ConnectException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
@ -21,203 +20,173 @@ import net.i2p.util.Log;
|
||||
*
|
||||
*/
|
||||
public class StreamSinkServer {
|
||||
|
||||
private Log _log;
|
||||
private String _sinkDir;
|
||||
private String _destFile;
|
||||
private String _i2cpHost;
|
||||
private int _i2cpPort;
|
||||
private int _handlers;
|
||||
|
||||
/**
|
||||
* Create but do not start the streaming server.
|
||||
*
|
||||
* @param sinkDir Directory to store received files in
|
||||
* @param ourDestFile filename to write our binary destination to
|
||||
*/
|
||||
public StreamSinkServer(String sinkDir, String ourDestFile) {
|
||||
this(sinkDir, ourDestFile, null, -1, 3);
|
||||
}
|
||||
|
||||
public StreamSinkServer(String sinkDir, String ourDestFile, String i2cpHost, int i2cpPort, int handlers) {
|
||||
_sinkDir = sinkDir;
|
||||
_destFile = ourDestFile;
|
||||
_i2cpHost = i2cpHost;
|
||||
_i2cpPort = i2cpPort;
|
||||
_handlers = handlers;
|
||||
_log = I2PAppContext.getGlobalContext().logManager().getLog(StreamSinkServer.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually fire up the server - this call blocks forever (or until the server
|
||||
* socket closes)
|
||||
*
|
||||
*/
|
||||
public void runServer() {
|
||||
I2PSocketManager mgr = null;
|
||||
if(_i2cpHost != null) {
|
||||
mgr = I2PSocketManagerFactory.createManager(_i2cpHost, _i2cpPort, new Properties());
|
||||
} else {
|
||||
mgr = I2PSocketManagerFactory.createManager();
|
||||
}
|
||||
Destination dest = mgr.getSession().getMyDestination();
|
||||
if(_log.shouldLog(Log.INFO)) {
|
||||
_log.info("Listening for connections on: " + dest.calculateHash().toBase64());
|
||||
}
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
fos = new FileOutputStream(_destFile);
|
||||
dest.writeBytes(fos);
|
||||
} catch(IOException ioe) {
|
||||
_log.error("Error writing out our destination to " + _destFile, ioe);
|
||||
return;
|
||||
} catch(DataFormatException dfe) {
|
||||
_log.error("Error formatting the destination", dfe);
|
||||
return;
|
||||
} finally {
|
||||
if(fos != null) {
|
||||
try {
|
||||
fos.close();
|
||||
} catch(IOException ioe) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
I2PServerSocket sock = mgr.getServerSocket();
|
||||
startup(sock);
|
||||
}
|
||||
|
||||
public void startup(I2PServerSocket sock) {
|
||||
for(int i = 0; i < _handlers; i++) {
|
||||
I2PThread t = new I2PThread(new ClientRunner(sock));
|
||||
t.setName("Handler " + i);
|
||||
t.setDaemon(false);
|
||||
t.start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually deal with a client - pull anything they send us and write it to a file.
|
||||
*
|
||||
*/
|
||||
private class ClientRunner implements Runnable {
|
||||
|
||||
private I2PServerSocket _socket;
|
||||
|
||||
public ClientRunner(I2PServerSocket socket) {
|
||||
_socket = socket;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
while(true) {
|
||||
try {
|
||||
I2PSocket socket = _socket.accept();
|
||||
if(socket != null) {
|
||||
handle(socket);
|
||||
}
|
||||
} catch(I2PException ie) {
|
||||
_log.error("Error accepting connection", ie);
|
||||
return;
|
||||
} catch(ConnectException ce) {
|
||||
_log.error("Connection already dropped", ce);
|
||||
return;
|
||||
} catch(SocketTimeoutException ste) {
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handle(I2PSocket sock) {
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
File sink = new File(_sinkDir);
|
||||
if(!sink.exists()) {
|
||||
sink.mkdirs();
|
||||
}
|
||||
File cur = File.createTempFile("clientSink", ".dat", sink);
|
||||
fos = new FileOutputStream(cur);
|
||||
if(_log.shouldLog(Log.DEBUG)) {
|
||||
_log.debug("Writing to " + cur.getAbsolutePath());
|
||||
}
|
||||
} catch(IOException ioe) {
|
||||
_log.error("Error creating sink", ioe);
|
||||
return;
|
||||
}
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
try {
|
||||
InputStream in = sock.getInputStream();
|
||||
byte buf[] = new byte[4096];
|
||||
long written = 0;
|
||||
int read = 0;
|
||||
while((read = in.read(buf)) != -1) {
|
||||
//_fos.write(buf, 0, read);
|
||||
written += read;
|
||||
if(_log.shouldLog(Log.DEBUG)) {
|
||||
_log.debug("read and wrote " + read + " (" + written + ")");
|
||||
}
|
||||
}
|
||||
fos.write(("written: [" + written + "]\n").getBytes());
|
||||
long lifetime = System.currentTimeMillis() - start;
|
||||
_log.info("Got EOF from client socket [written=" + written + " lifetime=" + lifetime + "]");
|
||||
} catch(IOException ioe) {
|
||||
_log.error("Error writing the sink", ioe);
|
||||
} finally {
|
||||
if(fos != null) {
|
||||
try {
|
||||
fos.close();
|
||||
} catch(IOException ioe) {
|
||||
}
|
||||
}
|
||||
if(sock != null) {
|
||||
try {
|
||||
sock.close();
|
||||
} catch(IOException ioe) {
|
||||
}
|
||||
}
|
||||
_log.debug("Client socket closed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire up the streaming server. <code>Usage: StreamSinkServer [i2cpHost i2cpPort] sinkDir ourDestFile [numHandlers]</code><br />
|
||||
* <ul>
|
||||
* <li><b>sinkDir</b>: Directory to store received files in</li>
|
||||
* <li><b>ourDestFile</b>: filename to write our binary destination to</li>
|
||||
* <li><b>numHandlers</b>: how many concurrent connections to handle</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static void main(String args[]) {
|
||||
StreamSinkServer server = null;
|
||||
switch(args.length) {
|
||||
case 0:
|
||||
server = new StreamSinkServer("dataDir", "server.key", "localhost", 7654, 3);
|
||||
break;
|
||||
case 2:
|
||||
server = new StreamSinkServer(args[0], args[1]);
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
int handlers = 3;
|
||||
if(args.length == 5) {
|
||||
try {
|
||||
handlers = Integer.parseInt(args[4]);
|
||||
} catch(NumberFormatException nfe) {
|
||||
}
|
||||
}
|
||||
try {
|
||||
int port = Integer.parseInt(args[1]);
|
||||
server = new StreamSinkServer(args[2], args[3], args[0], port, handlers);
|
||||
} catch(NumberFormatException nfe) {
|
||||
System.out.println("Usage: StreamSinkServer [i2cpHost i2cpPort] sinkDir ourDestFile [handlers]");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
System.out.println("Usage: StreamSinkServer [i2cpHost i2cpPort] sinkDir ourDestFile [handlers]");
|
||||
}
|
||||
if(server != null) {
|
||||
server.runServer();
|
||||
}
|
||||
}
|
||||
private Log _log;
|
||||
private String _sinkDir;
|
||||
private String _destFile;
|
||||
private String _i2cpHost;
|
||||
private int _i2cpPort;
|
||||
private int _handlers;
|
||||
|
||||
/**
|
||||
* Create but do not start the streaming server.
|
||||
*
|
||||
* @param sinkDir Directory to store received files in
|
||||
* @param ourDestFile filename to write our binary destination to
|
||||
*/
|
||||
public StreamSinkServer(String sinkDir, String ourDestFile) {
|
||||
this(sinkDir, ourDestFile, null, -1, 3);
|
||||
}
|
||||
public StreamSinkServer(String sinkDir, String ourDestFile, String i2cpHost, int i2cpPort, int handlers) {
|
||||
_sinkDir = sinkDir;
|
||||
_destFile = ourDestFile;
|
||||
_i2cpHost = i2cpHost;
|
||||
_i2cpPort = i2cpPort;
|
||||
_handlers = handlers;
|
||||
_log = I2PAppContext.getGlobalContext().logManager().getLog(StreamSinkServer.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually fire up the server - this call blocks forever (or until the server
|
||||
* socket closes)
|
||||
*
|
||||
*/
|
||||
public void runServer() {
|
||||
I2PSocketManager mgr = null;
|
||||
if (_i2cpHost != null)
|
||||
mgr = I2PSocketManagerFactory.createManager(_i2cpHost, _i2cpPort, new Properties());
|
||||
else
|
||||
mgr = I2PSocketManagerFactory.createManager();
|
||||
Destination dest = mgr.getSession().getMyDestination();
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Listening for connections on: " + dest.calculateHash().toBase64());
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
fos = new FileOutputStream(_destFile);
|
||||
dest.writeBytes(fos);
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error writing out our destination to " + _destFile, ioe);
|
||||
return;
|
||||
} catch (DataFormatException dfe) {
|
||||
_log.error("Error formatting the destination", dfe);
|
||||
return;
|
||||
} finally {
|
||||
if (fos != null) try { fos.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
|
||||
I2PServerSocket sock = mgr.getServerSocket();
|
||||
startup(sock);
|
||||
}
|
||||
|
||||
public void startup(I2PServerSocket sock) {
|
||||
for (int i = 0; i < _handlers; i++) {
|
||||
I2PThread t = new I2PThread(new ClientRunner(sock));
|
||||
t.setName("Handler " + i);
|
||||
t.setDaemon(false);
|
||||
t.start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually deal with a client - pull anything they send us and write it to a file.
|
||||
*
|
||||
*/
|
||||
private class ClientRunner implements Runnable {
|
||||
private I2PServerSocket _socket;
|
||||
public ClientRunner(I2PServerSocket socket) {
|
||||
_socket = socket;
|
||||
}
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
I2PSocket socket = _socket.accept();
|
||||
if (socket != null)
|
||||
handle(socket);
|
||||
} catch (I2PException ie) {
|
||||
_log.error("Error accepting connection", ie);
|
||||
return;
|
||||
} catch (ConnectException ce) {
|
||||
_log.error("Connection already dropped", ce);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handle(I2PSocket sock) {
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
File sink = new File(_sinkDir);
|
||||
if (!sink.exists())
|
||||
sink.mkdirs();
|
||||
File cur = File.createTempFile("clientSink", ".dat", sink);
|
||||
fos = new FileOutputStream(cur);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Writing to " + cur.getAbsolutePath());
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error creating sink", ioe);
|
||||
return;
|
||||
}
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
try {
|
||||
InputStream in = sock.getInputStream();
|
||||
byte buf[] = new byte[4096];
|
||||
long written = 0;
|
||||
int read = 0;
|
||||
while ( (read = in.read(buf)) != -1) {
|
||||
//_fos.write(buf, 0, read);
|
||||
written += read;
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("read and wrote " + read + " (" + written + ")");
|
||||
}
|
||||
fos.write(("written: [" + written + "]\n").getBytes());
|
||||
long lifetime = System.currentTimeMillis() - start;
|
||||
_log.info("Got EOF from client socket [written=" + written + " lifetime=" + lifetime + "]");
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error writing the sink", ioe);
|
||||
} finally {
|
||||
if (fos != null) try { fos.close(); } catch (IOException ioe) {}
|
||||
if (sock != null) try { sock.close(); } catch (IOException ioe) {}
|
||||
_log.debug("Client socket closed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire up the streaming server. <code>Usage: StreamSinkServer [i2cpHost i2cpPort] sinkDir ourDestFile [numHandlers]</code><br />
|
||||
* <ul>
|
||||
* <li><b>sinkDir</b>: Directory to store received files in</li>
|
||||
* <li><b>ourDestFile</b>: filename to write our binary destination to</li>
|
||||
* <li><b>numHandlers</b>: how many concurrent connections to handle</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static void main(String args[]) {
|
||||
StreamSinkServer server = null;
|
||||
switch (args.length) {
|
||||
case 0:
|
||||
server = new StreamSinkServer("dataDir", "server.key", "localhost", 7654, 3);
|
||||
break;
|
||||
case 2:
|
||||
server = new StreamSinkServer(args[0], args[1]);
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
int handlers = 3;
|
||||
if (args.length == 5) {
|
||||
try {
|
||||
handlers = Integer.parseInt(args[4]);
|
||||
} catch (NumberFormatException nfe) {}
|
||||
}
|
||||
try {
|
||||
int port = Integer.parseInt(args[1]);
|
||||
server = new StreamSinkServer(args[2], args[3], args[0], port, handlers);
|
||||
} catch (NumberFormatException nfe) {
|
||||
System.out.println("Usage: StreamSinkServer [i2cpHost i2cpPort] sinkDir ourDestFile [handlers]");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
System.out.println("Usage: StreamSinkServer [i2cpHost i2cpPort] sinkDir ourDestFile [handlers]");
|
||||
}
|
||||
if (server != null)
|
||||
server.runServer();
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user