So, i started working on my multiplayer game today and i run into a serious problem with delay in my networking. When i test things on one machine using localhost, theres no noticable delay. But when i tried running client on my laptop and server on PC im experiencing about 2-3 sec delay.
Basically what im doing is:
Server:
Is running two threads, one that listens for packets on a port and when recieves a packet with input, he updates the gamestate accordingly. The second thread takes the gamestate from the first and every 10ms he sends it to the client.
Client:
Also two threads, one recieves the gamestate and the second sends packets with keyboard input every 10ms.
Im seding datagrampackets with bytearray which came from serialized class (Both have size about 100 bytes)
Send code:
ServerPacket testPacket = new ServerPacket(player.getX(),player.getY());
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutput out = null;
try
{
out = new ObjectOutputStream(bos);
out.writeObject(testPacket);
byte[] Bytes = bos.toByteArray();
DatagramPacket packet = new DatagramPacket(Bytes,Bytes.length,ip,port);
socket.send(packet);
//System.out.println("SERVER:SentUpdate");
}
catch (IOException ex)
{
System.out.println(ex.getMessage());
}
finally
{
try
{
if (out != null)
{
out.close();
}
}
catch (IOException ex)
{
System.out.println(ex.getMessage());
}
try
{
bos.close();
}
catch (IOException ex)
{
System.out.println(ex.getMessage());
}
}
Recieve code:
byte[] data = new byte[packetLength];
DatagramPacket packet = new DatagramPacket(data, data.length);
try
{
socket.receive(packet);
}
catch (IOException ex)
{
System.out.println(ex.getMessage());
}
ByteArrayInputStream bis = new ByteArrayInputStream(packet.getData());
ObjectInput in = null;
try
{
in = new ObjectInputStream(bis);
ServerPacket res = (ServerPacket)in.readObject();
return res;
}
catch (IOException ex)
{
System.out.println(ex.getMessage());
}
catch (ClassNotFoundException ex)
{
System.out.println(ex.getMessage());
}
finally
{
try
{
bis.close();
} catch (IOException ex)
{
System.out.println(ex.getMessage());
}
try
{
if (in != null)
{
in.close();
}
}
catch (IOException ex)
{
System.out.println(ex.getMessage());
}
}
Any ides why is it so slow? Or anything i should know about udp networking.
How are you achieving such high frequency with your two threads (10ms)? Are you sure they are running at such high frequency? Why use different threads for receiving and sending - that will take longer than sending after receiving on the same thread. Of course you have to accommodate for some latency over the Internet games often have to tolerate up to 200ms between each peer - in client server that could be up to 400ms.
Related
I have a thread which sends UDP packets at fixed intervals. After a while I am calling interrupt() from another thread and I am expecting the sender thread to completely finish after that. Most of the time, the sender thread does finish after receiving the interrupt. In some rare cases, however, the sender thread does not. Can you help me spot the mistake in my thread code?
try {
DatagramSocket socket = null;
Timber.d("Initialize the sender...");
while (!Thread.currentThread().isInterrupted()) {
try {
Timber.d("Sending UDP broadcasts...");
socket = new DatagramSocket();
while (!Thread.currentThread().isInterrupted()) {
String s = "hello";
byte[] buffer = s.getBytes();
DatagramPacket packet = new DatagramPacket(
buffer, buffer.length,
mBroadcastAddress, PORT);
try {
if (BuildConfig.DEBUG)
Timber.d("[" + new DateTime().toLocalTime() + "] " +
"Send UDP packet");
socket.send(packet);
} catch (IOException ioe) {
Timber.d(ioe, "IOException");
}
Thread.sleep(TIMEOUT_SLEEP);
}
} catch (SocketException se) {
Timber.d(se, "Socket exception");
break;
} finally {
if (socket != null)
socket.close();
socket = null;
}
}
} catch (InterruptedException ie) {
Timber.d("The sender thread received interrupt request");
}
Timber.d("Finish the sender...");
I think that the problem is here:
} catch (IOException ioe) {
Timber.d(ioe, "IOException");
}
The problem is that one of the subtypes of IOException is ... InterruptedIOException. And when that exception is thrown (in response to an interrupt), the thread's interrupted flag is cleared. Now it is "pretty unlikely" that this code is going to be interrupted in the middle of a send call. But if it does, then you will effectively "eat" an interrupt.
I think you should change the above to:
} catch (InterruptedIOException ioe) {
Thread.currentThread().interrupt();
} catch (IOException ioe) {
Timber.d(ioe, "IOException");
}
In addition, if are going to test the interrupt flag later on, you are also "eating" it when you catch InterruptedException at the end of the snippet, and you should set it again ... as above.
As I can see, in a certain portion of your code, you are swallowing the interruptedException.Restore the interrupt after catching interruptedException,do not swallow it.
This is how you restore the interrupt
Thread.currentThread().interrupt();
I have written a small Client/Server Program which already worked once but after adding Threads and some real input Data to it, i always get a closed Socket before being able to read the Object (the String). The Program always Prints "Client has already closed Connection!" from Function handleConnection in the ProcessDataThread.
ClientCode:
synchronized private static void sendToServer(){
Socket clientSocket = null;
BufferedOutputStream socketOut = null;
ObjectOutputStream out = null;
try{
String xmlToSend = "<startTag>\n<someOtherTag id=\"5555\">\n12345\n</someOtherTag>\n</startTag>\n";
Log.d(TAG, "Trying to send the following to the Server:" + xmlToSend);
//TODO load these from file
clientSocket = new Socket( "10.0.2.2", 7777);
socketOut = new BufferedOutputStream(clientSocket.getOutputStream());
out = new ObjectOutputStream(socketOut);
out.writeObject(xmlToSend);
out.flush();
}catch(Exception ex){
Log.e(TAG, "Could not write File to Server.", ex);
}
finally{
try{
if(clientSocket != null){
clientSocket.close();
}
if(out != null){
out.close();
}
}catch(IOException ex){
Log.e(TAG, "Could not close Socket.");
}
}
}
ServerCode:
ReceiverThread:
public void run()
{
try {
ServerSocket server = new ServerSocket(port);
//Only block for 10 Seconds and try again
server.setSoTimeout(10000);
while(!server.isClosed() && !stopped){
//Run
Socket client = null;
try
{
client = server.accept();
System.out.println("Accepted ClientConnection from " + client.getRemoteSocketAddress());
new ProcessDataThread(client).start();
}
catch( SocketTimeoutException tx){
//nothing
}
catch ( IOException e ) {
e.printStackTrace();
}
finally {
if ( client != null )
try { client.close(); } catch ( IOException e ) { e.printStackTrace(); }
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
ProcessDataThread:
public class ProcessDataThread extends Thread {
Socket client;
public ProcessDataThread(Socket sock) {
// xmlToProcess = xmlString;
this.client = sock;
}
private String handleConnection() {
BufferedInputStream socketIn = null;
ObjectInputStream in = null;
String xmlToProcess = null;
try {
if(!client.isClosed()){
System.out.println("Trying to read from Stream;");
socketIn = new BufferedInputStream(client.getInputStream());
in = new ObjectInputStream(socketIn);
Object xmlString = in.readObject();
System.out.println("Read some Object from Stream:" + xmlString.toString());
if (xmlString instanceof String) {
xmlToProcess = (String) xmlString;
System.out.println("Received the following XML:\n" + xmlToProcess);
}
}else{
System.out.println("Client has already closed Connection!");
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (EOFException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (socketIn != null) {
socketIn.close();
}
if(client != null){
client.close();
}
} catch (IOException ioex) {
ioex.printStackTrace();
}
}
return xmlToProcess;
}
#Override
public void run() {
String xmlToProcess = handleConnection();
if (xmlToProcess == null || xmlToProcess.isEmpty()) {
// Es konnte kein String vom Client gelesen werden.
return;
}
System.out.println(xmlToProcess);
}
}
I made some changes with jboi's Suggestions. This is what i got now. The error stays the same. I don't even get to reading the Stream in the Server because client.getClosed()
is always true!
In the Client Code:
clientSocket = new Socket( "10.0.2.2", 7777);
clientSocket.setTcpNoDelay(true);
socketOut = new BufferedOutputStream(clientSocket.getOutputStream());
out = new ObjectOutputStream(socketOut);
out.writeObject(xmlToSend);
out.flush();
socketOut.flush();
//Close Output on Socket to signalize the Server that we finished writing!
clientSocket.shutdownOutput();
in = clientSocket.getInputStream();
byte[] receivedData = new byte[8192];
while(in.read(receivedData) != -1) {
//Wait for the Server to Close the Connection
}
In the Server Code
socketIn = new BufferedInputStream(client.getInputStream());
in = new ObjectInputStream(socketIn);
Object xmlString = in.readObject();
System.out.println("Read some Object from Stream:" + xmlString.toString());
if (xmlString instanceof String) {
xmlToProcess = (String) xmlString;
System.out.println("Received the following XML:\n" + xmlToProcess);
}
out = client.getOutputStream();
out.write(1);
//Signalize the Client that we have read everything
client.shutdownOutput();
It is very probable that your client has closed the socket in the finally block before the server was able to read the data.
In your clients finally block you should use socket.shutdownOutput, then read on the client all incoming data till EOF and then close the socket.
On your server you read till EOF and then send an object as kind of acknowledge, e.g. Number of bytes in the message. You also end the send with socket.shutdownOutput() as you've done at the client. This puts again an EOF at the end of the data. This EOF is received by the client and it will finally close the socket.
The issue seems to be the client and server are unable to identify each others state:
Client sending data to server, where server has closed the connection
Server sending/reading data to client , where client has closed the connection
Either are unable to coordinate with each other, solutions could be to establish a proper state machine. Some examples in Google if you search for (client and server state machine) gives mathematically definite state machine examples for your application: hope this comment helps.
Hence it's not useful to look into this problem in solution perspective and probably start using protocols in place like : telnet etc .
Ok now i'm feeling stupid.
I closed the Socket inside the Server Code myself.
After accepting a connection the following is executed inside the finally Block:
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
The reason that there is this finally Block was because i didn't use Threads before so the ReceiverThread also did handle the Connection and therefore close the socket after using it.
I then moved the code to the new Thread and forgot to remove that finally block!
You can't use a buffered input stream and another kind of stream on the same socket. The buffered stream will steal data from the other one. Make up your mind. The ObjectInputStream will do everything you need. Just use that.
EDIT Re your edit, 'socket closed' means that you closed your socket and then continued to use it.
I've been sending packets to clients connected to the server fine, but trying to
send the same packet to the actual host itself keeps coming up with an error.
Here is the code that breaks
if(socket == null)
{
try
{
socket = new DatagramSocket( port );
}
catch (SocketException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if( server.returnPlayers() > 0)
{
byte[] buf = new byte[256];
DatagramPacket packet = new DatagramPacket( buf, buf.length );
try
{
socket.receive( packet ); ..<---this line
}
catch (IOException e)
{
Log.d(TAG, "Error with receiving data");
e.printStackTrace();
}
...
Any idea why? the same packet is sent to the clients, and it works 100%.
Canvas
Your try/catch is too local It is possible for 'socket' to be still null after the catch. Move the catch to the end of this code. Don't catch exceptions until after the code that can be affected by them.
I'm conducting an experiment to see how long it takes the TCP in java. First I start the server. Then call the function client_tcp many times, more than 50000 times. And measure the time it takes to connect, and send and receive 1 byte. When the server get more than 16384 requests (sometimes varies), the client can't connect to the server.
I don't know if it is because of the receive buffer size in the server socket. In my case, ss.getReceiveBufferSize() = 131072.
Here is the code:
public synchronized void server_tcp(int port) {
ServerSocket ss;
Socket so;
InputStream is;
OutputStream os;
try {
ss = new ServerSocket(port);
} catch (Exception e) {
System.out.println("Unable to connect to port " + port +
" TCP socket.");
return;
}
while (true) {
try {
so = ss.accept();
is = so.getInputStream();
os = so.getOutputStream();
int ch = is.read();
os.write(65);
so.close();
} catch (IOException e) {
System.out.println("Something went wrong.");
} catch (InterruptedException e) {
System.out.println("Bye.");
}
}
}
public void client_tcp(String host, int port) {
Socket so = null;
InputStream is = null;
OutputStream os = null;
try {
so = new Socket(host, port);
} catch (UnknownHostException e) {
System.err.println("Error Host not found.");
return;
} catch (IOException e) {
Syste.err.println("Error Creating socket.");
return;
}
try {
os = so.getOutputStream();
is = so.getInputStream();
os.write(65);
is.read();
os.close();
is.close();
so.close();
} catch (IOException e) {
System.err.println("Error.");
return;
}
}
What's wrong?
Thank you.
You are creating a massive number of sockets almost at once and the OS is not having time enough to release them. You could add a tiny delay (to be experimentally tuned) to the loop that invokes the client_tcp() method.
for(int i=0; i<50000; i++) {
new SocketReuse().client_tcp("127.0.0.1", 4444);
Thread.sleep(2); // 2 milliseconds delay
}
I have three classes, the client, the server and the handler (which is going to handle the server connections) as I show below:
// The Client
public void sendSomePackage() {
try {
socket = new Socket("localhost", 54321);
sos = socket.getOutputStream();
oos = new ObjectOutputStream(sockOutput);
} catch (IOException e) {
e.printStackTrace(System.err);
return;
}
// About to start reading/writing to/from socket
try {
Package package = new Package(100);
oos.writeObject(pacote);
} catch (IOException e) {
e.printStackTrace(System.err);
}
try {
Thread.sleep(50);
} catch (Exception e) {
e.printStackTrace();
}
// Done reading/writing to/from socket, closing socket.
try {
sock.close();
} catch (IOException e) {
System.err.println("Exception closing socket.");
e.printStackTrace(System.err);
}
//Exiting
}
Now the server class:
// The Server - with a method that just wait for connections
public void waitForConnections() {
while (true) {
try {
socket = serverSocket.accept();
// Server:Accepted new socket, creating new handler for it
SimpleHandler handler = new SimpleHandler(socket);
handler.start();
// Server:Finished with socket, waiting for next connection
}
catch (IOException e){
e.printStackTrace(System.err);
}
}
}
My handler, which just handle the server connections:
#Override
public void run() {
//Handler: Handler run() starting
while (true) {
try {
package = (Package) ois.readObject();
if (pacote != null) {
System.out.println("Package received " + pacote.getSourceid());
}
} catch (Exception e) {
e.printStackTrace(System.err);
break;
}
}
try {
// SimpleHandler:Closing socket
sock.close();
ois.close();
} catch (Exception e) {
// Handler: Exception while closing socket, e=" + e);
e.printStackTrace(System.err);
}
}
The idea is the client send some 'package' object to my server which is going to keep running receiving the 'package' object any time.
The connection works fine, but in the end of the program an exception is launched, this is the one:
Package received 100
java.io.EOFException
at java.io.ObjectInputStream$BlockDataInputStream.peekByte(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at br.ufscar.socket.SimpleHandler.run(SimpleHandler.java:45)
at java.lang.Thread.run(Unknown Source)
I already search for something on Google but nothing so far.
Any idea ?
This is working exactly as you want it to (probably). It reads the 100 then goes through the loop again (while(true) never stops looping until a break statement) and throws an exception because no more data has been sent and it goes to the catch statement and prints the error before exiting your while loop.
EOFException ist an IOException that indicates the end of an stream.
Here we say that if there aren't any more bytes to read then we should break out of the while loop before trying to read the object, etc.
while (true) {
if (ois.read() == -1) break;
//...rest of the code
}
Ok, this is how object streams work and the solution that works everywhere.
Object stream data is preceded by a 4 byte 'magical' sequence AC ED 00 05. An ObjectInputStream will peek for this data at construction time rather than before the first read. And that's logical: one wants to be sure it is a proper stream before being too far in an application. The sequence is buffered by the ObjectOutputStream at construction time so that it is pushed on the stream at the first write.
This method gives rise to complexities in buffered situations or transferring via sockets.
Fortunately there is a just as simple as effective solution to all these problems:
Flush the ObjectOutputStream immediately after construction!
ObjectOutputStream myStream = new ObjectOutputStream ( anotherStream );
myStream.flush();