1
\$\begingroup\$

This is a student project of mine. I got 4/5 points but I suspect there's a lot of weird stuff in there. I never had a chance to hear my teacher's feedback about it and it bothers me so I figured somebody could take a glance at it and provide some feedback.

Quick documentation

First we run "Server" program that opens ports provided by runtime args[] parameters listening in parallel in separate threads.
Next we run "Client" passing to the constructor hostname and list of ports to knock. We can run many clients or set numberofclients variable in code.

Server UDP sockets have access to synchronized data structure that stores information about incoming connections with sockets.
After each connection program checks if order of connections is same as key. If yes then server sends message with TCP port to the socket of unauthorized yet client that sent correct combination of ports and another thread is run with TCP socket listening for authorized client. Authorized client connects to TCP socket and exchanges short information with the server. After the exchange TCP socket closes.

Enter key closes program and every thread and socket is freed.

Server

package com.company;

import java.io.IOException;
import java.net.*;
import java.util.*;
import java.util.stream.Collectors;

public class Server implements Runnable{
    byte[] buffer = new byte[2048];
    byte[] sendBuffer = new byte[2048];

    List<DatagramSocket> socketList;
    Map<InetSocketAddress, List<Integer>> internalMap;
    Map<InetSocketAddress, List<Integer>> guestMap;
    List<Integer> key;

    Server(Integer... ports) throws SocketException {
        this.key = new ArrayList<>();
        //key.addAll(Arrays.asList(ports));
        for(Integer portnumber : ports)
            key.add(portnumber);

        this.socketList = new ArrayList<>();
        this.internalMap = new HashMap<>();
        this.guestMap = Collections.synchronizedMap(internalMap);

        List<Integer> uniquePorts = key.stream().distinct().collect(Collectors.toList());

        for(Integer port: uniquePorts){
            DatagramSocket socket = new DatagramSocket(port);
            socketList.add(socket);
        }

        System.out.println("Zajete porty: ");
        socketList.forEach(n -> System.out.print(n.getLocalPort() + ", "));
        System.out.println();


        this.key = socketList.stream().map(DatagramSocket::getLocalPort).collect(Collectors.toList());
    }


    @Override
    public void run() {

        for(DatagramSocket socket : socketList){
            Thread t = new Thread(){
                boolean running;
                @Override
                public void run() {

                    while(true){
                        DatagramPacket packet = new DatagramPacket(buffer, buffer.length);

                        try {
                            socket.receive(packet);
                            ProtocolMessage msgRecvd = new ProtocolMessage(packet.getData());
                            System.out.println(msgRecvd); //dbgging
                            if(!msgRecvd.equals(Protocol.synMessage))
                                throw new ProtocolException("Request not recognized, possibly malformed");

                            InetAddress address = packet.getAddress();
                            int port = packet.getPort();
                            InetSocketAddress guest = new InetSocketAddress(address,port);


                            if(guestMap.containsKey(guest)){
                                guestMap.get(guest).add(socket.getLocalPort());   //refactor it later

                                if(guestMap.get(guest).equals(key)){
                                    ServerClientService serverClientService = new ServerClientService();
                                    int establishedPort = serverClientService.serverSocket.getLocalPort();
                                    System.out.println("Guest: " + address + ":" + port+ " authenticated sending tcp port: " + establishedPort);

                                    ProtocolMessage auth = Protocol.authSuccess;
                                    auth.setValue(establishedPort);
                                    sendBuffer = auth.toByte();
                                    socket.send(new DatagramPacket(sendBuffer,sendBuffer.length, address, port));

                                    serverClientService.run();
                                }

                                else if(key.size() < guestMap.get(guest).size()){
                                    guestMap.remove(guest);
                                }
                            }

                            else{
                                List<Integer> list = new ArrayList<>();
                                List<Integer> synchlist = Collections.synchronizedList(list);
                                synchlist.add(socket.getLocalPort());
                                guestMap.put(guest, synchlist);
                            }

                            sendBuffer = Protocol.ackMessage.toByte();
                            DatagramPacket confirmation = new DatagramPacket(sendBuffer, sendBuffer.length, address, port);
                            socket.send(confirmation);

                        }catch (SocketTimeoutException e){
                            e.printStackTrace();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }

                }
            };
            t.setDaemon(true);  //inne wątki nie moga istniec bez procesu main
            t.start();

        }

        Scanner s = new Scanner(System.in);
        System.out.println("Naciśnij \"Enter\" aby wyjść.....");
        s.nextLine();
    }

}

Server-Client TCP service

package com.company;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerClientService implements Runnable {
    ServerSocket serverSocket;

    ServerClientService(){
        try {
            serverSocket = new ServerSocket(0);
        } catch (IOException e) {
            e.printStackTrace();
            System.err.println("Port niedostepny");
        }
    }

    @Override
    public void run() {
        try(    Socket clientSocket = serverSocket.accept();
                PrintWriter out = new PrintWriter(clientSocket.getOutputStream());
                BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()))
        )
        {
            String query = in.readLine();
            System.out.println(query);
            String response = "hello there client " + getPort();
            out.println(response);
            out.flush();


            serverSocket.close();

        } catch (IOException e) {
            e.printStackTrace();
        }


    }

    public int getPort() {
        return serverSocket.getLocalPort();
    }
}

Client

package com.company;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.*;
import java.util.ArrayList;
import java.util.List;

public class Client {
    byte[] sendBuffer;
    byte[] recvBuffer;
    DatagramSocket socket;
    String hostName;

    List<Integer> key;


    Client(String address, int... ports) throws SocketException {
        this.key = new ArrayList<>();
        this.sendBuffer = new byte[2048];
        this.recvBuffer = new byte[2048];
        this.socket = new DatagramSocket();
        this.hostName = address;
        for(int i : ports)
            this.key.add(i);
    }

    public void run() {
        try {
            InetAddress inetAddress = InetAddress.getByName(hostName);
            for(Integer port : key){

                sendBuffer = Protocol.synMessage.toByte();
                DatagramPacket packet = new DatagramPacket(sendBuffer, sendBuffer.length, inetAddress,port);
                socket.send(packet);
                //System.out.println("sent: " + new ProtocolMessage(sendBuffer));

                DatagramPacket returnPacket = new DatagramPacket(recvBuffer, recvBuffer.length);
                returnPacket.setLength(returnPacket.getLength());
                socket.setSoTimeout(5000);
                try {
                    socket.receive(returnPacket);
                } catch (SocketTimeoutException e){
                    System.err.println("No response, probably invalid authentication");
                }
                ProtocolMessage response = new ProtocolMessage(returnPacket.getData());
                System.out.println(response); //debugging

                if (response.equals(Protocol.ackMessage)) {
                    //proceed
                    continue;
                }
                else if(response.equals(Protocol.authSuccess)){
                    //connect to sent tcp port
                    int portNumber = response.value;
                    startTcpConnection(portNumber);
                    break;
                }

                else{
                    throw new ProtocolException("Response not recognized, possibly malformed");
                    //should like catch it and try again perhaps
                }

            }


        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        socket.close();

    }

    public void startTcpConnection(int port ){
        try(Socket tcpSocket = new Socket(hostName, port);
            PrintWriter out = new PrintWriter(tcpSocket.getOutputStream());
            BufferedReader in = new BufferedReader(new InputStreamReader(tcpSocket.getInputStream()));)
        {

            out.println("Client " + port + ": hello");
            out.flush();
            String response = in.readLine();
            System.out.println(response);


        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}

Common protocol

package com.company;

public class Protocol {
    final static ProtocolMessage synMessage = new ProtocolMessage(1);
    final static ProtocolMessage ackMessage = new ProtocolMessage(2);
    final static ProtocolMessage authSuccess = new ProtocolMessage(3);

}
package com.company;

import java.nio.ByteBuffer;

public class ProtocolMessage {
    int type;
    int value;

    ProtocolMessage(int type, int value){
        this.type = type;
        this.value = value;
    }
    ProtocolMessage(int type){
        this.type = type;
        this.value = 0;
    }
    ProtocolMessage(byte[] bytes){
        ByteBuffer wrapped = ByteBuffer.wrap(bytes);
        this.type = wrapped.getInt(0);
        this.value = wrapped.getInt(50);
    }

    public byte[] toByte(){
        ByteBuffer byteBuffer = ByteBuffer.allocate(2048);
        byteBuffer.putInt(0,this.type);
        byteBuffer.putInt(50,this.value);

        return byteBuffer.array();
    }

    @Override
    public String toString(){
        return type + ":" + value;
    }

    @Override
    public boolean equals(Object o){
        ProtocolMessage another = (ProtocolMessage) o;
        if(another.type == this.type)
            return true;
        else
            return false;
    }

    void setValue(int value){
        this.value = value;
    }
}
\$\endgroup\$
2
\$\begingroup\$

I'm not going to look at the code for what is does, but I can give you some feedback on how it looks / is styled, and where you might improve.

Exception handling

Your current exception handling consist of print errors with a stack trace. There is no explicit error handling or comments about what should be done.

Unnecessary code

Code like this:

    if(another.type == this.type)
        return true;
    else
        return false;

Is really too verbose. It should be:

    return (another.type == this.type);

Missing access modifiers

public class ProtocolMessage {
    int type;
    int value;

type and value could be private, (and final). Prefer classes to be as "closed" as possible.

Missing comments/javadoc

You should document WHY you have a class or a piece of code that is not directly obvious:

  ProtocolMessage(byte[] bytes){
        ByteBuffer wrapped = ByteBuffer.wrap(bytes);
        this.type = wrapped.getInt(0);
        this.value = wrapped.getInt(50);
    }

What is the 50? Why is it there?

Simplify methods

Your run() method in Server has many many nested levels of code. Try to extract pieces of logic so that they have exactly one purpose.

Safe equals

Your equals() method breaks if null or any class other than a ProtocolMessage is provided... make it safe by checking for null and same type ..

public boolean equals(final Object other) {
    if (null == other) {
        return false;
    }
    if (!(other instanceof ProtocolMessage)) {
        return false;
    }
    // equals as you had it.
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.