Tech and Media Labs
This site uses cookies to improve the user experience.




Grid Ops Java - TcpSocketsPort

Jakob Jenkov
Last update: 2016-10-10

To read messages from incoming TCP connections froma TcpServer you use a TcpSocketsPort. The TcpSocketsPort takes care of registering incoming connections (SocketChannel instances) with a NIO Selector, read data from the connections and break the data up into messages.

Creating a TcpSocketsPort

You create a TcpSocketsPort like this:

TcpPort tcpServer = GridOps.tcpServerBuilder().buildAndStart();

TcpSocketsPort tcpSocketsPort =
        GridOps.tcpSocketsPortBuilder().tcpServer(tcpServer).build();

Reading Messages

You read messages from the TcpSocketsPort object via its read() method. Here is an example:

MemoryBlock[] messages = new MemoryBlock[64];

int messageCount = tcpSocketsPort.read(messages);

The read() method reads messages into MemoryBlock instances (actually TcpMessage instances which is a subclass of MemoryBlock). The TcpSocketsPort will write the MemoryBlock instances into the MemoryBlock array passed as parameter to the read() method. The value returned from the read() method is the number of messages read into the MemoryBlock array.

You can iterate the message written into the MemoryBlock array like this:

int messageCount = tcpSocketsPort.read(messages);

for(int i=0; i<messageCount; i++){
    MemoryBlock message = messages[i];
}

This code can be simplified a bit to:

for(int i=0, n=tcpSocketsPort.read(messages); i<n; i++){
    MemoryBlock message = messages[i];
}

Whether you like this simplification or not, I will leave up to you.

read() is Non-blocking

The read() method is non-blocking. It reads all full messages received as of the time the read() method is called. Half received messages are cached inside the TcpSocketsPort until the full message has been received. It is possible to get 0 (zero) messages back from a read() call.

TcpMessage

The TcpMessage class is a subclass of the MemoryBlock class. A MemoryBlock is a sequence of bytes stored in a larger, shared array. The TcpMessage class adds some information about what TCP socket the message was read from. When reading messages from TCP sockets, the returned messages are represented by TcpMessage instances. Therefore you can cast the read messages to TcpMessage instances, like this:

for(int i=0, n=tcpSocketsPort.read(messages); i<n; i++){
    TcpMessage message = (TcpMessage) messages[i];
}

Writing Messages

Writing messages to sockets managed by the TcpSocketsPort requires the following steps:

  • Allocate a TcpMessage to hold the response.
  • Write data to the TcpMessage.
  • Decide what TcpSocket the TcpMessage should be sent to.
  • Enqueue the TcpMessage with the desired TcpSocket.
  • Write enqueued messages to their designated TcpSockets.

A full example can be seen below. The actual writing of data to the TcpMessage has been left out. That will be covered in a different tutorial.

TcpServer tcpServer = GridOps.tcpServerBuilder().buildAndStart();

TcpSocketsPort tcpSocketsPort =
        GridOps.tcpSocketsPortBuilder().tcpServer(tcpServer).build();

MemoryBlock[] messages = new MemoryBlock[64];

for(int i=0, n=tcpSocketsPort.read(messages); i<n; i++){
    TcpMessage request  = (TcpMessage) messages[i];
    TcpMessage response = tcpSocketsPort.allocateWriteMemoryBlock(128);

    //write data to response - left out for brevity

    // send response to tcpSocket request was received from
    response.tcpSocket = request.tcpSocket;

    tcpSocketsPort.enqueue(response);
}

// write as much as possible of enqueued messages to the sockets they
// are enqueued for.
tcpSocketsPort.writeToSockets();

Note, that the writeToSockets() method called at the end of this example does not guarantee that all enqueued messages are written to their designated sockets. The writeToSockets() method will write as much data as possible to the designated sockets, but if a write to a socket fails to write any bytes (writes 0 bytes), it will not attempt to write more data to that socket until next time you call writeToSockets().

The part of the previous example that reads messages from the TcpSocketsPort and writes responses back should normally be called within a loop. That way the writeToSockets() method will get called once per iteration in the loop, and thus any partially written messages will eventually be written to their designated sockets. Here is a simple example that shows the previous example inside a while loop:

TcpServer tcpServer = GridOps.tcpServerBuilder().buildAndStart();

TcpSocketsPort tcpSocketsPort =
        GridOps.tcpSocketsPortBuilder().tcpServer(tcpServer).build();

MemoryBlock[] messages = new MemoryBlock[64];

while(true) {
    for(int i=0, n=tcpSocketsPort.read(messages); i<n; i++){
        TcpMessage request  = (TcpMessage) messages[i];
        TcpMessage response = tcpSocketsPort.allocateWriteMemoryBlock(128);

        // write data to response - left out for brevity

        // send response to tcpSocket request was received from
        response.tcpSocket = request.tcpSocket;

        tcpSocketsPort.enqueue(response);
    }

    // write as much as possible of enqueued messages to the sockets they
    // are enqueued for.
    tcpSocketsPort.writeToSockets();
}

In a real application you would probably have a different while() clause that reacts to some stop signal. This example just uses a while(true) loop to illustrate the point about repeating the reading and writing of messages.

Jakob Jenkov




Copyright  Jenkov Aps
Close TOC