sjamesr/jfreesane

Socket closed while setting SaneOption value

Closed this issue · 13 comments

Hi everyone, sorry to bother.

While setting option values (any type) I get a socket closed exception in SaneOption.writeWordListOption(List value)

java.net.SocketException: Socket closed
at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:118)
at java.net.SocketOutputStream.write(SocketOutputStream.java:155)
at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82)
at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140)
at au.com.southsky.jfreesane.SaneOption.writeWordListOption(SaneOption.java:624)
at au.com.southsky.jfreesane.SaneOption.writeOption(SaneOption.java:657)
at au.com.southsky.jfreesane.SaneOption.setFixedValue(SaneOption.java:492)

SaneOutputStream out = device.getSession().getOutputStream();

returns an open stream however later on the flush fails.
Any idea that can help me?
thanks in advance

private ControlOptionResult writeWordListOption(List value)
      throws IOException, SaneException {
    Preconditions.checkState(isWriteable(), "option is not writeable");
    Preconditions.checkState(isActive(), "option is not active");

    SaneOutputStream out = device.getSession().getOutputStream();
    out.write(SaneRpcCode.SANE_NET_CONTROL_OPTION);
    out.write(device.getHandle().getHandle());
    out.write(SaneWord.forInt(optionNumber));
    out.write(SaneWord.forInt(OptionAction.SET_VALUE.getWireValue()));
    out.write(getValueType());

    out.write(SaneWord.forInt(value.size() * SaneWord.SIZE_IN_BYTES));

    // Write the pointer to the words
    out.write(SaneWord.forInt(value.size()));

    for (SaneWord element : value) {
      // and the words themselves
      out.write(element);
    }

    out.flush();

    ControlOptionResult result = handleWriteResponse();
    if (result.getInfo().contains(OptionWriteInfo.RELOAD_OPTIONS)
        || result.getInfo().contains(OptionWriteInfo.RELOAD_PARAMETERS)) {
      device.invalidateOptions();
      device.listOptions();
    }

    return result;
  }

Are you able to share the code that is calling into jfreesane?

hmm, what bit would be relevant?

The part that calls "setFixedValue", for example.

I suspect your SANE daemon is somehow misconfigured and is closing the socket shortly after it's opened. You could confirm with server logs.

Ok, strangely enough if I catch those exceptions and continue I am able to get images back (I am using the test configuration).

In fact if at this point

SaneOutputStream out = device.getSession().getOutputStream();

I inspect device.isOpen(), that returns true

Can you share the code you're using to call into JFreeSane? Where are you catching exceptions? Which socket is closed? If the socket to the SANE server is closed, there's no way you should be able to retrieve images.

Help me understand how you're using SANE/jfreesane.

Well, there is not much to show. I am going by the manual

SaneDevice device = ...;
device.open();
List options = device.listOptions();
then I set a value to a given option (using the setter according to the type) (this step fails with socket closed exception)
then I do device.acquireImage()
And I get a color test image as set in the configuration file.

I am using Ubuntu16.04 64 on VMWareFusion, and a saned daemon with a virtual device configured.

Tomorrow I'll put my hands on a physical device and I'll see. Up to now I don't understand. I'll get back to you, thanks

Oh, let me see if I can find that one. thanks

Ok. I am working with a Kodak ScanMate i940 now. I see the same problem this time while acquiring images.

My client code:

@Override
public BufferedImage doScan() throws xxxSaneException {

if (!device.isOpen())
     openDevice();

Logger logger = getLogger();
try {
     logger.info(Messages.getString("akey", getName())); //$NON-NLS-1$
     return device.acquireImage();
} catch (Exception e) {
     Device.getExecutor().submit(()->
    Try.run(()->((SaneDevice)device).close()));

    logger.log(Level.SEVERE, "doScan() " + e.getClass().getName() + ": "+ e.getMessage());
    throw new xxxJSaneException(e);
} finally {
    logger.info(Messages.getString("anotherKey", getName())); //$NON-NLS-1$
    }
}

So here, preconditions are met

public BufferedImage acquireImage(ScanListener listener) throws IOException, SaneException {
    Preconditions.checkState(isOpen(), "device is not open");
    if (listener == null) {
      listener = new ScanListenerAdapter();
    }
    return session.acquireImage(this, listener);
  }

error is thrown in line marked as bold, with a socketClosedException

BufferedImage acquireImage(SaneDevice device, ScanListener listener)
      throws IOException, SaneException {
    SaneImage.Builder builder = new SaneImage.Builder();
    SaneParameters parameters = null;
    listener.scanningStarted(device);
    int currentFrame = 0;

    do {
      SaneDeviceHandle handle = device.getHandle();
      outputStream.write(SaneRpcCode.SANE_NET_START);
      outputStream.write(handle.getHandle());
      outputStream.flush();

      SaneWord startStatus = inputStream.readWord();

      int port = inputStream.readWord().integerValue();
      SaneWord byteOrder = inputStream.readWord();
      String resource = inputStream.readString();
...

Server log reads

07:15:04 osboxes systemd[1]: Started Scanner Service (127.0.0.1:55110).
Jan  9 07:15:04 osboxes saned[35837]: saned (AF-indep+IPv6+systemd) from sane-backends 1.0.25git starting up
Jan  9 07:15:04 osboxes saned[35837]: check_host: access by remote host: ::ffff:127.0.0.1
Jan  9 07:15:04 osboxes saned[35837]: init: access granted to osboxes@::ffff:127.0.0.1
Jan  9 07:15:15 osboxes gnome-session[2384]: Memory pressure relief: Total: res = 7553024/7561216/8192, res+swap = 5726208/5730304/4096
Jan  9 07:15:28 osboxes saned[35837]: saned exiting

thanks!

It certainly doesn't look like saned is closing the socket. The only reason that should happen on the client side is because of a call to SaneSession.close.

Do you keep the SaneSession around for the duration of your scanning?

Oh! I am very sorry you are right.

disclaimer: I am migrating an existing code to use jfreesane (a code that I am not very familiar with)

thanks a lot for your time

No problem, glad we got it sorted.