Skip to main content
gamedev.net
🔒 Locked

Java Server Q&A

Started by Xer0botXer0 27 replies 8.1k views
Original Post
Xer0botXer0
Xer0botXer0

Hi guys,

So I made this thread to deal with my server problems!

First thing's first, in this guy's server code he has the following


  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);


At line 2 he commented, check if we have a connection and if we do continue otherwise try again..

I don't see an if statement, so how is he preventing line 3 and up from running if there isn't a connection ?

When I look up the .accept method it says this

  • Listens for a connection to be made to this socket and accepts it. The method blocks until a connection is made.
when it says block does that mean the code wont read past line 2 until a connection is made ?
Avalander
Avalander

That's hard to say without knowing what type the server object is. My best two guesses are either

  1. The accept() method keeps checking for a connection and returns when it gets it. (You can probably check that easily looking at the method implementation)
  2. The comment is wrong.
GibbonThatCodes
GibbonThatCodes

if its using a ServerSocket in Java, the .accept() method blocks until it receives a connection, so until it actually has a connection it will just block further execution of code.. or thats how it should work.. so doesnt need an if statement

Xer0botXer0
Xer0botXer0

Thanks, yes it is using ServerSocket, that makes sense..

I'm not using threads at the moment, because I don't want to complicate the code too soon, first getting a feel for how running a server works.

At line 4 I'm creating an instance of the class Player, what I'm wondering is at what point does the code within that instance run ? will the new Player instance's code run before line 5 runs ?

GibbonThatCodes
GibbonThatCodes

Well line 4 creates a new Player and passes in two parameters, so in the Player class will obviously be a matching constructor, anything in that constructor will happen before line 5 happens yes.

Xer0botXer0
Xer0botXer0

So the constructor for the new player object is run immediately before line 5, but what about the rest of the code in the player object ? is that run immediately or will it run after all the code from the main method first ?


script 1:

lots of code, also creates a new object player with more code that follows


script 2:

player object has code and runs..


does script 1 finish first before script 2 from player object is run ? or does script 1 run then create object player then run script 2s code and once finished it continues with script 1.


In both cases it's been determined that the constructor for object player will run

GibbonThatCodes
GibbonThatCodes

What code is inside the Player class exactly?

When you create a new player, its constructor will get called, but other than that no code will be executed it until that code is called.

Since the while loop on the server doesnt seem to be call any code from the Player in your example then no code inside of that class other than the constructor should fire.

Xer0botXer0
Xer0botXer0

So no methods within the player class will run unless I call those methods from another class ? or call them in the constructor ? That kind of makes sense. Ill have to see where the player class is being used.


Server Class:


package mypackage;

//Import area
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Server {

    private static ServerSocket server; // Define the server socket
    private static boolean run = true; //Continuously run server loop Switch
    private static HashMap<String, Player> players = new HashMap <String, Player>();// create an array list to store all players
    
    //use threads
    public static int player_count = 0; //holds player count
    
    public static void main(String[] args){
    
        try {
            System.out.println("Starting Server...");
            server = new ServerSocket(1234,10); //New server socket object 
            System.out.println("Server started on port: " + Integer.toString(server.getLocalPort()));
            
            do{
                Socket newConnection = server.accept();
                String name = "Guest"+player_count;//Gives each new player a unique name(change later)
                Player p = new Player(name,newConnection); //Create a new Player instance and set the socket to it
                System.out.println("New player connected: " + name);
                players.put(name,p); //add the new player instance to the player HashMap
                
            }while (run);
            
        } catch (IOException e) {}
        
        
    
    }
    
    
}



Player class:


package mypackage;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketTimeoutException;

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(30000);//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
            
        } catch(IOException e){}
    }

    
    
    public void run()
    {
        do
        { //start a do loop while alive is true
            checkForMessages();//check for messages
        }
        while (alive);
    }        
        
    
    private void checkForMessages()
    {
        byte received;
        
        try
        {
            received = input.readByte();//read the header from the message
        }catch(IOException ex){alive = false; return;}
        
        
        if (received == -34) 
        { //header start ?
            
            try {
                    input.skipBytes(11); //ignore the header
                    received = input.readByte(); //message ID
                    switch (received)
                    {
                        case 0:
                        break;
                    }
                }catch (SocketTimeoutException ex)
            
            {
                    
                    //if we get a socket timoue display it and free the player's instance
                    System.out.println("Disconnected "+name+ "due to timeout.");
                    alive =false;
            }catch (IOException ex){
                ex.printStackTrace();
            }
        }
    }
    

}



This is all the server code, Is the run method being called anywhere ? I don't see it being called.. from my understanding or from what you've said the methods must be called somewhere before it is run.

GibbonThatCodes
GibbonThatCodes

Well, from what I can see it doesnt look like the player run code is ever being called, so it looks like the program gets a Player, add it to a list and thats about it, though if you want to double check if it ever gets called you can just put a break point on the run() function in the player class. Not sure what IDE youre using though you should be able to just stick break points in.

Xer0botXer0
Xer0botXer0

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.

Xer0botXer0
Xer0botXer0

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.

TheChubu
TheChubu

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 
Xer0botXer0
Xer0botXer0

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.

Xer0botXer0
Xer0botXer0

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 ?

Kylotan
Kylotan

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.)

Xer0botXer0
Xer0botXer0

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 ?



Xer0botXer0
Xer0botXer0

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. checkForMessages();//Check for messages
  4. }while(alive);
  5. try {
  6. socket.close();//Close and Free the socket from memory
  7. } catch (IOException e) {//If we get an exception we coudn't close the socket
  8. System.out.println("We got an error while closing a socket on player "+name+".");
  9. }
  10. }

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 ?

Kylotan
Kylotan

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.
Xer0botXer0
Xer0botXer0

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

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.