| // Licensed to the Apache Software Foundation (ASF) under one or more |
| // contributor license agreements. See the NOTICE file distributed with |
| // this work for additional information regarding copyright ownership. |
| // The ASF licenses this file to You 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. |
| = Binary Client Protocol |
| |
| == Overview |
| |
| Ignite binary client protocol enables user applications to communicate with an existing Ignite cluster without starting a full-fledged Ignite node. An application can connect to the cluster through a raw TCP socket. Once the connection is established, the application can communicate with the Ignite cluster and perform cache operations using the established format. |
| |
| To communicate with the Ignite cluster, a client must obey the data format and communication details explained below. |
| |
| == Data Format |
| |
| === Byte Ordering |
| |
| Ignite binary client protocol has little-endian byte ordering. |
| |
| === Data Objects |
| |
| User data, such as cache keys and values, are represented in the Ignite link:key-value-api/binary-objects[Binary Object] format. A data object can be a standard (predefined) type or a complex object. For the complete list of data types supported, see the link:binary-client-protocol/data-format[Data Format] section. |
| |
| == Message Format |
| |
| All messages- requests and responses, including handshake, start with an `int` type message length (excluding these first 4 bytes) followed by the payload (message body). |
| |
| === Handshake |
| |
| The binary client protocol requires a connection handshake to ensure that client and server versions are compatible. The following tables show the structure of handshake message request and response. Refer to the <<Example>> section on how to send and receive a handshake request and response respectively. |
| |
| |
| [cols="1,2",opts="header"] |
| |=== |
| |Request Type| Description |
| |int| Length of handshake payload |
| |byte| Handshake code, always 1. |
| |short| Version major. |
| |short| Version minor. |
| |short| Version patch. |
| |byte| Client code, always 2. |
| |String| Username |
| |String| Password |
| |=== |
| |
| |
| [cols="1,2",opts="header"] |
| |=== |
| | Response Type (success) | Description |
| |int| Success message length, 1. |
| |byte| Success flag, 1. |
| |=== |
| |
| |
| [cols="1,2",opts="header"] |
| |=== |
| |Response Type (failure) | Description |
| |int| Error message length. |
| |byte| Success flag, 0. |
| |short| Server version major. |
| |short| Server version minor. |
| |short| Server version patch. |
| |String| Error message. |
| |=== |
| |
| |
| === Standard Message Header |
| |
| Client operation messages are composed of a header and operation-specific data. Each operation has its own <<Client Operations,data request and response format>>, with a common header. |
| |
| The following tables and examples show the request and response structure of a client operation message header: |
| |
| |
| [cols="1,2",opts="header"] |
| |=== |
| |Request Type | Description |
| |int| Length of payload. |
| |short| Operation code |
| |long| Request id, generated by client and returned as-is in response |
| |=== |
| |
| |
| .Request header |
| [source, java] |
| ---- |
| private static void writeRequestHeader(int reqLength, short opCode, long reqId, DataOutputStream out) throws IOException { |
| // Message length |
| writeIntLittleEndian(10 + reqLength, out); |
| |
| // Op code |
| writeShortLittleEndian(opCode, out); |
| |
| // Request id |
| writeLongLittleEndian(reqId, out); |
| } |
| ---- |
| |
| |
| [cols="1,2",opts="header"] |
| |=== |
| |Response Type | Description |
| |int| Length of response message. |
| |long| Request id (see above) |
| |int| Status code (0 for success, otherwise error code) |
| |String| Error message (present only when status is not 0) |
| |=== |
| |
| |
| |
| .Response header |
| [source, java] |
| ---- |
| private static void readResponseHeader(DataInputStream in) throws IOException { |
| // Response length |
| final int len = readIntLittleEndian(in); |
| |
| // Request id |
| long resReqId = readLongLittleEndian(in); |
| |
| // Success code |
| int statusCode = readIntLittleEndian(in); |
| } |
| ---- |
| |
| |
| == Connectivity |
| |
| === TCP Socket |
| |
| Client applications should connect to server nodes with a TCP socket. By default, the connector is enabled on port 10800. You can configure the port number and other server-side connection parameters in the `clientConnectorConfiguration` property of `IgniteConfiguration` of your cluster, as shown below: |
| |
| [tabs] |
| -- |
| tab:XML[] |
| |
| [source, xml] |
| ---- |
| <bean id="ignite.cfg" class="org.apache.ignite.configuration.IgniteConfiguration"> |
| <!-- Thin client connection configuration. --> |
| <property name="clientConnectorConfiguration"> |
| <bean class="org.apache.ignite.configuration.ClientConnectorConfiguration"> |
| <property name="host" value="127.0.0.1"/> |
| <property name="port" value="10900"/> |
| <property name="portRange" value="30"/> |
| </bean> |
| </property> |
| |
| <!-- Other Ignite Configurations. --> |
| |
| </bean> |
| |
| ---- |
| |
| |
| tab:Java[] |
| |
| [source, java] |
| ---- |
| IgniteConfiguration cfg = new IgniteConfiguration(); |
| |
| ClientConnectorConfiguration ccfg = new ClientConnectorConfiguration(); |
| ccfg.setHost("127.0.0.1"); |
| ccfg.setPort(10900); |
| ccfg.setPortRange(30); |
| |
| // Set client connection configuration in IgniteConfiguration |
| cfg.setClientConnectorConfiguration(ccfg); |
| |
| // Start Ignite node |
| Ignition.start(cfg); |
| ---- |
| |
| -- |
| |
| === Connection Handshake |
| |
| Besides socket connection, the thin client protocol requires a connection handshake to ensure that client and server versions are compatible. Note that handshake must be the first message after the connection is established. |
| |
| For the handshake message request and response structure, see the <<Handshake>> section above. |
| |
| |
| === Example |
| |
| |
| .Socket and Handshake Connection |
| [source, java] |
| ---- |
| Socket socket = new Socket(); |
| socket.connect(new InetSocketAddress("127.0.0.1", 10800)); |
| |
| String username = "yourUsername"; |
| |
| String password = "yourPassword"; |
| |
| DataOutputStream out = new DataOutputStream(socket.getOutputStream()); |
| |
| // Message length |
| writeIntLittleEndian(18 + username.length() + password.length(), out); |
| |
| // Handshake operation |
| writeByteLittleEndian(1, out); |
| |
| // Protocol version 1.0.0 |
| writeShortLittleEndian(1, out); |
| writeShortLittleEndian(1, out); |
| writeShortLittleEndian(0, out); |
| |
| // Client code: thin client |
| writeByteLittleEndian(2, out); |
| |
| // username |
| writeString(username, out); |
| |
| // password |
| writeString(password, out); |
| |
| // send request |
| out.flush(); |
| |
| // Receive handshake response |
| DataInputStream in = new DataInputStream(socket.getInputStream()); |
| int length = readIntLittleEndian(in); |
| int successFlag = readByteLittleEndian(in); |
| |
| // Since Ignite binary protocol uses little-endian byte order, |
| // we need to implement big-endian to little-endian |
| // conversion methods for write and read. |
| |
| // Write int in little-endian byte order |
| private static void writeIntLittleEndian(int v, DataOutputStream out) throws IOException { |
| out.write((v >>> 0) & 0xFF); |
| out.write((v >>> 8) & 0xFF); |
| out.write((v >>> 16) & 0xFF); |
| out.write((v >>> 24) & 0xFF); |
| } |
| |
| // Write short in little-endian byte order |
| private static final void writeShortLittleEndian(int v, DataOutputStream out) throws IOException { |
| out.write((v >>> 0) & 0xFF); |
| out.write((v >>> 8) & 0xFF); |
| } |
| |
| // Write byte in little-endian byte order |
| private static void writeByteLittleEndian(int v, DataOutputStream out) throws IOException { |
| out.writeByte(v); |
| } |
| |
| // Read int in little-endian byte order |
| private static int readIntLittleEndian(DataInputStream in) throws IOException { |
| int ch1 = in.read(); |
| int ch2 = in.read(); |
| int ch3 = in.read(); |
| int ch4 = in.read(); |
| if ((ch1 | ch2 | ch3 | ch4) < 0) |
| throw new EOFException(); |
| return ((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + (ch1 << 0)); |
| } |
| |
| |
| // Read byte in little-endian byte order |
| private static byte readByteLittleEndian(DataInputStream in) throws IOException { |
| return in.readByte(); |
| } |
| |
| // Other write and read methods |
| |
| ---- |
| |
| |
| == Client Operations |
| |
| Upon successful handshake, a client can start performing various cache operations: |
| |
| * link:binary-client-protocol/key-value-queries[Key-Value Queries] |
| * link:binary-client-protocol/sql-and-scan-queries[SQL and Scan Queries] |
| * link:binary-client-protocol/binary-type-metadata[Binary-Type Operations] |
| * link:binary-client-protocol/cache-configuration[Cache Configuration Operations] |