Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
969 views
in Technique[技术] by (71.8m points)

sockets - IB TWS API has a SocketException when calling eDisconnect() on the EClientSocket which occurs at java.io.DataInputStream.readInt(EClientSocket)

To use the Interactive Brokers (IB) TWS API, I need to write a class that implements EWrapper. I am having trouble properly disconnecting my socket in that class, even when using the built-in eDisconnect() method provided by IB.

Whenever I write a class that implements EWrapper, calling eDisconnect() on the EClientSocket instance throws an Exception (which is passed to error(Exception e)...more details below). I would like to be able to disconnect properly, so that I can connect and disconnect serially within the same program.

Consider the following example:

public class ConnectionTest {
    public static void main(String[] args) {
        // Create Contract
        Contract contract = new Contract();
        contract.symbol("IBM");
        contract.secType("STK");
        contract.exchange("NYSE");

        TWSCounter twsCounter = new TWSCounter(); // Class I wrote that simply keeps track of ID request numbers
        APIClient apiClient = new APIClient(twsCounter);

        apiClient.connect();
        apiClient.reqContractDetails(contract);
        //apiClient.disconnect(); // commented out for first example
    }
}
public class APIClient implements EWrapper {
    private TWSCounter twsCounter;
    private EClientSocket clientSocket = null;
    private EJavaSignal signal = new EJavaSignal();

    private EReader reader;
    
    // Constructor
    public APIClient(TWSCounter twsCounter) {
        this.twsCounter = twsCounter;
    }
    
    public void connect() {
        // Create a new EClientSocket object
        clientSocket = new EClientSocket (this, signal);
        
        // Connect to the TWS
        clientSocket.eConnect("127.0.0.1", 7497, 0);
        
        // Pause here for connection to complete
        try {
            while (! (clientSocket.isConnected()));
        } 
        catch (Exception e) {
            e.printStackTrace();
            System.err.println("Error trying to connect to TWS API");
            System.exit(1);
        }

        reader = new EReader(clientSocket, signal);
        reader.start();
        
        // Based on IB sample code in Test.java
        new Thread(() -> {
            while (clientSocket.isConnected()) {
                signal.waitForSignal();

                try {
                    reader.processMsgs();
                } catch(Exception e) {
                    error(e);
                    System.err.println("Error while trying reader.processMsgs()");
                }
                
            }
        }).start();

    }
    
    public void disconnect() {
        clientSocket.eDisconnect();
    }

    public void reqContractDetails(Contract contract) {
        clientSocket.reqContractDetails(twsCounter.getNextNumber(), contract);
    }

    @Override
    public void contractDetails(int reqId, ContractDetails contractDetails) {
        System.out.println("conID: " + contractDetails.conid());
    }

    // More methods...

When I run ConnectionTest, I correctly get the output, conID: 8314. The program continues to run indefinitely, however, until I terminate it manually in eclipse by pushing the 'stop' button.

If I "uncomment" apiClient.disconnect() at the end of ConnectionTest, then the program disconnects, however an exception is thrown.

How one chooses to override the error methods in the APIClient affects what happens. If they are not overridden, the process terminates silently, despite the Exception.

If, however, public void error(Exception e) is overridden as follows, we get the following stack trace:

public void error(Exception e) {
    e.printStackTrace();
}
java.net.SocketException: Socket closed
    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.socketRead(Unknown Source)
    at java.net.SocketInputStream.read(Unknown Source)
    at java.net.SocketInputStream.read(Unknown Source)
    at java.net.SocketInputStream.read(Unknown Source)
    at java.io.DataInputStream.readInt(Unknown Source)
    at com.ib.client.EClientSocket.readInt(EClientSocket.java:233)
    at com.ib.client.EReader.readSingleMessage(EReader.java:119)
    at com.ib.client.EReader.putMessageToQueue(EReader.java:79)
    at com.ib.client.EReader.run(EReader.java:57)

I believe issue is that the EReader (EReader extends Thread) calls putMessageToQueue() even after the socket is disconnected. From EReader:

    @Override
    public void run() {
        try {
            // loop until thread is terminated
            while (!isInterrupted()) {
                if (!putMessageToQueue())
                    break;
            }
        }
        catch ( Exception ex ) {
            //if (parent().isConnected()) {
                if( ex instanceof EOFException ) {
                    eWrapper().error(EClientErrors.NO_VALID_ID, EClientErrors.BAD_LENGTH.code(),
                            EClientErrors.BAD_LENGTH.msg() + " " + ex.getMessage());
                }
                else {
                    eWrapper().error( ex);
                }
                
                parent().eDisconnect();
            //}
        } 
        
        m_signal.issueSignal();
    }

Modifying the APIClient.disconnect() method to interrupt the EReader, doesn't seem to help however:

    public void disconnect() {
        reader.interrupt();
        clientSocket.eDisconnect();
    }

For the most part, I've been able to get by without solving this issue properly. What I would really like to be able to do, however, is connect and disconnect serially, as in this simplified example:

public class ConnectionTest {

    public static void main(String[] args) {
        // Create Contracts
        Contract contract = new Contract();
        contract.symbol("IBM");
        contract.secType("STK");
        contract.exchange("NYSE");

        Contract contract2 = new Contract();
        contract2.symbol("KO");
        contract2.secType("STK");
        contract2.exchange("NYSE");


        TWSCounter twsCounter = new TWSCounter();
        APIClient apiClient = new APIClient(twsCounter);

        apiClient.connect();
        apiClient.reqContractDetails(contract);

        try {
            Thread.sleep(1000);
        } catch (Exception e){
            System.err.println("Couldn't sleep");
            System.exit(1);
        }
        
        apiClient.disconnect();

        try {
            Thread.sleep(1000);
        } catch (Exception e){
            System.err.println("Couldn't sleep");
        }

        apiClient.connect();
        apiClient.reqContractDetails(contract2);

        try {
            Thread.sleep(1000);
        } catch (Exception e){
            System.err.println("Couldn't sleep");
        }

        apiClient.disconnect();
    }
}

Running this, as is, will sometimes correctly give conID: 8314 and conID: 8894 along with two stack traces the same as above, but will also sometimes give conID: 8314 and Connection Message: -1, 507, Bad Message Length null. Error message 507 is:

Bad Message Length (Java-only) Indicates EOF exception was caught while reading from the socket. This can occur if there is an attempt >to connect to TWS with a client ID that is already in use, or if TWS is locked, closes, or breaks the >connection. It should be handled by the client application and used to indicate that the socket connection is not valid. IB Message Codes

For what it's worth, I've been completely frustrated by this problem off and on for two years. (I'm not a professional programmer.) I've searched for solutions online and even paid a programmer to help me, all to no avail (although I have learned much more java in the process). I've also downloaded the latest version of the IB API (v 9.76). The same problem occurs with multiple versions of the IB API.

question from:https://stackoverflow.com/questions/65896041/ib-tws-api-has-a-socketexception-when-calling-edisconnect-on-the-eclientsocket

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)
Waitting for answers

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...