Lag Over Sockets java - java

I am making a multi-player snake game in java using sockets. All the transmission is done through a server to all the connected clients. The code for the same is yet not completely finished but it does the basic job of moving the snakes around and increasing scores if a particular client eats its food.
I generate random numbers for food coordinates from the server side and relay it to all the clients. If a client presses a key the requested movement is calculated and the direction of movement is sent to the server which then relays the movement to ALL clients (including the one who sent it) and only on receipt of the movement info do the clients make changes to the snake which moved. So every movement is tracked over the network and no movement decision is made by the client itself until it receives that, say client 'player1' has asked to move.
The problem I am facing is that even with two players there seems to be a difference in coordinates after moving about the snakes a little.
What possible remedies could I apply to my code so as to remove this apparent lag between the position of snakes?
This is the client code:
package mycode;
import java.awt.Point;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
import java.util.Map;
import javax.swing.JOptionPane;
public class ConnectionManager implements Runnable {
Socket socket;
boolean start = false;
DataInputStream in;
DataOutputStream out;
Map<String, Snake> map;
ConnectionManager(String name, String IP, Map<String, Snake> m) {
this.map = m;
try {
socket = new Socket(IP, 9977);
in = new DataInputStream(new BufferedInputStream(
socket.getInputStream()));
out = new DataOutputStream(new BufferedOutputStream(
socket.getOutputStream()));
out.writeUTF(name);
out.flush();
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(null, "Could Not Find Server",
"ERROR", JOptionPane.ERROR_MESSAGE);
System.exit(0);
}
}
void populateMap() {
try {
String name = in.readUTF();
System.out.println("Name received: " + name);
if (name.equals("start_game_9977")) {
start = true;
System.out.println("Game Started");
return;
} else if (name.equals("food_coord")) {
Game.foodx = in.readInt();
Game.foody = in.readInt();
return;
}
map.put(name, new Snake(5));
} catch (Exception e) {
e.printStackTrace();
}
}
boolean start() {
return start;
}
void increaseSnakeLength(String thisname){
Snake temp = map.get(thisname);
Point temp1=new Point(0,0);
temp.length++;
switch (temp.move) {
case DOWN:
temp1= new Point(temp.p[temp.length - 2].x,
temp.p[temp.length - 2].y+6);
break;
case LEFT:
temp1= new Point(temp.p[temp.length - 2].x-6,
temp.p[temp.length - 2].y);
break;
case RIGHT:
temp1= new Point(temp.p[temp.length - 2].x+6,
temp.p[temp.length - 2].y);
break;
case UP:
temp1= new Point(temp.p[temp.length - 2].x,
temp.p[temp.length - 2].y-6);
break;
default:
break;
}
if(temp1.y>Game.max)
temp1.y=Game.min;
if(temp1.x>Game.max)
temp1.x=Game.min;
if(temp1.y<Game.min)
temp1.y=Game.max;
if(temp1.x<Game.min)
temp1.x=Game.max;
temp.p[temp.length-1]=temp1;
}
void readMotion() {
try {
while (true) {
if (Game.changedirection) {
String mov = "";
mov = Game.move.name();
// System.out.println(Game.move);
out.writeUTF(mov);
out.flush();
Game.changedirection = false;
}
if (Game.foodeaten) {
out.writeUTF("food_eaten");
out.flush();
Game.foodeaten = false;
}
Thread.sleep(50);
}
} catch (Exception e) {
e.printStackTrace();
}
}
void otherRunMethod() {
try {
while (true) {
String mname = in.readUTF();
String mov = in.readUTF();
if (mov.equals("Resigned")) {
map.remove(mname);
} else if (mov.length() >= 10) {
if (mov.substring(0, 10).equals("food_eaten")) {
String[] s = mov.split(",");
Game.foodx = Integer.parseInt(s[1]);
Game.foody = Integer.parseInt(s[2]);
int score = ++map.get(mname).score;
increaseSnakeLength(mname);
System.out.println(mname + ":" + score+" Length:"+map.get(mname).length);
}
} else {
Game.move = Direction.valueOf(mov);
map.get(mname).move = Game.move;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void run() {
while (true) {
if (!start) {
populateMap();
} else if (start) {
new Thread(new Runnable() {
public void run() {
otherRunMethod();
}
}).start();
readMotion();
break;
}
try {
Thread.sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
The code is pretty long so I am just putting up the server side of code that manages connections.
package mycode;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.Socket;
import java.util.Map;
public class Playerhandler implements Runnable {
Socket player;
String thisname;
Map<String, Socket> map;
DataInputStream in = null;
DataOutputStream out = null;
ObjectInputStream ob;
Snake snake;
Playerhandler(Socket player, Map<String, Socket> m) {
this.player = player;
this.map = m;
try {
in = new DataInputStream(new BufferedInputStream(
player.getInputStream()));
thisname = in.readUTF();
map.put(thisname, this.player);
populatePlayers();
System.out.println("Connected Client " + thisname);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
void populatePlayers() {
try {
out = new DataOutputStream(new BufferedOutputStream(
player.getOutputStream()));
for (String name : map.keySet()) {
out.writeUTF(name);
out.flush();
}
for (String name : map.keySet()) {
out = new DataOutputStream(new BufferedOutputStream(map.get(
name).getOutputStream()));
out.writeUTF(thisname);
out.flush();
}
} catch (Exception e) {
e.printStackTrace();
}
}
void relay(String move) {
try {
if (move.equals("food_eaten")) {
move = move + ","
+ (Snakeserver.randomGenerator.nextInt(100) * 6) + ","
+ (Snakeserver.randomGenerator.nextInt(100) * 6);
}
for (String name : map.keySet()) {
out = new DataOutputStream(new BufferedOutputStream(map.get(
name).getOutputStream()));
out.writeUTF(thisname);
out.flush();
out.writeUTF(move);
// System.out.println(Direction.valueOf(move));
out.flush();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void run() {
while (true) {
try {
relay(in.readUTF());
} catch (IOException e) {
// TODO Auto-generated catch block
System.out.println("Player " + thisname + " Resigned");
map.remove(thisname);
relay("Resigned");
return;
}
}
}
}

This answer is to recap the dialog to arrive at a solution as well as to point some additional areas to research or try out.
The main software behavior problem was that having multiple clients resulted in the various clients showing different snake positions after several moves.
After a number of questions and responses through the comments, the poster of the question modified their software so that all of the clients are synchronized by the server sending out the objects of all of the snakes to all of the clients so that all clients are now using the same snake object. Previously each client was maintaining its own snake object data and just receiving changes or deltas in snake data. With this change, all of the clients are now synchronized through the snake object transmitted by the server however there is still a problem with clients showing slightly different positions which is corrected after a moment or two, as each client receives an update on all of the snakes, the clients become synchronized again.
The next step is to look at a different approach so that the clients will remain synchronized more closely using UDP/IP as the network transmission protocol rather than the currently used TCP/IP. The expected results of using UDP/IP is to reduce the various lags introduced by the TCP network transmission protocol in order to provide the connection oriented, sequenced byte stream provided by TCP. However using the UDP network transmission protocol requires that some of the delivery mechanisms used by TCP in order to provide the dependable sequence of bytes must be assumed by the user of UDP.
Some of the issues with UDP are: (1) packets may not be received in the same sequence in which they are sent, (2) packets may be dropped or lost so that some packets sent may not be received, and (3) data sent using UDP must be explicitly put into packets for transmission so that the sender and the receiver see packets rather than a stream of bytes.
The basic architecture for this snake game would look something like the following.
Clients would send a snake update to the server. This interaction would require an acknowledgement sent by the server back to the client. If the client does not receive such an acknowledgement, the client would resend the snake update after some time period.
The server would then update its data to reflect the change and using its list of clients, send the same data packet to all clients. Each client receiving the packet would send an acknowledgement. By sending the acknowledgement, each client notifies the server that they are still in the game. If the server is no longer receiving client acknowledgements, it will know that a client has possibly left the game or there is some kind of network problem.
Each packet would have a sequence number which is incremented after sending the packet. This sequence number gives a unique identifier so that clients and server can detect if packets have been missed or if a packet received is a duplicate of an already received packet.
With UDP it is best if packets are as small as possible. UDP packets that are larger than what can be sent in the underlying IP network protocol will be split up into multiple IP packets with the multiple IP packets sent one at a time and then reassembled into the UDP packet at the receiving network node.
Here are some resources on UDP network protocol using the Java programming language.
Lesson: All about datagrams.
A simple Java UDP server and UDP client.
Stackoverflow: Send and receive serialize object on UDP in java.
Java-Gaming.org UDP vs TCP.
Gaffer On Games: What every programmer needs to know about game networking.
Gaffer On Games: Reliability and Flow control.
Stackoverflow: What are possible ways to send Game/Simulation state with javaNIO?

I've never implemented a network multiplayer game before, but I think the most widely used 'solution' here is to cheat.
I think its referred to as 'dead reckoning', although snake works exactly like this anyway.
http://www.gamasutra.com/view/feature/3230/dead_reckoning_latency_hiding_for_.php
Basically you decouple the game loop from network updates. Have each client keep its own state, and simply predict where the opponents are going to be at each frame. Then when updates from the server arrive you can adjust opponents to their true location. To hide this discrepancy, I think its common to render the state of the game as it was a few milliseconds ago, rather than the current state. That way the network updates have a more realistic chance of catching up with the game loop, so it will seem less choppy.
As I said though, I've never actually implemented this myself so YMMV. This is one of the harder problems in game development.

I would be inclined to add an explicit call of setTcpNoDelay(true). This will make sure that http://en.wikipedia.org/wiki/Nagle%27s_algorithm is turned off and so disable an optimization that increases efficiency at what is usually a small amount of increased delay.

Related

While loop in java thread doesn't run

I am making a Minecraft mod in which I want to have a socket server running in the background waiting for a message from the python client.
The server is running in a thread.
Here is the code in which I start the thread:
Thread t = new Thread(new Runnable() {
#Override
public void run() {
try {
InterPythonJavaCommunicatorServer.begin(socket, sockIn, sockOut);
}catch (IOException e){
System.out.println("Woah! Somethings gone wrong! ringing a alarm now!");
}
}
});
t.start();
t.join();
And the entire Server class
package com.satyamedh.minecraft_ai_helper.communicator;
import com.satyamedh.minecraft_ai_helper.Movement;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.net.Socket;
public class InterPythonJavaCommunicatorServer{
public static void begin(Socket socket, BufferedReader sockIn, BufferedWriter sockOut) throws IOException {
boolean done = false;
while (true) {
System.out.println("Test!");
boolean ready = sockIn.ready();
if(!ready) {
return;
}
try {
String response = sockIn.readLine(); // Stops Here!
System.out.println(response);
//if (response == null) {
//System.out.println("Remote process closed the connection.");
//done=true;
//}
if (response.equals("forward\n")){
boolean o = Movement.forward(1);
if (o){
sockOut.write("done");
sockOut.flush();
}
}
} catch (IOException e) {
System.out.println("Welp! ALARM BOI!");
}
}
}
}
I have breakpoined the entire code and it seems to stop on String response = sockIn.readLine();. And the Test! message only runs once.
Ps: I have googled for hours searching why! cant seem to find anything related!
I know it might be very stupid and easiely caught out :)
Edit: As #DevParzival Said I tried using t.join(); but it still does the same! I have edited the above code to match my current one.
Edit2: here is the client code(python)
import socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(("", 1243))
server_socket.listen(5)
print(
"TCPServer Waiting for client on port 1243")
while 1:
client_socket, address = server_socket.accept()
print(
"I got a connection from ", address)
while 1:
data = input("SEND( TYPE q or Q to Quit):")
if data == 'Q' or data == 'q':
client_socket.send(bytes(data, encoding='utf8'))
client_socket.close()
break
else:
client_socket.send(bytes(data, encoding='utf8'))
data = client_socket.recv(512)
if data == 'q' or data == 'Q':
client_socket.close()
break
else:
print("RECEIVED:", data)
Thx in advance
Try to send NewLine | CHR(10) | \n character at the end of python packet payload, if you use socketIn.readLine() in a receiving side it waits for a line delimiter before return.
If you want to study Java NonBlocking NIO socket you may study my example program here, please note NIO socket need quite a different approach
https://stackoverflow.com/a/26312841/185565

Blocking a non-blocking socket client connection

First, I'm not a developer (and I've been coding only for 2 weeks), so feel free to tell me I'm completely misunderstanding the thing (also, I wrote all of this for myself, so I'm sure it's super not cool) :). I want to learn and get it right, so I'm keen to listen to suggestions or complete rewrites.
I want to connect to a socket in non-blocking mode (I'm the client, not the server). I'll mainly need to read from it, but sometimes I'll need to write to it, too. The procedure is as follows:
Connect to socket
Send some initial requests to login to the server
Read from the socket
Sometimes, write some stuff (subscribe to certain information, for example)
My solution is as follows (I'm writing it in Java, because I've read it's a fast and good programming language, but I'm happy to change if required... hopefully not needed though!):
public class SocketClient {
public static void main(String[] args) {
new Feed().init();
}
private boolean isSocketConnected() {
return socket != null && socket.isConnected();
}
public void init() {
try {
if (isSocketConnected()) {
// What here if I'm in non-blocking mode?
// Would be good to know if the "close API" request succeeded
// otherwise next time I won't be able to connect to their socket...
sendCloseRequestToApi();
socket.close();
}
run();
} catch (Exception e) {
if (isSocketConnected()) {
// Same question as above...
sendCloseRequestsToApi();
socket.close();
}
}
}
public void run() throws IOException {
System.out.println("Starting connection in blocking mode...");
SocketChannel channel = SocketChannel.open();
socket = channel.socket();
socket.setReceiveBufferSize(RECEIVE_BUFFER_SIZE);
socket.setSendBufferSize(SEND_BUFFER_SIZE);
channel.connect(new InetSocketAddress("127.0.0.1", 2121));
channel.finishConnect();
System.out.println("Finished connecting in blocking mode");
// Writes to the socket (user and password)
initialiseTheApi();
System.out.println("Sent API requests in blocking mode");
System.out.println("Now we should probably go non-blocking (I guess)");
channel.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ);
selector = Selector.open();
channel.configureBlocking(false);
System.out.println("Selector created and switched to non-blocking mode...");
long timeWithoutData = 0;
boolean needsReconnection = false;
while (!needsReconnection) {
selector.select();
Iterator < SelectionKey > keys = selector.selectedKeys().iterator();
while (keys.hasNext()) {
SelectionKey key = keys.next();
keys.remove();
if (!key.isValid()) {
continue;
}
if (key.isWritable()) {
// Execute write...
// What if I need to know the result to the write operation?
}
if (key.isReadable()) {
int dataRead = readDataFromSocket(buffer);
buffer.flip();
if (buffer.remaining() > 0) {
// I process the data read here,
// but sometimes the data sent is
// "reconnect to API". So I need to close
// the connection and start again.
// How can I do that if I'm in non-blocking mode?
// I mean, I need to make sure when I send that request
// (for reconnection).
// I need to know that the request got to the server and
// was processed OK before moving on and
// reading/writing again...
}
if (dataRead > -1) {
timeWithoutData = 0;
} else {
if (timeWithoutData > 0) {
long diffInMillis = System.currentTimeMillis() - timeWithoutData;
if (diffInMillis > 2000) {
System.out.println("Timeout or something? I need to reconnect I think");
needsReconnection = true;
}
} else {
timeWithoutData = System.currentTimeMillis();
}
}
// Do I even need this? Already did it before, right?
key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
}
}
}
if (needsReconnection) {
// We need full reconnection, go back up and reconnect
init();
}
}
}
I removed imports and other non-useful methods for convenience, and to keep the post short.
As you can see in my questions in the code (plus some added ones):
Reconnection: If I'm in non-blocking mode, how do I know that my request got sent successfully to the server
If I read from the socket and the message is "Reconnect to API", how can I make sure that happens before any other read / write?
Do I need to send the interestedOps over and over again?
I should only connect once to the socket. The fact that I'm non-blocking doesn't change that, right?
I've seen this could all be simplified using Netty or something, but I'm already bloated with so much stuff! :(
I hope my questions are clear. Let me know otherwise, please.
Thanks a lot.
I was trying to do something that just didn't make sense. In my case I can definitely use a blocking connection, which I just didn't know about :/. Internet is a bad source of information sometimes! I kept reading over here not to use a blocking connection :D. But now it makes perfect sense the different scenarios. – Will

Java socket data works only when sending time is delayed

I am sending a protobuf from C++ to Java via a raw socket, the C++ program being the client and the java program being the server. The C++ program generates packets almost every 1ms which is sent to the java program.
If I run the program normally, I see that there are only the half the packets being received.
If I set a breakpoint in the C++ program and then run the client and the server, all the packets are received.
How do I ensure that all packets are received without setting a breakpoint? Can I introduce a delay?
All the packets have bytes sizes upto a maximum of 15 bytes.
By default TCP sockets use the "Nagle Algorithm" which will delay transmission of the next "unfilled" fragment in order to reduce congestion. Your packet size is small enough and the time delay between packets is small enough that the nagle algorithm will have an effect on your transmissions.
As already discussed in the comments, what you are trying to do won't work in a reliable way. This is also described in the Protobuf documentation:
If you want to write multiple messages to a single file or stream, it
is up to you to keep track of where one message ends and the next
begins. The Protocol Buffer wire format is not self-delimiting, so
protocol buffer parsers cannot determine where a message ends on their
own. The easiest way to solve this problem is to write the size of
each message before you write the message itself. When you read the
messages back in, you read the size, then read the bytes into a
separate buffer, then parse from that buffer. (If you want to avoid
copying bytes to a separate buffer, check out the CodedInputStream
class (in both C++ and Java) which can be told to limit reads to a
certain number of bytes.)
The bold italic part is where you code isn't correct.
On the write side you should write
the Protobuf's length in some format that is understandable for both sender and receiver (selecting the proper format is especially important when transporting between systems whose endianness is different).
the protobuf
On the receiving end you need to
perform a read with the fixed, known size of the length field
a read for the length learned in step 1. This read will retriev the protobuf.
There's example code here on SO in this question: Sending struct via Socket using JAVA and C++
#fvu: This is my code which I am trying:
import Visualization.DataSetProtos.PacketData; // protos import
import java.io.InputStream;
import java.util.Arrays;
import javax.swing.JFrame;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
class WorkerThread extends Thread {
Socket service;
static DynamicData demo;
static int size;
static int times;
static byte[] buffer;
WorkerThread(Socket service)
{
this.service = service;
buffer = new byte[500];
size = 1;
times = 0;
}
static void Print(PacketData packetData)
{
System.out.print("Packet Number: " + (++times));
System.out.print(" DataSet Size: " + packetData.getLength() + "\n");
}
static void Print(PacketHeader packetHeader)
{
System.out.print("Packet Number: " + (++times));
System.out.print(" DataSet Size: " + packetHeader.getLength() + "\n");
}
public void run() {
boolean flag=true; //you can change this flag's condition, to test if the client disconects
if(demo == null)
{
demo = new DynamicData("GridMate Data Visualization");
demo.pack();
RefineryUtilities.centerFrameOnScreen(demo);
//demo.setVisible(true);
}
try
{
while (flag)
{
InputStream inputStream = service.getInputStream();
int read;
read = inputStream.read(buffer);
byte[] readBuffer = new byte[read];
readBuffer = Arrays.copyOfRange(buffer, 0, read);
PacketData packetData = PacketData.parseFrom(readBuffer);
Print(packetData);
}
service.close();
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
public class Test
{
Test()
{
server = null;
client= null;
}
public static void main(final String[] args) {
int i =0;
try
{
server = new ServerSocket(25715);
System.out.println("Server setup and waiting for client connection ...");
while(true)
{
client = server.accept();
WorkerThread wt = new WorkerThread(client);
wt.start();
i++;
}
}
catch(IOException e)
{ System.out.println("IO Error in streams " + e);
e.printStackTrace();
}
}
public void finalize()
{
try
{
server.close();
client.close();
}
catch(Exception e)
{
e.printStackTrace();
}
}
static ServerSocket server;
static Socket client;
}

Permanent and persistent Socket connection in java

I've created a client-server connection, something like a chat system. Previously I was using a while loop on the client side, and it was waiting to read a message from the console every time (of course server has a while loop as well to serve forever). But now, I'm trying to first create a connection at the beginning of the session, and then occasionally send a message during the session, so to maintain a permanent and persistent connection.
Currently, without the while loop, the client closes the connection and I don't know how to find a workaround.
Here is the client code:
import java.net.*;
import java.io.*;
public class ControlClientTest {
private Socket socket = null;
// private BufferedReader console = null;
private DataOutputStream streamOut = null;
public static void main(String args[]) throws InterruptedException {
ControlClientTest client = null;
String IP="127.0.0.1";
client = new ControlClientTest(IP, 5555);
}
public ControlClientTest(String serverName, int serverPort) throws InterruptedException {
System.out.println("Establishing connection. Please wait ...");
try {
socket = new Socket(serverName, serverPort);
System.out.println("Connected: " + socket);
start();
} catch (UnknownHostException uhe) {
System.out.println("Host unknown: " + uhe.getMessage());
} catch (IOException ioe) {
System.out.println("Unexpected exception: " + ioe.getMessage());
}
String line = "";
// while (!line.equals(".bye")) {
try {
Thread.sleep(1000);
//TODO get data from input
// line = console.readLine();
line="1";
if(line.equals("1"))
line="1,123";
streamOut.writeUTF(line);
streamOut.flush();
} catch (IOException ioe) {
System.out.println("Sending error: " + ioe.getMessage());
}
// }
}
public void start() throws IOException {
// console = new BufferedReader(new InputStreamReader(System.in));
streamOut = new DataOutputStream(socket.getOutputStream());
}
}
And here is the Server code:
import java.awt.*;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class ControlServer {
private Socket socket = null;
private ServerSocket server = null;
private DataInputStream streamIn = null;
public static void main(String args[]) {
ControlServer server = null;
server = new ControlServer(5555);
}
public ControlServer(int port) {
try {
System.out
.println("Binding to port " + port + ", please wait ...");
server = new ServerSocket(port);
System.out.println("Server started: " + server);
System.out.println("Waiting for a client ...");
socket = server.accept();
System.out.println("Client accepted: " + socket);
open();
boolean done = false;
while (!done) {
try {
String line = streamIn.readUTF();
// TODO get the data and do something
System.out.println(line);
done = line.equals(".bye");
} catch (IOException ioe) {
done = true;
}
}
close();
} catch (IOException ioe) {
System.out.println(ioe);
}
}
public void open() throws IOException {
streamIn = new DataInputStream(new BufferedInputStream(
socket.getInputStream()));
}
public void close() throws IOException {
if (socket != null)
socket.close();
if (streamIn != null)
streamIn.close();
}
}
I would like to summarize some good practices regarding the stability of TCP/IP connections which I apply on a daily basis.
Good practice 1 : Built-in Keep-Alive
socket.setKeepAlive(true);
It automatically sends a signal after a period of inactivity and checks for a reply. The keep-alive interval is operating system dependent though, and has some shortcomings. But all by all, it could improve the stability of your connection.
Good practice 2 : SoTimeout
Whenver you perform a read (or readUTF in your case), your thread will actually block forever. In my experience this is bad practice for the following reasons: It's difficult to close your application. Just calling socket.close() is dirty.
A clean solution, is a simple read time-out (e.g. 200ms). You can do this with the setSoTimeoutmethod. When the read() method timeouts it will throw a SocketTimeoutException. (which is a subclass of IOException).
socket.setSoTimeout(timeoutInterval);
Here is an example to implement the loop. Please note the shutdown condition. Just set it to true, and your thread will die peacefully.
while (!shutdown)
{
try
{
// some method that calls your read and parses the message.
code = readData();
if (code == null) continue;
}
catch (SocketTimeoutException ste)
{
// A SocketTimeoutExc. is a simple read timeout, just ignore it.
// other IOExceptions will not be stopped here.
}
}
Good practice 3 : Tcp No-Delay
Use the following setting when you are often interfacing small commands that need to be handled quickly.
try
{
socket.setTcpNoDelay(true);
}
catch (SocketException e)
{
}
Good practice 4 : A heartbeat
Actually there are a lot of side scenario's that are not covered yet.
One of them for example are server applications that are designed to only communicate with 1 client at a time. Sometimes they accept connections and even accept messages, but never reply to them.
Another one: sometimes when you lose your connection it actually can take a long time before your OS notices this. Possibly due to the shortcomings described in good practice 3, but also in more complex network situations (e.g. using RS232-To-Ethernet converters, VMware servers, etc) this happens often.
The solution here is to create a thread that sends a message every x seconds and then waits for a reply. (e.g. every 15 seconds). For this you need to create a second thread that just sends a message every 15 seconds. Secondly, you need to expand the code of good practice 2 a little bit.
try
{
code = readData();
if (code == null) continue;
lastRead = System.currentTimeMillis();
// whenever you receive the heart beat reply, just ignore it.
if (MSG_HEARTBEAT.equals(code)) continue;
// todo: handle other messages
}
catch (SocketTimeoutException ste)
{
// in a typical situation the soTimeout is about 200ms
// the heartbeat interval is usually a couple of seconds.
// and the heartbeat timeout interval a couple of seconds more.
if ((heartbeatTimeoutInterval > 0) &&
((System.currentTimeMillis() - lastRead) > heartbeatTimeoutInterval))
{
// no reply to heartbeat received.
// end the loop and perform a reconnect.
break;
}
}
You need to decide if your client or server should send the message. That decision is not so important. But e.g. if your client sends the message, then your client will need an additional thread to send the message. Your server should send a reply when it receives the message. When your client receives the answer, it should just continue (i.e. see code above). And both parties should check: "how long has it been?" in a very similar way.
You could wrap a thread around the connection and have it periodically send a status to keep the line open, say every 30 seconds or whatever. Then, when it actually has data to send it would reset the keep alive to be 30 seconds after the last transmission. The status could be helpful to see if the client is still alive anyway, so at least it can be a useful ping.
Also, you should change your server code, you appear to only handle one connection at the moment. You should loop and when a socket connection comes in spawn a thread to handle the client request and go back to listening. I may be reading to much into what may just be your test code, though.
Make the client socket connection wrapped around a thread. Use a blocking queue to wait for messages. There should only be a single sender queue throughout your application, so use a singleton pattern.
e.g.
QueueSingleton queue = QueueSingleton.getSenderQueue();
Message message = queue.take() // blocks thread
send(message); //send message to server
When you need to send a message to the server, you can use the blocking queue to send the message.
QueueSingleton queue = QueueSingleton.getSenderQueue();
queue.put(message)
The client thread will wake up and process the message.
For maintaining the connection, use a timer task. This is special type of thread that calls a run method repetitively at specified periods. You can use this to post a message, a ping message, every so often.
For processing the received message, you could have another thread, waiting for messages on another blocking queue (receiver queue). The client thread will put the received message on this queue.

How to create a simultaneous connection between a server and a client on the same network in Java?

I am trying to create a MapleStory type game for my computer science final. It's basically a 2D RPG played over LAN. My question is how would I get the connection between two computers to be simultaneous?
class MagicServer extends Thread
{
private ServerSocket serverSocket;
public MagicServer(int port) throws IOException
{
serverSocket = new ServerSocket(port);
//serverSocket.setSoTimeout(10000);
}
public void run()
{
Scanner kb = new Scanner(System.in);
while(true)
{
try
{
System.out.println("Waiting for client on port " + serverSocket.getLocalPort() + "...");
Socket server = serverSocket.accept();
System.out.println("Just connected to " + server.getRemoteSocketAddress());
DataInputStream in = new DataInputStream(server.getInputStream());
DataOutputStream out = new DataOutputStream(server.getOutputStream());
System.out.println(in.readUTF());
for(int i=0; i<5; i++)
{
System.out.println(in.readUTF());
out.writeUTF(kb.nextLine());
}
server.close();
}
catch(SocketTimeoutException s)
{
System.out.println("Socket timed out!");
break;
}
catch(IOException e)
{
e.printStackTrace();
break;
}
}
}
public static void main(String [] args)
{
int port = 2001;
try
{
Thread t = new MagicServer(port);
t.start();
}catch(IOException e)
{
e.printStackTrace();
}
}
}
I was experimenting with this and I can only send/recieve messages between two computers in order (i.e. server sends to client then client sends to server) and I cannot go out of order. How would I do this?
You have to de-couple your reads/writes by using multiple threads or through interlocked queues to manage your work.
In you loop, you force the serialization by doing a read then a write in the code.
There's no reason you couldn't spawn a reader thread that feeds into a queue to perform work by worker threads and writes happen on another thread. You just need to have synchronization on the work queues.
You can also poll the socket to see if there's any data available to read and if not, send any data waiting to be written.
There's more exotic ways to do it, and plenty of examples, search around and see what your comfortable with.
There's a similar thread here
You could use multiple threads to have a connection one way in one thread on one port and a connection the other way on a different port in the other thread. This could introduce some synchronization issues though, so I would recommend rethinking your design so that communication only needs to occur in one direction at a time (you can switch back and forth as often as you like if need be).

Categories

Resources