Advertisement

Java Server Q&A

Started by July 14, 2017 07:19 AM
27 comments, last by Kylotan 7 years, 4 months ago

Yeap I tested it with println, it wasn't calling those methods. Eventually I added run() method to the player constructor.

Weird how the tutorial excluded that.

 

The guy who wrote the tutorial also failed to explain a bit and he was using deprecated information.

Going to look for a new one.

 

So I've got a new question, if I want to send/receive data through a buffer over the network which classes/methods would I need, I'm not sure which they're called for example "DataOutputStream".

 

So far what I've got down is creating the server socket which is just one line of code, creating a boolean and then a do until loop for the main method which keeps the server alive, I then have a hashmap or arraylist which ever works to reference new Player instances, I look for incoming connections and when one is found I store the socket and name in a new object Player then increase the player count. I dont know how to loop for disconnections yet because that requires the player 'pinging' the server every 30 seconds or so. If the server receives it the client is still connected else destroy that player object and decrease player count. 

What I'd like to do next is receive a message from the client, I've created a chatbox on the client and prepared a send message button. My format is msg_type, msg. Msg_type represents an integer from 1-XXX and depending on the message type the server can anticipate a different msg using a switch case statement. This is what I tried..


 


package mypackage;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.io.BufferedReader;
public class Player {

    private DataOutputStream output;//define a data output that we will later use to send messages to the client
    private DataInputStream input; //define a data input stream that we will later us to read messages from the client;
    
    private Socket socket;// define a socket
    private boolean alive = true; //define a boolean to keep the player class running
    private String name; //define a string that will hold the player name
    
    public Player(String nm, Socket sck) //constructor or method ?
    {
        socket =sck; //set the received socket to the player class
        name = nm;// set the player name
        
        
        try {
            socket.setSoTimeout(5000);//Set the socket timeout to 30 seconds
            output = new DataOutputStream(socket.getOutputStream()); //get the data output stream and set it to output
            output.flush();//flush away all the bytes on the data output stream
            input = new DataInputStream(socket.getInputStream()); //get the data input stream and set it to input
            
            run();
        } catch(IOException e){}
    }

    
    
    public void run()
    {
        do
        { //start a do loop while alive is true
            checkForMessages();//check for messages
System.out.println("Run method..");
        }
        while (alive);
    }        
        
    
    private void checkForMessages()
    {
        int received_type;
        String receive_text;
        System.out.println("CheckForMessages method..");
        
        try{
            received_type = input.readInt();
        
        if (received_type != 0)
            {
                System.out.println(received_type);
            }
        
        if (received_type == 1)
                {
            //1 means textmsg
            receive_text = input.readLine();
            System.out.println(receive_text);
                }
        } catch(IOException e){}
        
    }
    
    
}

 

So I'm using data input and dataoutput streams, then remembering the format I'm using msg_type and msg which I call received_type and receive_text, first I check received_type, if it's not == 0 then print the type. If it's equal to 1 then read the next received buffer and print it.

 

My problem is receive_text = input.readLine();

.readline() is deprecated.

It's recommending I use BufferedStream or something like that. 

 

I believe I'm going at reading all wrong here. 

Programming is pretty hard, especially learning it. I'll have a look at related documentation regarding incoming/outgoing methods first, no need to respond with answers. My main problem right now is with using the right methods from libraries to send and receive data. 

Advertisement

I recommend you read this first instead of whatever tutorial you might find: https://docs.oracle.com/javase/tutorial/networking/sockets/

It's not game oriented, but it explains the workings of the standard Socket related classes. It's official stuff from the company that currently owns Java.

Second, either make up a coding standard of your own, or follow Java's. Don't do half your stuff in camelCase and the other with underscores. Any IDE will help you with this (ctrl+shift+f to format the code in Eclipse for instance).

Third,

catch (IOException ex) {}

Never do that unless you know exactly what you're doing. Your code could be failing in 15 different places and you wont know because you're eating up the errors without printing/signaling anything meaningful. You'll waste a lot of time finding those errors that way. Either re-throw the exceptions as 'new RuntimeException(ex)' so the program outright fails and stops (you'll know exactly when that way), or at least print the stack trace and continue.

Fourth, it is wise to avoid threads for now. You still have a lot in your plate, starting with client-server is hard enough already.

 

On 7/14/2017 at 9:30 AM, Xer0botXer0 said:

It's recommending I use BufferedStream or something like that. 

https://docs.oracle.com/javase/8/docs/api/java/io/DataInputStream.html#readLine--

The docs even tell you how to use BufferedStream. Become used to read the documentation. It's probably one of the big benefits of using Java. Easy docs available and IDE (Eclipse/Netbeans/IntelliJ) integrated, by just hovering over any standard method/class you should get a popup with the related javadoc snippet.

"I AM ZE EMPRAH OPENGL 3.3 THE CORE, I DEMAND FROM THEE ZE SHADERZ AND MATRIXEZ"

My journals: dustArtemis ECS framework and Making a Terrain Generator

Naturally I'd print the error message!

I'm not working on the server at the moment, Since I had a problem with BufferedStream I ended up getting some Java books to help me. I see in chapter four it covers BufferedStream how ever I'm only at chapter three now, and it's a huge chapter! I'm fine with that because since I started I've learnt about some fun and interesting concepts.

Eventually the plan is to get to java.io* stuff. So I'll post in this thread once I've gotten back to working on the server. 

Hi again!

 


while (online)
        {
        
            Socket new_Client = server.accept();
            connected_Count += 1;
            if (new_Client != null)
            {
                
                Player p = new Player();
                
                
                new_Client = null;
            }
            
        }
        

So I've got a question regarding this code, This is in my main method, Basically what my idea is to look for a new connecting socket and when one is found or when new_Client has a value then create an object of Player then reset new_Client so that the 'setup code' only happens once per new client.

 

The thing is I also have Socket new_Client at the start, so I'm wondering if Socket new_Client is going to create hundreds of new_Clients instead of use the same one ? because I'm declaring new_Client in the while statement, if so my work around is to declare new_Client before the while statement. 

 

Second thing is I'm getting a message "The value of the local variable connected_Count is not used"

I've declared int connected_Count = 0; at the start of the main method then within the code above you can see I'm incrementing it by one, I also added a println function for connected_Count and still getting that message ? 

accept() creates a new socket for every incoming connection it accepts. Obviously once you hit the bottom of the loop that socket goes out of scope, gets destroyed, the connection gets dropped. The same happens if you declare it outside the loop, because if you overwrite that value, the old socket gets destroyed, the connection gets dropped. You'd want to store multiple connections in some sort of data structure. Or maybe store them in the Player, and store the Players in some sort of data structure.

Your message about the value being unused - it's not saying the variable is unused - obviously you increment it - it's saying the value is unused. It's asking "why would a programmer bother incrementing a variable if they never read it or assign it to anything else?" Normally an unused variable means you did something wrong, or forgot to finish something. If you're sure it's not a mistake - because you intend to use it later - ignore it. (Also, don't increment that variable until you know that the socket isn't null.)

Advertisement

Hi guys,

 

so I'm trying to make sense of streams, i understand there's an input and an output stream and that these have purpose specific streams.

 

kR0Yf.jpg

 

From what  I read Byte Array streams are 8-bit, and then fileinputstream/outputstream are 16bit, does this mean that's the max size that each type can hold ? does someone have a table for this.

The problem is in GML (Game maker studios language) has buffers and reading a buffer is something like this

incoming_data = buffer_read(buffer, buffer_type); Where buffer type can be any of these

https://docs.yoyogames.com/source/dadiospice/002_reference/buffers/using buffers.html

buffer_u81
An unsigned, 8bit integer. This is a positive value from 0 to 255.
 

buffer_s81
A signed, 8bit integer. This can be a positive or negative value from -128 to 127 (0 is classed as positive).
 

buffer_u162
An unsigned, 16bit integer. This is a positive value from 0 - 65,535.
 

buffer_s162
A signed, 16bit integer. This can be a positive or negative value from -32,768 to 32,767 (0 is classed as positive).
 

buffer_u324
An unsigned, 32bit integer. This is a positive value from 0 to 4,294,967,295.
 

buffer_s324
A signed, 32bit integer. This can be a positive or negative value from -2,147,483,648 to 2,147,483,647 (0 is classed as positive).
 

buffer_f162
A 16bit floating point number. This can be a positive or negative value within the range of +/- 65504. (Not currently supported!)
 

buffer_f324
A 32bit floating point number. This can be a positive or negative value within the range of +/-16777216.
 

buffer_f648
A 64bit floating point number. This can be a positive or negative value from -(252) to 252 - 1.
 

buffer_bool1
A boolean value. Can only be either 1 or 0 (true or false)
 

buffer_stringN/A
This is a UTF-8 null terminated (0x00) string. Basically a GameMaker string is dumped in the buffer, and a 0 is put at the end.

 

I can't send incorrect types from the java server to the gml client, but I have the idea that Java has a universal stream that will read what I want to send from the server whether it be a string/u8/u16. These are the three I primarily use. Any advice on which stream type I should use for stuff like that ? 

 

And from there I'm not yet sure how sending works, im GML It would work something like this

GML Client:


msg_id = 32;//32 represents player is sending registration username/pass

buffer_seek(buffer, buffer_seek_start, 0); //buffer reference, base position to read/write from, offset from position +/-

buffer_write(buffer, buffer_u8,msg_id);

buffer_write(buffer,buffer_string,my_username);

buffer_write(buffer,buffer_string,my_pass);

network_send_packet(socket,buffer,buffer_tell(buffer)); //buffer tell looks at the size of the buffer and sends that too. I'm not sure how the receiving end handles that im going to check out the GMS forum but I'm guessing that information can be seen on java when reading and printing incoming information ?

GML Server:

Asynchronous Event:


///Handle clients


var client_sock = ds_map_find_value(async_load, "socket"); //incoming client socket
var net_type = ds_map_find_value(async_load, "type");

switch(net_type)
{
    
    
    case network_type_data: //work with data[Info]
    {
        //prepare incoming data to be read [Info]
        var client_id = ds_map_find_value(async_load, "id");
        var buff = ds_map_find_value(async_load,"buffer");
        buffer_seek(buff,buffer_seek_start,1);
        var msg_type = buffer_read(buff,buffer_u8);
        
        
        switch(msg_type)
        {
            case 10:                                                                //Client player is pending registration (usn/pass)
            {
                  reg_usn = buffer_read(buff, buffer_string);
                  reg_pass = buffer_read(buff, buffer_string);

 When ever the server detects incoming data it will show which client is sending the data via the first line where the client socket is stored in a ds_map called async load, from there - there are three states to read, Connecting clients, Disconnecting clients and Data flow, in this case it's the data flow one which has a constant called network_type_data. 

The main thing here is that the incoming buffer is stored in the ds_map "buffer", in the client side you can see I sent three values, a u8, a string and another string. in the server you can see I read the first one and stored it in msg_type, the second in reg_usn and third in reg_pass. so in GML the types had to match. I used the correct data types buffer_u8, buffer_string and buffer_string.

I know how to get the sockets with java of gml clients and vice versa, so i believe the next step is sending a message, but when the server responds or sends something to the client it needs to be the right type, so with the image I provided can someone explain which will allow me to use u8,u18 & Strings ? 

 

 

Okay according to these docs https://docs.oracle.com/javase/8/docs/api/java/io/DataOutputStream.html

there are various methods for sending different types of data, I think that answers my question, the same with DataInputStream.

I'm wondering about this

  1. do{//Start a do while loop while run is true
  2. Socket newConnection = server.accept();//Check if we have a connection, if we do continue otherwise try again
  3. String name = "guest"+ply;//Select an unique name for this player
  4. Player p = new Player(name, newConnection);//Create a new Player instance and set the socket to it
  5. System.out.println("New Player Connected from IP: "+newConnection.getInetAddress()+" and was assigned the name "+name);//Display the message that we got a new player
  6. ep.execute(p);//Assign the player instance a thread
  7. players.put(name, p);//Add the player instance to the player HashMap
  8. }while (run);

Within this do while loop line 2 looks for incoming connections right, what I'm wondering is if line 2 doesn't get a connection does line 3 not run ? I'd think the rest of the code within the do while loop would still run. But it makes sense that it shouldn't else errors would occur. 

 

The same thing is happening here

 

  1. public void run() {
  2. do{//Start a do while loop while alive is true
  3.  
  4. checkForMessages();//Check for messages
  5. }while(alive);
  6. try {
  7. socket.close();//Close and Free the socket from memory
  8. } catch (IOException e) {//If we get an exception we coudn't close the socket
  9. System.out.println("We got an error while closing a socket on player "+name+".");
  10. }
  11. }

The guy has a do while loop that runs a method while the alive bool is true, but he also has a try catch statement within the run method, the try catch statement tries to close the player socket, is the do while loop going to run until alive is false before the try catch statement will run ? 

You probably want to rephrase this, given the discussion in the other thread about what a byte is. As such I'll not address the low-level aspects of the questions above.

  • All your networked data will, normally, be sent as a byte stream. You might use a byte/u8 buffer or array to form the message before sending, and you might have a byte/u8 buffer or array to receive a message at the other end.
  • If you have to implement messages at this level, you'll need to have a way to know when you have successfully read all of a message. The simplest way to do this is to send the message length first, so that the receiver can get that and then knows how many more bytes it has to read before the message is fully received.

Regarding your loop questions:

  • In the first example: it's not possible for line 2 to not get a connection - that function doesn't return until it does get a connection. It could raise an exception, in which case the program will fall out of that whole while loop entirely.
  • In the second example: the while loop and the try block are completely unrelated. If the while loop completes normally, it will execute socket.close(), and maybe end up in the catch block.

Hi Kylotan, thanks for the answers I actually just came here to ask about knowing how much data is going to be received.

The other thing is to identify requests and data that comes with it from the client, say I want to tell the server hey the player wants to move, so I need to start with a unique ID, then the length of the following incoming data.

I'm going to give this a try and see what comes up. 

 

Also my idea behind this is to use a bufferedinputstream to get the socket inputstream, and then place the bufferedinputstream inside of a datainputstream, from what I remember low level stream means it works with bytes, high level works with data types, dis is high level, and bis is low level so that 'stack' should work. 

from there I just need to figure out what's being sent and so on. 

 

I was thinking as an alternative to sending the size, Instead I could send a unique Short value to say 'this is the end of this packet'. With a unique short value as the ID for the start of a packet.

I think that might work but I'm a bit worried that I might lose packets if I were to use udp instead of tcp, i've heard about rudp which is where the developer would make it reliable by doing something similar to tcp by checking to see if all packets were received. something I haven't wrapped my head around is that even the 'checks' could become lost packets. my guess is that the checks come first before unique IDs/lengths and so on, so if the check doesn't arrive then don't process the incoming information, at that point the server would have to tell the client hey send that again i didn't get that or rather if it does arrive the server tells the client it arrived, so if the client doesn't get that the packet you sent has arrived then resend it. but then it's a matter of how soon should it be resent. but ill get to that later lol 

 

So far I've only been able to receive bytes individually, I'd declare a byte bytes[]; array but the bytes won't concatenate. maybe using the dis will change that. It's kinda fun to see how these things work 

This topic is closed to new replies.

Advertisement