Transmit - Multi-Threaded Server Socket Library

Discussion in 'Resources' started by kumpelblase2, Jan 21, 2012.

Thread Status:
Not open for further replies.
  1. Hey Bukkit!


    Some of you might be confused because of the title. So let me explain:
    Multi-Threaded means that the program can perform multiple actions at the SAME time. Normally everything is executed after each other. When you have (multiple) threads you can do things at the same time without waiting for something to finish.
    But what's Socket? A socket is basically an 'adapter' that listening on network input. So whenever a packet is send to a port and a socket is listening on that port, the socket will receive it.

    And what does this thing actually do? Well, it makes things easier. With this library you just create an instance and hook up the listeners and you're done. You don't need to worry about the threads, nor about receiving packets, everything is done by the library.

    Where could this be useful?
    • Connection between website <-> server
    • Remote connection to server
    So, how do I use it?

    First, you need to create the actual Socket:
    Code:
    TransmitServer server = new TransmitServer(maxClients, port);
    maxClients: maximum amount of clients being connected at the same time. 0 = infinite.
    port: the port the socket should listen on. there's no default.

    Now the socket is already listening for incoming connections and incoming packets from clients.


    MAKE SURE YOU TAKE A LOOK AT POST #6!
    So now, maybe you want broadcast a message. Here's how you doing it:
    Code:
    server.broadcastMessage("Hey clients!");
    You can also simply send something to just one client:
    Code:
    server.sendToClient(clientID, "Hey client!");
    The clientID represents the id a client get's when he connects.

    And here's how you set up the listener(s):
    Code:java
    1.  
    2. public class YourClientListener implements TransmitClientEventListener
    3. {
    4. @Override
    5. public void onClientRecieve(ClientRecieveEvent event)
    6. {
    7. System.out.println("Yaay, we got some data! Here it is: " + event.getPacketData());
    8. }
    9.  
    10. @Override
    11. public void onClientDisconnect(ClientConnectionEvent event)
    12. {
    13. System.out.println("Oh no, we lost a client :(");
    14. }
    15.  
    16. @Override
    17. public void onClientConnect(ClientConnectionEvent event)
    18. {
    19. System.out.println("Oh, someone found it's way!");
    20. }
    21. }


    There's also a listener for server actions (but it's not really needed.)
    Code:java
    1.  
    2. public class YourServerListener implements TransmitServerEventListener
    3. {
    4. @Override
    5. public void onServerSend(ServerSendEvent event)
    6. {
    7. System.out.println("Off you go data! That's what we just send: " + event.getPacketData());
    8. }
    9. }
    10.  

    You can edit then how you would need/like. Now you only need to register them (like with bukkit) and here's how you do that:
    Code:
    server.addClientListener(<YourClientListener>);
    Just replace the parameter with an instance of you ClientListener. The same thing applies for the server listener:
    Code:
    server.addServerListener(<YourServerListener>);
    If you want to remove them, just do this:
    Code:
    server.removeClientListener(<YourClientListener>);
    server.removeServerListener(<YourServerListener>);
    And that's it! You're done. Now you see when data arrives or when a client disconnects. What you can do with it, lies in you imagination.
    If you have questions, error, bugs or what ever, just post it here or send me a pm and i will take a look. Especially if you have something that I need to improve or to add, LET ME KNOW! Thanks.

    Source: dropbox-link
    jar: dropbox-link

    Have nice day! ;)






    You need a client, but don't know how to make one yourself? I now have provided a client to use! see #46 and have fun ;)
     
    Rocoty likes this.
  2. Offline

    MCManager

    Wow, I posted a library for sockets less than a week ago (here) :eek:. What a coincidence :p. Anyway, I would suggest that when receiving messages, you either use a delimiter or put the length of the message in the header of the message, or else it is possible that the message will be broken up (read my thread on that). Other than that, nice job! :)
     
  3. ohh... I didn't noticed that :D ( But as i look at how you did it, it really differs from my 'solution' ;) )
    And I don't think you need like a length or a delimeter to check if the packet is brocken or not since tcp has a checksum in the header which also includes the length of the (user-)data. Meaning that if the userdata gets brocken in the process the checksum would be different. So the client automatically tries to get the same packet from the server again. Sending the length again in the (user-)data is kinda useless actually.

    BTW: I never excperienced such a problem everywhere I use TCP. It could happen when you use UDP, because it doesn't get checked if the packet was successful recieved or not.
     
  4. Offline

    MCManager


    You probably haven't noticed the problem because when you tested it, the server/client were on the same computer or it was on a high-end connection. Sometimes, when the connection is not as good or when sending large amounts of data, this problem can occur.


    Also:

     
  5. For this library, yes, I only tested it locally, BUT: I'm also developing my own game and for example the movement packets are currently also send via TCP (i know, UDP would be better for movements) and I NEVER experienced the problem your quote describes. And these movement packets are SO MANY, so I can draw a conclusion. If they wouldn't have been received successfully and with the correct data, the client/server wouldn't be able to parse/handle them and a exception would appear which was currently never the case.

    * Added getClientID to 'ClientConnectionEvent'. It returns the ID of the client which connected/disconnected.
    * Fixed duplication of clientIDs. They're now generated randomly and not based on the count. Also they won't grow to infinity anymore.

    * (YAAY!) Added JSON as packet protocol. All messages are now send in JSON-format with this structure:
    Code:
    {
    "packets": [
        {
          "command":"testcommand",
          "respondCommand":"",
          "args": [
              "argument1",
              "argument2"
          ]
        },
        {
          "command":"othercommand",
          "respondCommand":"update",
          "args": []
        }
        ...
    ]
    }
    You could now put more than one action/command/whatever in one packet, so you could reduce the amounts of packets send which means less traffic. So let me explain further:
    'command' is the command you send to the server or client. Example 'UpdateChat' or whatever you like.
    'respondCommand' should be used if this command is a response to a command that was executed earlier. So you would pass the older command in here.
    'args' is an array with arguments needed for the command. There doesn't need to be values in it.

    Also the Listeners got some new methods because of this change. 'getPacketData()' is no longer available. There are three other methods instead:
    Code:
    event.getCommand();
    event.getOriginalCommand();
    event.getArguments();
    They apply for 'ServerSendEvent' as well as 'ClientRecieveEvent'.

    Because of the change sending text messages isn't as easy as it used to. You would need to create an instance of the class Packet (de.kumpelblase2.transmit.Packets) and and set all values or set those in the constructor. You can still pass in "" if you don't want to have data for that key in the json-format.
    Broadcasting a message could look like this:
    Code:
    TransmitServer.broadcastMessage(new Packet("command", "", new String[] {"Hello World!"}));
    (Like I said, you could also leave "command" blank.)
    Sending a command to a specific client looks like the same, but instead of the String you would need to pass in a 'Packet' :
    Code:
    TransmitServer.sendToClient(clientID, new Packet("command", "", new String[] {"Hey Client!"}));
    As a side note: If you send multiple commands in one packet from the client, they will get handled separately and for each of the command the receive event will get fired.

    If you have problems, just let me know.

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: May 23, 2016
  6. Offline

    wouter0100

    One question,
    I have pasted private
    TransmitServer server = new TransmitServer(0, 25567);
    In my main class, and created another class named "[Pluginname]SocketListener"
    With this code in it:

    Code:
    package me.Wouter0100.[Pluginname];
     
    import de.kumpelblase2.transmit.ServerEvents.*;
     
    public class  [Pluginname]SocketListener implements TransmitServerEventListener
    {
        @Override
        public void onServerSend(ServerSendEvent event)
        {
            System.out.println("Off you go data! That's what we just send: " + event.getArguments());
        }
    }
    
    Is this okay? And how can i send data from PHP to the Server?
     
  7. Offline

    Killie01

     
  8. Correct. But since the protocol is now handled in json you would need to do that as well. I'm going to give some examples for doing that in some minutes.
     
  9. Offline

    wouter0100

    I know already JSON things, JavaScript and PHP....

    But thanks ;D
     
  10. That's good but i'm giving an example anyways so other might not need to ask:

    PHP:
    $jsonObject = array(
        
    'packets' => array(
            array(
                
    'command' => "testCommand",
                
    'respondCommand' => "",
                
    'args' => array("argument1""argument2")
            ),
            array(
                
    'command' => "testCommand2",
                
    'respondCommand' => "request",
                
    'args' => array()
            )
        )
    );
     
    $jsonString json_encode($jsonObject);
    This will be the structure you'll need to use. You can add as many arrays inside the 'packets'-array as you want.
     
  11. Offline

    wouter0100

    Oh, Nice! And sending like
    PHP:
    $jsonObject = array(
        
    'packets' => array(
            array(
                
    'command' => "testCommand",
                
    'respondCommand' => "",
                
    'args' => array("argument1""argument2")
            ),
            array(
                
    'command' => "testCommand2",
                
    'respondCommand' => "request",
                
    'args' => array()
            )
        )
    );
     
    $jsonString json_encode($jsonObject);
    $sock socket_create(AF_INETSOCK_STREAMgetprotobyname('tcp'));
     
    socket_connect($sock, [IP], 25567);
     
    socket_send($sock$jsonObjectstrlen($jsonObject), MSG_DONTROUTE);
     
    socket_close($sock);
    or?

    Thanks anyways ;D
     
  12. should work, yes.
     
  13. Offline

    wouter0100

    I got:
    Line 68 is public TransmitServer server = new TransmitServer(0, 25567);
     
  14. Offline

    wouter0100

    Uhm, Okay, I think i have done this right, but it still dont work.
    I have add the com.google.gson things to my src file?
     
  15. hm.... The google.gson thing shouldn't be your problem, this is inside the jar already...
    just to be clear: did you use fat jar and/or maven or something else and what exactly did you use?


    EDIT: just tested it with fat jar... works fine for me.
     
  16. Offline

    wouter0100

    Oh, Okay, i dont have used anything, but i have installed 'Fatjar' and dont know how that works :oops:
     
  17. well then:
    right click on your project -> a bit above 'properties' there should be a green cross with 'build fat jar'. click that -> you can now specify the name of the jar and click next -> now make sure that the transmit library IS CHECKED. Hit finish and it should build the jar for you.

    sidenote: the plugin.yml needs to be in the src folder otherwise fat jar isn't able to add it.
     
  18. Offline

    wouter0100

    Thanks!
    I think its done :D
     
  19. Nice, I'm looking forward to see what you've accomplished with this ;)
     
  20. Offline

    soliddanii

    ok, i figured out. now i've my plugin working and listening for client data. I created a client that sends Strings, and I configured the server to show that strings in the log info

    Code:
    main.log.log(Level.INFO, "Yaay, we got some data! Here it is: {0}", event.getRawData());
    but when i try the client the log throws this error :S:

    Code:
    [INFO] Oh, someone found it's way!
    2012-03-13 17:23:32 [SEVERE] Exception in thread "Thread-12"
    2012-03-13 17:23:32 [SEVERE] com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 5
    2012-03-13 17:23:32 [SEVERE]    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:180)
    2012-03-13 17:23:32 [SEVERE]    at com.google.gson.Gson.fromJson(Gson.java:755)
    2012-03-13 17:23:32 [SEVERE]    at com.google.gson.Gson.fromJson(Gson.java:721)
    2012-03-13 17:23:32 [SEVERE]    at com.google.gson.Gson.fromJson(Gson.java:670)
    2012-03-13 17:23:32 [SEVERE]    at com.google.gson.Gson.fromJson(Gson.java:642)
    2012-03-13 17:23:32 [SEVERE]    at de.kumpelblase2.transmit.TransmitServer.handle(TransmitServer.java:188)
    2012-03-13 17:23:32 [SEVERE]    at de.kumpelblase2.transmit.ClientConnectionThread.run(ClientConnectionThread.java:128)
    2012-03-13 17:23:32 [SEVERE]    at java.lang.Thread.run(Unknown Source)
    2012-03-13 17:23:32 [SEVERE] Caused by: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 5
    2012-03-13 17:23:32 [SEVERE]    at com.google.gson.stream.JsonReader.expect(JsonReader.java:339)
    2012-03-13 17:23:32 [SEVERE]    at com.google.gson.stream.JsonReader.beginObject(JsonReader.java:322)
    2012-03-13 17:23:32 [SEVERE]    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:168)
    2012-03-13 17:23:32 [SEVERE]    ... 7 more
    
    I supose it has to do with the type object and string ( i think object was expected, or i'm wrong?)
    I want to send string (like comands) and depending on the command is reciebed the server will do something (switch case?)
    Thanks!
    Thanks for your help

    ok, i supose i need to pass the information in json format, but i dont have any idea on how to do that :(

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: May 23, 2016
  21. soliddanii Yep, needs to be in json. Did you read these posts by me:
    #6
    and
    #11 (php example)
    ?

    in which language are conding the client in?
     
  22. Offline

    soliddanii

    thanks for your response, i have already read all your post but i don't understand how to do it. I'm trying to do it in java :rolleyes:
     
  23. ok.
    I'm using the gson library for (de-)serializing the packet data. I don't know if there are any tutorials on youtube, but there should be some examples you can find using google. Depending on how large your project is you might not need that library and do it normally with strings (see my example in #6).
     
  24. Offline

    soliddanii

    bffff, why did you changed the amazing strings to json! xD
     
  25. sending some simple strings isn't easy now, but if you're really want to make a good way to communicate between an app and the server, it's always better to have an own protocol. otherwise you won't have unified commands and every request from the client might look different. What I'm providing is a, in my opinion, good way to handle commands between server and client.

    I'm not forcing you to use my lib. There's another one out there, so if you find my solution worse, you can still change to the other one ;)
     
  26. Offline

    soliddanii

    yes, i want to use your library because its amazing and easy. I only have the json problem, i´ll try to solve it, and i´ll tell you. Yo are right with the protocol, its better.
    I dont understand how to make it with strings like #6 . I cant put that in a string, can you give me an example? or a methodi should use? I´m so noob with json xD

    Thanks for your library and all your support :D
     
  27. no problem ;)
    I think I have a little testing project laying around which might help ya, but I'm pretty sure it's not using the gson lib but we'll se...

    this is what i found. It's not using the library but might just give you an idea:
    Code:
    try
    {
                Socket socket = new Socket("127.0.0.1", 25567);
                System.out.println("Connected");
                String toSend = "{\"packets\":[{\"command\":\"update\",\"responseCommand\":\"none\",\"args\":[]}]}";
                socket.getOutputStream().write(toSend.getBytes("UTF-8"));
                socket.getOutputStream().flush();
                System.out.println("data written");
                socket.close();
    }
    catch (UnknownHostException e)
    {
                e.printStackTrace();
    }
    catch(IOException ex)
    {
                ex.printStackTrace();
    }
    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: May 23, 2016
  28. Offline

    soliddanii

    Thanks a LOT! I´ll try it right now, but, why the json string is writed like that? With that "\" , i mean, if i have my own json sentence (for example #6) how do you put it in that format with the \, where do you put the \

    Thanks :p
     
  29. soliddanii: the \ are used to say java that the " should be inside the string and not end it.
     
Thread Status:
Not open for further replies.

Share This Page