Skip to main content
deleted 19 characters in body; edited title
Source Link
Jamal
  • 35.2k
  • 13
  • 134
  • 238

Java Multi-threaded Server Socketserver socket

I'm somewhat new to network programming. I I have a server that uses Ubuntu, which needs to send data quickly to about 50 clients. As

As of now, I have about 50 concurrent connections (of course), and it needs to be scalable up to 500.

JSONServer

JSONServer

JSONClient

JSONClient

Main Points

Main Points

  • Are there any techniques I should be using?
  • Does the code above properly close sockets?
  • I have researched about java.nio and wondered, should I use that package in my current situation? I know it's meant for non-blocking sockets, and millions of connections, but I only expect to have up to 500 concurrent connections.
  • Am I using threads correctly? 1 socket --> 1 connection
  1. Are there any techniques I should be using?
  2. Does the code above properly close sockets?
  3. I have researched about java.nio and wondered, should I use that package in my current situation? I know it's meant for non-blocking sockets, and millions of connections, but I only expect to have up to 500 concurrent connections.
  4. Am I using threads correctly? 1 socket --> 1 connection

Java Multi-threaded Server Socket

I'm somewhat new to network programming. I have a server that uses Ubuntu, which needs to send data quickly to about 50 clients. As of now, I have about 50 concurrent connections (of course), and it needs to be scalable up to 500.

JSONServer

JSONClient

Main Points

  • Are there any techniques I should be using?
  • Does the code above properly close sockets?
  • I have researched about java.nio and wondered, should I use that package in my current situation? I know it's meant for non-blocking sockets, and millions of connections, but I only expect to have up to 500 concurrent connections.
  • Am I using threads correctly? 1 socket --> 1 connection

Multi-threaded server socket

I'm somewhat new to network programming. I have a server that uses Ubuntu, which needs to send data quickly to about 50 clients.

As of now, I have about 50 concurrent connections (of course), and it needs to be scalable up to 500.

JSONServer

JSONClient

Main Points

  1. Are there any techniques I should be using?
  2. Does the code above properly close sockets?
  3. I have researched about java.nio and wondered, should I use that package in my current situation? I know it's meant for non-blocking sockets, and millions of connections, but I only expect to have up to 500 concurrent connections.
  4. Am I using threads correctly? 1 socket --> 1 connection
Source Link
Kayla
  • 598
  • 1
  • 6
  • 9

Java Multi-threaded Server Socket

I'm somewhat new to network programming. I have a server that uses Ubuntu, which needs to send data quickly to about 50 clients. As of now, I have about 50 concurrent connections (of course), and it needs to be scalable up to 500.

I've created a JSON-based protocol to handle the streams and binary. Can these 2 classes be reviewed for any potential problems, bad techniques, etc?

JSONServer

package com.wordpress.waffalware.tcpson;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.ArrayList;

import org.json.JSONObject;

public class JSONServer {

    private int connectedSocketLimit = 10000;
    private RequestCallback onRequest;
    private ErrorCallback onError;

    private ServerSocket server;
    private boolean connected;

    public final ArrayList<JSONClient> clients = new ArrayList<JSONClient>();

    public void setOnRequest(RequestCallback handler) {
        onRequest = handler;
    }

    public void setOnError(ErrorCallback handler) {
        onError = handler;
    }

    public boolean isConnected() {
        return connected;
    }

    public void start(int port) throws IOException {
        server = new ServerSocket();
        server.setSoTimeout(5000);
        server.setReuseAddress(true);
        server.setPerformancePreferences(1, 0, 0);
        server.bind(new InetSocketAddress(InetAddress.getByAddress(new byte[] {
                0, 0, 0, 0 }), port));

        Thread accepter = new Thread(new Runnable() {
            @Override
            public void run() {
                while (connected) {
                    Socket client = null;
                    try {
                        client = server.accept();
                    } catch (SocketTimeoutException e) {
                        continue;
                    } catch (IOException e) {
                        if (onError != null) {
                            onError.onError(e);
                        }
                    }
                    if (client != null) {
                        handleClient(new JSONClient(client));
                    }
                }
            }
        }, "JSONServer-accepter");

        connected = true;
        accepter.start();
    }

    public void close() {
        connected = false;
        if (server != null) {
            try {
                server.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            server = null;
        }
    }

    @Override
    protected void finalize() throws Throwable {
        close();
        super.finalize();
    }

    private void handleClient(final JSONClient client) {
        Thread clientThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    clients.remove(client);
                    clients.add(client);
                    client.setSoTimeout(5000);
                    while (connected) {
                        JSONPacket request = client.readPacket();
                        if (request == null) {
                            break;
                        }
                        JSONPacket response;
                        if (onRequest != null) {
                            response = onRequest.onRequest(request);
                        } else {
                            response = new JSONPacket(new JSONObject());
                        }

                        client.writePacket(response);

                        if (clients.size() > connectedSocketLimit) {
                            break;
                        }
                    }
                } catch (SocketTimeoutException e) {
                    if (onError != null) {
                        onError.onError(e);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    client.close();
                    clients.remove(client);
                }
            }
        }, "JSONServer-clientHandler");
        clientThread.start();
    }

    public interface RequestCallback {
        public JSONPacket onRequest(JSONPacket request);
    }

    public interface ErrorCallback {
        public void onError(Exception e);
    }

}

JSONClient

package com.wordpress.waffalware.tcpson;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.ByteBuffer;

import org.json.JSONObject;

public class JSONClient {

    private boolean connected;
    private Socket client;

    private static final byte[] VERSION1 = new byte[] { 0x17, 0x78 };

    public static JSONPacket getResponse(String host, int port,
            JSONPacket request) throws IOException {
        JSONClient client = new JSONClient();
        try {
            client.setSoTimeout(5000);
            client.connect(new InetSocketAddress(host, port));

            client.writePacket(request);
            return client.readPacket();

        } finally {
            client.close();
        }
    }

    public JSONClient() {
        client = new Socket();
    }

    protected JSONClient(Socket baseClient) {
        client = baseClient;
        connected = baseClient.isConnected() && !baseClient.isClosed();
    }

    public boolean isConnected() {
        return connected;
    }

    public void connect(SocketAddress endpoint) throws IOException {
        client.connect(endpoint);
        connected = true;
    }

    public void close() {
        connected = false;
        try {
            client.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        client = new Socket();
    }

    /**
     * Writes <code>packet</code> to the <code>Socket</code>.
     * 
     * @param packet
     *            The <code>JSONPacket</code> to write to <code>Socket</code>.
     * @throws IOException
     *             If an IO/Error has occurred.
     */
    public void writePacket(JSONPacket packet) throws IOException {
        try {
            OutputStream str = client.getOutputStream();
            byte[] payloadRaw = packet.getPayload().toString().getBytes();
            byte[] extraData = packet.getExtraData();
            byte[] buffer = ByteBuffer
                    .allocate(14 + payloadRaw.length + extraData.length)
                    .put(VERSION1).put(intToByteArray(0))
                    .put(intToByteArray(payloadRaw.length))
                    .put(intToByteArray(extraData.length)).put(payloadRaw)
                    .put(extraData).array();
            str.write(buffer);
            str.flush();
        } catch (IOException e) {
            close();
            throw e;
        }
    }

    /**
     * Reads and creates a <code>JSONPacket</code> from the <code>Socket</code><br>
     * <br>
     * If <code>null</code> is returned, the other side of the
     * <code>Socket</code> should be treated as incompatible, and the
     * <code>Socket</code> be closed immediately.
     * 
     * @param str
     *            The stream to read the <code>JSONPacket</code> from.
     * @return A <code>JSONPacket</code> representing the data from the stream,
     *         or <code>null</code> if there were no bytes to read or the data
     *         does not follow the format.
     * @throws IOException
     *             If an IO/Error has occurred.
     */
    public JSONPacket readPacket() throws IOException {
        try {
            byte[] tempBytes;

            JSONPacket ret = null;

            InputStream str = client.getInputStream();

            tempBytes = new byte[14];
            if (str.read(tempBytes, 0, tempBytes.length) == tempBytes.length) {

                if (VERSION1[0] == tempBytes[0] & VERSION1[1] == tempBytes[1]) {

                    // int reservedValue = byteArrayToInt(tempBytes, 2);
                    int payloadSize = byteArrayToInt(tempBytes, 6);
                    int extraDataSize = byteArrayToInt(tempBytes, 10);

                    if (payloadSize >= 0 & extraDataSize >= 0) {
                        tempBytes = new byte[payloadSize];

                        if (str.read(tempBytes, 0, tempBytes.length) == tempBytes.length) {
                            JSONObject payloadData = new JSONObject(new String(
                                    tempBytes));

                            tempBytes = new byte[extraDataSize];

                            if (str.read(tempBytes, 0, tempBytes.length) == tempBytes.length) {
                                ret = new JSONPacket(payloadData, tempBytes);
                            }

                        }
                    }

                }
            }

            return ret;
        } catch (IOException e) {
            close();
            throw e;
        }
    }

    public void setSoTimeout(int timeout) throws SocketException {
        // TODO: Should the connection stop if an exception is thrown?
        client.setSoTimeout(timeout);
    }

    private static int byteArrayToInt(byte[] buffer, int offset) {
        return (buffer[offset] & 0xFF) << 24
                | (buffer[offset + 1] & 0xFF) << 16
                | (buffer[offset + 2] & 0xFF) << 8 | buffer[offset + 3] & 0xFF;
    }

    private static byte[] intToByteArray(int buffer) {
        return new byte[] { (byte) ((buffer >> 24) & 0xFF),
                (byte) ((buffer >> 16) & 0xFF), (byte) ((buffer >> 8) & 0xFF),
                (byte) (buffer & 0xFF) };
    }

}

The JSONPacket is just a holding object for a byte array, and a JSONObject.

A long time ago, the accept function kept throwing an IOException because there were "too many open files". I suspected it was that I forgot to call Socket.close() after I called accept.

Main Points

  • Are there any techniques I should be using?
  • Does the code above properly close sockets?
  • I have researched about java.nio and wondered, should I use that package in my current situation? I know it's meant for non-blocking sockets, and millions of connections, but I only expect to have up to 500 concurrent connections.
  • Am I using threads correctly? 1 socket --> 1 connection