Jump to content

Creating a local socket client/server


dreameo

Recommended Posts

Creating a server allows for a central place of communication. You might want to have several bot instances communicate. Creating a central server that talks with multiple clients is the best way to communicate. Some noobs use a text file to communicate, they are n00bs.  The issue with that is, the code generally is ignorant of the external environment. Not knowing who is accessing the file, if it's already open, if a change has been made and it's viewing old data, etc.  You could try some hacky things but majority of the time, the hacky things will break (that or you could never scale the size of your project to large numbers).

When you speak with a server, the server is aware of the states of the clients and would give you the rights to do X and Y. Communication is one application of client server architecture. You could easily delegate tasks to your server that you wouldn't want to do as a client.

Server:

Spoiler

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.function.Consumer;

public class Server {

    private ServerSocket server;
    private final int PORT;
    private IO ioHandler;
    private Consumer<String> readHandler;

    Server(int PORT){
        this.PORT = PORT;
    }

    Server(int PORT, Consumer<String> readHandler){
        this.PORT = PORT;
        this.readHandler = readHandler;
    }

    public void listen(){
        try{
            server = new ServerSocket(PORT);
            Socket client = server.accept();
            ioHandler = new IOHandler(client.getInputStream(), client.getOutputStream());

            if(readHandler != null){
                ioHandler.setReadHandler(readHandler);
            }

            new Thread((Runnable) ioHandler).start();
        } catch (IOException e){
            e.printStackTrace();
        }
    }

    public void write(String msg){
        ioHandler.write(msg);
    }

    public void setReadHandler(Consumer<String> reader){
        ioHandler.setReadHandler(reader);
    }

    public void close(){
        try{
            ioHandler.stopListener();
            server.close();
        } catch (IOException e){
            e.printStackTrace();
        }
    }
}

 

Client:

Spoiler

import java.io.IOException;
import java.net.Socket;
import java.util.function.Consumer;

public class Client {

    private Socket socket;
    private final String HOST;
    private final int PORT;
    private IO ioHandler;
    private Consumer<String> readHandler;

    public Client(String HOST, int PORT){
        this.HOST = HOST;
        this.PORT = PORT;
    }

    /*
        overloaded constructor to provide a way to handle read events prior to
        connecting to server
    */
    public Client(String HOST, int PORT, Consumer<String> readHandler){
        this.HOST = HOST;
        this.PORT = PORT;
        this.readHandler = readHandler;
    }

    public void connect(){
        try{
            socket = new Socket(HOST, PORT);
            ioHandler = new IOHandler(socket.getInputStream(), socket.getOutputStream());

            if(readHandler != null){
                ioHandler.setReadHandler(readHandler);
            }

            new Thread((Runnable) ioHandler).start();
        } catch (IOException e){
            e.printStackTrace();
        }
    }

    public void write(String msg){
        ioHandler.write(msg);
    }

    public void setReadHandler(Consumer<String> readHandler){
        ioHandler.setReadHandler(readHandler);
    }

    public void close(){
        try{
            ioHandler.stopListener();
            socket.close();
        } catch (IOException e){
            e.printStackTrace();
        }
    }
}

 

IOHandler:

Spoiler

import java.io.*;
import java.util.function.Consumer;

public class IOHandler implements IO, Runnable {

    private BufferedReader reader;
    private PrintWriter writer;
    private Consumer<String> readHandler;
    private boolean listen = true;

    public IOHandler(InputStream inputStream, OutputStream outputStream){
        reader = new BufferedReader(new InputStreamReader(inputStream));
        writer = new PrintWriter(outputStream, true);
        readHandler = System.out::println; // default read behaviour
    }

    @Override
    public void write(String output) {
        writer.println(output);
    }

    @Override
    public void setReadHandler(Consumer<String> readHandler) {
        this.readHandler = readHandler;
    }

    @Override
    public void stopListener() {
        this.listen = false;
    }
  
    private void read(Consumer<String> readHandler, String string){
        readHandler.accept(string);
    }

    @Override
    public void run() {
        String input;

        try{
            while(listen && (input = reader.readLine()) != null){
               read(readHandler, input);
            }
        } catch (IOException e){
            e.printStackTrace();
        }
    }
}

 

 

IO Interface:

Spoiler

import java.util.function.Consumer;

public interface IO {
    void write(String output);
    void setReadHandler(Consumer<String> input);
    void stopListener();
}

 

 

I didn't fully test these but it seems to be fine.

Usage:

Server must always run before the client. Choose any port you like.

Server:

public class Test {

    public static void main(String[] args) throws InterruptedException {
        Server server = new Server(50000);
        server.listen();

        server.write("Hello peasant");
    }
}

 

Client:

public class Test {
    public static void main(String[] args) throws UnknownHostException {
        Client client = new Client("0.0.0.0", 50000);
        client.connect();
        client.setReadHandler((s) -> {
            if(s.equals("Hello peasant"))
                client.close();
            else
                System.out.println(s);
        });
        client.write("Hi dummy");
    }
}

 

Everything should be really easy and straight forward.

1. Create a server, choose a port, then listen();

2. Create a client, for local usage, just use "0.0.0.0" IP and the same port as the server.

3. Optional, you can choose the read behavior when creating a client or server. By default, when receiving any messages. it will print to console. You can override this in by specifying the read behavior in the constructor or by called the setReadHandler method. The ReadHandler is simple a Functional Interface, Consumer<T>.

4. Call connect();

5. use write(); to freely send messages. Ideally, you would want to create some protocol in the ReadHandler.

6. call close(): to end the connection.

  • Like 10
Link to comment
Share on other sites

6 hours ago, dormic said:

Thanks for the tutorial. What would happen when you have a large botfarm using this system?

Here's an example of what i've done before.

I had one script and multiple accounts. I would start any number of clients, say 4 osbot clients. Without telling it what to do, the server would tell each client which account to log into. The client would bot and after x random minutes, would log off and tell the server that this account is now going on break mode. The server would put that account now in break mode and not hand it out to any of the clients. The server would send over a new account to the client to start botting. So the server was responsible for telling the client which account to use. The server would be notified however of other details, if the account was banned for instance. The server would remove the account and immediately send over a new account to the client. 

The script itself was to get from 1-60 WC. So imagine just having a large list of accounts and after a couple days, you would automatically have couple hundred of accounts with just 60wc. But things get a bit more complex if you want to use proxies and stuff. Since then you would need the server to also create processes (start the client from a cmd).

The important thing is just communication. You can talk between all your bots when you have a central place to do so. From that point, you can do whatever you like.

 

EDIT: One thing I should point out is that the code only supports a 1 to 1 connection. You would need to modify the server to support talking to multiple people. I wasn't thinking about that last night.

An easier way to communicate is making HTTP calls. That way you don't need to worry about multi threading if you aren't familiar. You would still have a server but this time it would be an HTTP server and you wouldn't have sockets. Each client would make an HTTP call to the server with information.

Edited by dreameo
  • Like 1
Link to comment
Share on other sites

22 minutes ago, extatus said:

Needed something like this for login-handling.

So far (externally) ive used .txt files but for static values only .  I definetly see that race condition could be a thing (Alek explained it a while back)

Thanks.

 

 

Yeah I would suggest doing what I said in my edit for now.

Need to rework the code to support 1-n connections from server to client. Over looked that.

There could be some unneeded complexities due to using a socket client/server. When an HTTP server might be just as efficient and easier. Going to look up some answers.

Edited by dreameo
Link to comment
Share on other sites

6 hours ago, dreameo said:

Yeah I would suggest doing what I said in my edit for now.

Need to rework the code to support 1-n connections from server to client. Over looked that.

There could be some unneeded complexities due to using a socket client/server. When an HTTP server might be just as efficient and easier. Going to look up some answers.

server.accept is blocking and returns a socket representing that connection so you can just do something like:

final List<Socket> sessions = new ArrayList<>();
  
while (listening)
{
    ...
	sessions.add(server.accept());
}

You would need to have the listener on a separate thread so that established connections can process data / do IO operations.

For more advanced data you could also look into sending binary data or serialization with something like Gson (helps a lot if you want to serialize and send objects across the socket).

I like to subclass Socket and customize it a bit so you have more control over each session.

Edited by LoudPacks
  • Like 1
Link to comment
Share on other sites

23 minutes ago, LoudPacks said:

server.accept is blocking and returns a socket representing that connection so you can just do something like:


final List<Socket> sessions = new ArrayList<>();
  
while (listening)
{
    ...
	sessions.add(server.accept());
}

You would need to have the listener on a separate thread so that established connections can process data / do IO operations.

For more advanced data you could also look into sending binary data or serialization with something like Gson (helps a lot if you want to serialize and send objects across the socket).

I like to subclass Socket and customize it a bit so you have more control over each session.

That's not really the issue.

The issue is, do I really want to create a separate thread for each client connection. Seems a bit excessive. Making calls via REST api and an HTTP server seems easy for everyone.

Link to comment
Share on other sites

19 minutes ago, dreameo said:

That's not really the issue.

The issue is, do I really want to create a separate thread for each client connection. Seems a bit excessive. Making calls via REST api and an HTTP server seems easy for everyone.

Only for the listener, not for each connection. Although scaling wise it might be a good idea. I also don't see any HTTP anywhere in this post.

Edited by LoudPacks
Link to comment
Share on other sites

19 minutes ago, LoudPacks said:

Only for the listener, not for each connection. Although scaling wise it might be a good idea. I also don't see any HTTP anywhere in this post.

Yes I'm aware of what you said xD.

If you look, there exists a thread for each Client Output Stream. To prevent the server from being blocked waiting on input. I'd rather not do this.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...