A TCP connection between the API client application and TWS needs to first be established via the IBApi.EClientSocket.eConnect function.
【译】 客户端与TWS采用的是TCP形式进行连接。
TWS acts as a server to receive requests from the API application (the client) and responds by taking appropriate actions. The first step is for the API client to initiate a connection to the socket port on which TWS is listening.
It is possible to have multiple TWS instances running on the same computer if each is configured with a different API socket port number. Also, each TWS session can receive up to 32 different client applications simultaneously. The client ID field specified in the API connection is used to distinguish different API clients.
eConnect starts by requesting from the OS that a TCP socket be opened to the specified IP address and host number- the first two parameters in the function call.
【译】 eConnect通过指定的目标地址和端口号建立与服务器的连接,前两个参数就是地址和端口号。
If the socket cannot be opened, the OS returns an error condition which the API returns as error code 502 to IBApi.EWrapper.error.
Since this error is not generated by TWS it is not captured in TWS log files.
【译】 如果TWS无法生成错误,那么在TWS的日志文件中也无法找到。
Most commonly error 502 will indicate that TWS is not running with the API enabled or is listening for connection requests on a different socket port.
【译】 多数情况下,502错误指示TWS没有启用API连接或者监听了错误的端口号。
If connecting across a network, it can also occur if there is a firewall or antivirus program blocking connections.
【译】 如果通过网络建立连接,它也有可能是由防火墙或者杀毒软件屏蔽了信号引起的。
After the socket has been opened, both applications need to exchange essential information for the API session.
【译】 当Socket打开了连接通道,主客机都需要通过信道交换重要的消息。
First, the version numbers of the client (API program) and server (TWS) are exchanged so that the server and client each know the function calls supported by one another. In this way backwards compatibility can be maintained between TWS and previous API versions.
Next TWS will always return certain information for the client session, namely the accounts which are accessible by the TWS session, the next valid order identifier (ID), and the time of connection. In the most common mode of operation the EClient.AsyncEConnect field is set to false and the initial handshake is taken to completion immediately after the socket connection is established. TWS will then immediately provides the API client with this information.
Important: The IBApi.EWrapper.nextValidID callback is commonly used to indicate that the connection is completed and other messages can be sent from the API client to TWS. There is the possibility that function calls made prior to this time could be dropped by TWS.
There is also an alternative mode of connection used in special cases in wich the variable AsyncEconnect is set to true, and the call to startAPI is only called from the connectAck() function. All IB samples use the mode AsyncEconnect = False.
The EReader Thread
API programs always have at least two threads of execution. One thread is used for sending messages to TWS, and another thread is used for reading returned messages. The second thread uses the API EReader class to read from the socket and add messages to a queue.
Everytime a new message is added to the message queue, a notification flag is triggered to let other threads now that there is a message waiting to be processed. In the two-thread design of an API program, the message queue is also processed by the first thread. In a three-thread design, an additional thread is created to perform this task.
The thread responsible for the message queue will decode messages and invoke the appropriate functions in EWrapper. The two-threaded design is used in the IB Python sample Program.py and the C++ sample TestCppClient, while the 'Testbed' samples in the other languages use a three-threaded design. Commonly in a Python asynchronous network application, the asyncio module will be used to create a more sequential looking code design.
final EReader reader = new EReader(m_client, m_signal);
reader.start();
//An additional thread is created in this program design to empty the messaging queue
new Thread(() -> {
while (m_client.isConnected()) {
m_signal.waitForSignal();
try {
reader.processMsgs();
} catch (Exception e) {
System.out.println("Exception: "+e.getMessage());
}
}
}).start();
复制代码
Now it is time to revisit the role of IBApi.EReaderSignal initially introduced in The EClientSocket Class. As mentioned in the previous paragraph, after the EReader thread places a message in the queue, a notification is issued to make known that a message is ready for processing. In the (C++, C#/.NET, Java) APIs, this is done via the IBApi.EReaderSignal object we initiated within the IBApi.EWrapper's implementer. In the Python API, it is handled automatically by the Queue class.
The client application is now ready to work with the Trader Workstation! At the completion of the connection, the API program will start receiving events such as IBApi.EWrapper.nextValidId and IBApi.EWrapper.managedAccounts. In TWS (not IB Gateway)if there is an active network connection, there will also immediately be callbacks to IBApi::EWrapper::error with errorId as -1 and errorCode=2104,2106, errorMsg = "Market Data Server is ok" to indicate there is an active connection to the IB market data server.
【译】 至此,已经可以与Trader Workstation通信并工作了!当完成了连接,API程序将开始接收消息,如IBApi.EWrapper.nextValidId,IBApi.EWrapper.managedAccounts。在TWS(而不是IB网关),如果有活跃的网络连接,也会被立即的通过回掉传值给IBApi::EWrapper::error,errorId为-1,errorCode=2104,2106,errorMsg = "Market Data Server is ok" 用于指示有活跃的连接到IB市场数据服务器。
Callbacks to IBApi::EWrapper::error with errorId as -1 do not represent true 'errors' but only notifications that a connection has been made successfully to the IB market data farms.
IB Gateway by contrast will not make connections to market data farms until a request is made by the IB client. Until this time the connection indicator in the IB Gateway GUI will show a yellow color of 'inactive' rather than an 'active' green indication.
When initially making requests from an API application it is important that the verifies that a response is received rather than proceeding assuming that the network connection is ok and the subscription request (portfolio updates, account information, etc) was made successfully.
Accepting an API connection from TWS
【译】 接收来自TWS的消息
For security reasons, by default the API is not configured to automatically accept connection requests from API applications. After a connection attempt, a dialogue will appear in TWS asking the user to manually confirm that a connection can be made:
To prevent the TWS from asking the end user to accept the connection, it is possible to configure it to automatically accept the connection from a trusted IP address and/or the local machine. This can easily be done via the TWS API settings:
Note: you have to make sure the connection has been fully established before attempting to do any requests to the TWS. Failure to do so will result in the TWS closing the connection. Typically this can be done by waiting for a callback from an event and the end of the initial connection handshake, such as IBApi.EWrapper.nextValidId or IBApi.EWrapper.managedAccounts.
In rare cases in which IB Gateway or TWS has a momentarily delay in establishing connecting to the IB servers, messages sent immediately after receiving the nextValidId could be dropped and would need to be resent. If the API client has not receive the expected callbacks from issued requests, it should not proceed assumming the connection is ok.
If there is a problem with the socket connection between TWS and the API client, for instance if TWS suddenly closes, this will trigger an exception in the EReader thread which is reading from the socket. This exception will also occur if an API client attempts to connect with a client ID that is already in use.
The socket EOF is handled slightly differently in different API languages. For instance in Java, it is caught and sent to the client application to IBApi::EWrapper::error with errorCode 507: "Bad Message". In C# it is caught and sent to IBApi::EWrapper::error with errorCode -1. The client application needs to handle this error message and use it to indicate that an exception has been thrown in the socket connection. Associated functions such as IBApi::EWrapper::connectionClosed and IBApi::EClient::IsConnected functions are not called automatically by the API code but need to be handled at the API client-level.