I'm currently trying to implement the game of Nim using Java, I want to be able to have one player act as the server and another as the player.
I'm fairly new to Java networking and have only had experience using basic TCP/IP where the human client connects to a computer host.
The trouble I'm having is that I need to be able to differentiate between the different players whilst implementing the protocol for the game (The protocol being the logic for the game).
As it stands I can let one player (Client) interact with the Server. All that happens is the Client can play the game but there is no oppostion (The Server merely tracks the state of the game e.g. How many sticks left, valid input etc..).
How would I go about adding a second player to take the place of the host?
Edit:
The Client and Server code has been posted, it is the code I have used and I'm quite comfortable with, the question I am asking is would it be a suitable base to implement a multi-player game or would I need to do something completely different?
My Nim Protocol: (Untested)
public class NimLogic
{
private static final int WAITING = 0;
private static final int EVALUATING = 1;
private static final int ANOTHER = 2;
private int currentState = WAITING;
private int theInput = 0;
private int totalSticks = 10;
String processInput(String input) {
String theOutput = null;
try
{
theInput = Integer.parseInt(input);
}
catch (Exception e)
{
// Ignore conversion error
}
switch (currentState)
{
case WAITING:
theOutput = "Take how many sticks?";
currentState = EVALUATING;
break;
case EVALUATING:
if(theInput == 1 | theInput == 2 | theInput == 3)
{
if (theInput < totalSticks)
{
totalSticks -= theInput;
theOutput = "There are" + totalSticks + " left.";
}
else if (theInput > totalSticks)
{
theOutput = "Error: You cannot take more sticks than that are available";
currentState = EVALUATING;
}
}
if(totalSticks == 1)
{
theOutput = "Game Over! Play again? (Yes = 1, No = 0)...";
currentState = ANOTHER;
}
break;
case ANOTHER:
if (theInput == 1)
{
totalSticks = 10;
currentState = EVALUATING;
theOutput = "Take how many sticks?";
}
else
{
theOutput = "Bye.";
}
}
return theOutput;
}
}
Thanks for all the help!
Edit:
Client
public class Client
{
#SuppressWarnings("static-access")
public static void main(String machine[])
{
Socket kkSocket = null;
PrintStream os = null;
DataInputStream is = null;
try
{
kkSocket = new Socket(machine[0], 4444);
os = new PrintStream(kkSocket.getOutputStream());
is = new DataInputStream(kkSocket.getInputStream());
}
catch(UnknownHostException e)
{
System.err.println("Socket Connect failed on " + machine[0]);
}
catch (IOException e)
{
System.err.println("Streams failed on " + machine[0]);
}
if (kkSocket != null && os != null && is != null )
{
try
{
String fromServer, fromClient;
while((fromServer = is.readLine()) != null && !fromServer.equals("Bye."))
{
fromClient = JOptionPane.showInputDialog(fromServer);
os.println(fromClient);
}
JOptionPane.showMessageDialog(null, "Goodbye, keep smiling.");
os.close();
is.close();
kkSocket.close();
}
catch (UnknownHostException e)
{
System.err.println("Can't connect to " + machine[0] + e);
}
catch (IOException e)
{
e.printStackTrace();
System.err.println("I/O failed on " +machine[0]);
}
}
}
}
Server
public class Server
{
public static void main(String arg[])
{
ServerSocket serverSocket = null;
try
{
serverSocket = new ServerSocket(4444);
}
catch (IOException e)
{
System.err.println("Can't listen on 4444 -> " + e);
System.exit(1);
}
Socket clientSocket = null;
try // allow the client to connect
{
clientSocket = serverSocket.accept();
}
catch (IOException e)
{
System.err.println("Failed accept on 4444 -> " + e);
System.exit(1);
}
try
{
DataInputStream is =
new DataInputStream(new BufferedInputStream
(clientSocket.getInputStream()));
PrintStream os =
new PrintStream(new BufferedOutputStream
(clientSocket.getOutputStream(), 1024), false);
GuessState kks = new GuessState();
String inputLine, outputLine;
outputLine = kks.processInput(null);
os.println(outputLine);
os.flush();
while((inputLine = is.readLine()) != null
&& !outputLine.equals("Bye."))
{
outputLine = kks.processInput(inputLine);
os.println(outputLine);
os.flush();
}
os.close();
is.close();
clientSocket.close();
serverSocket.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
I'm not quite sure if I'm answering your question here, so apologies if I'm not. Also, it's been a little while since I did any Java networking code, so there might be a few wrinkles here which hopefully others can sort out.
The following is a bit of a brain dump of the changes I'd probably make, for better or worse...
Rework the networking code to accept multiple connections. Normally you'd do this by handing off the socket returned by ServerSocket.accept to a thread to process. If you were dealing with a lot of connections, you could do it using NIO instead, but that's probably too far to fast for now.
Separate the game state from the client conversation code. To keep things simple, embed the client conversation code in the thread object. The game state needs to be in an object that's shared between all the threads servicing the socket.
I'd recommend making the game state a proper 'domain object' rather than having it parsing strings etc. It should have operations like 'take(clientID, int)' rather than 'processInput'.
Consider using the observer pattern to distribute events from the domain object to the socket threads. Examples of events might be 'turnTaken' or 'gameComplete'.
Embed the notion of 'turns' into the game object, and have the server broadcast an event to the socket threads announcing whose turn it is.
Hope that gives you a starter for ten?
The server/client aspect should have no bearing on the communication of the two players. You should be able to spawn two instances of the Nim game, one that listen for an incoming connection on some port(Server), and one that connects to it (Client). Once the connection is established, you can pass Objects between the two instances over your connection that represent game information. Each instance of your Nim game is responsible for parsing that game data and running the Nim logic on it.
In essence, each instance of the game can run as a server or a client. Here's some code I wrote for Chess that should be applicable. Read through it. Elsewhere I instance a Server or Client and store it in a reference of type NetworkIdenitity.
private abstract class NetworkEntity
extends Thread {
ObjectOutputStream outputStream;
ObjectInputStream inputStream;
Socket connectionHandle;
Object messageToSend;
Object receivedMessage;
public NetworkEntity(final String name) {
super(name);
}
#Override
public abstract void run();
public void getStreams()
throws IOException {
this.outputStream = new ObjectOutputStream(this.connectionHandle.getOutputStream());
this.outputStream.flush();
this.inputStream = new ObjectInputStream(this.connectionHandle.getInputStream());
}
public void closeConnection() {
try {
if (this.outputStream != null) {
this.outputStream.close();
}
if (this.inputStream != null) {
this.inputStream.close();
}
if (this.connectionHandle != null) {
this.connectionHandle.close();
chatPanel.writeToDisplay("Connection closed with "
+ this.connectionHandle.getInetAddress().getHostName());
}
}
catch (final IOException e) {
JOptionPane.showMessageDialog(thisFrame, "Problems experienced when closing connection",
"Notification", JOptionPane.ERROR_MESSAGE);
}
}
public void processIncomingData()
throws IOException {
do {
try {
this.receivedMessage = this.inputStream.readObject();
}
catch (final ClassNotFoundException e) {
JOptionPane.showMessageDialog(thisFrame, "read() error: message from "
+ this.connectionHandle.getInetAddress().getHostName() + " not received", "Notification",
JOptionPane.ERROR_MESSAGE);
}
if (this.receivedMessage instanceof Move) {
final Move m = (Move) this.receivedMessage;
System.out.println(getName() + " got move" + m);
requestMove(Table.this.chessBoard, Table.this.currentPlayer, Table.this.currentOpponent, m, false);
repaint();
}
else if (this.receivedMessage instanceof Board) {
final Board b = (Board) this.receivedMessage;
System.out.println(getName() + " received this board:");
b.printCurrentBoardState();
// System.out.println("local copy looked like this: " );
// chessBoard.printCurrentBoardState();
// chessBoard.setGameBoard(b.getGameBoard());
// switchCurrentPlayer();
// chessBoard.updateBoardState(currentPlayer,
// currentOpponent);
repaint();
}
else if (this.receivedMessage instanceof String) {
chatPanel.writeToDisplay((String) this.receivedMessage);
}
} while (/* !message.equals("SERVER>>> TERMINATE") */true);
}
public void sendData(final Object obj_to_send) {
try {
this.outputStream.writeObject(obj_to_send);
this.outputStream.flush();
}
catch (final IOException e) {
}
}
}
private final class Client
extends NetworkEntity {
private final String hostName;
private final int serverPort;
public Client(final String host, final int port) {
super("CLIENT");
this.hostName = host;
this.serverPort = port;
}
#Override
public void run() {
try {
connectToServer();
getStreams();
processIncomingData();
}
catch (final EOFException eof) {
}
catch (final IOException ioe) {
}
catch (final NullPointerException npe) {
}
finally {
closeConnection();
}
}
private void connectToServer()
throws IOException {
try {
this.connectionHandle = new Socket(InetAddress.getByName(this.hostName), this.serverPort);
connectionEstablished = true;
chatPanel.writeToDisplay("Successfully connected to "
+ this.connectionHandle.getInetAddress().getHostName());
}
catch (final IOException e) {
chatPanel.writeToDisplay("Failed to connect to: " + this.hostName);
}
}
}
private final class Server
extends NetworkEntity {
private ServerSocket server;
private final int listenPort;
public Server(final int listen_port) {
super("SERVER");
this.listenPort = listen_port;
}
#Override
public void run() {
try {
this.server = new ServerSocket(this.listenPort, 1);
chatPanel.writeToDisplay("Listening on port " + this.listenPort);
try {
waitForConnection();
getStreams();
processIncomingData();
}
catch (final EOFException eof) {
// System.out.println(getName() + "exception: " +eof);
// eof.printStackTrace();
}
catch (final IOException ioe) {
// System.out.println(getName() + "exception: " +ioe);
// ioe.printStackTrace();
}
finally {
closeConnection();
}
}
catch (final IOException e) {
JOptionPane.showMessageDialog(thisFrame, "Network Error: " + e, "Notification",
JOptionPane.ERROR_MESSAGE);
}
}
private void waitForConnection()
throws IOException {
this.connectionHandle = this.server.accept();
connectionEstablished = true;
chatPanel.writeToDisplay("Connection received from:" + this.connectionHandle.getInetAddress().getHostName());
}
#Override
public void closeConnection() {
super.closeConnection();
try {
this.server.close();
}
catch (final IOException e) {
chatPanel.writeToDisplay(getName() + "failed to disconnect from the network");
}
}
Related
I have to create a simple rotating proxy application where 100 requests get evenly distributed to 10 devices. I've got the following structure:
WebServer with a Java-SocketServer running. All Android devices are connected to this Socket-Server to be able to know which devices are currently online and for determining which device should be used for the next request.
10 Android devices in different networks. They are connected to the Socket Server and are waiting for requests that should be forwarded to the remote address and then sent back to the SocketServer.
In easy words: I basically have to create an application similar like Honeygain, Peer2Profit or IPRoyal Pawns so that I can later do requests like this:
//Use "-x" to set Proxy-IP and Proxy-Port
curl -x ANDROID_DEVICE_IP:PORT -L https://www.google.com
I managed to have an always running proxy service in an Android application. It basically looks like this and just forwards HTTP-Requests from Port 1440 to the desired remote address and then sends the response back to the original client. The Proxy basically works fine.
public class ProxyServerThread extends Thread {
public static void main(String[] args) {
(new ProxyServerThread()).run();
}
public ProxyServerThread() {
super("Server Thread");
}
#Override
public void run() {
try (ServerSocket serverSocket = new ServerSocket(1440)) {
Socket socket;
try {
while ((socket = serverSocket.accept()) != null) {
(new Handler(socket)).start();
}
} catch (IOException e) {
e.printStackTrace(); // TODO: implement catch
}
} catch (IOException e) {
e.printStackTrace(); // TODO: implement catch
return;
}
}
public static class Handler extends Thread {
public static final Pattern CONNECT_PATTERN = Pattern.compile("CONNECT (.+):(.+) HTTP/(1\\.[01])", Pattern.CASE_INSENSITIVE);
private final Socket clientSocket;
private boolean previousWasR = false;
public Handler(Socket clientSocket) {
this.clientSocket = clientSocket;
}
#Override
public void run() {
try {
String request = readLine(clientSocket);
System.out.println(request);
Matcher matcher = CONNECT_PATTERN.matcher(request);
if (matcher.matches()) {
String header;
do {
header = readLine(clientSocket);
} while (!"".equals(header));
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(clientSocket.getOutputStream(), "ISO-8859-1");
final Socket forwardSocket;
try {
forwardSocket = new Socket(matcher.group(1), Integer.parseInt(matcher.group(2)));
System.out.println(forwardSocket);
} catch (IOException | NumberFormatException e) {
e.printStackTrace(); // TODO: implement catch
outputStreamWriter.write("HTTP/" + matcher.group(3) + " 502 Bad Gateway\r\n");
outputStreamWriter.write("Proxy-agent: Simple/0.1\r\n");
outputStreamWriter.write("\r\n");
outputStreamWriter.flush();
return;
}
try {
outputStreamWriter.write("HTTP/" + matcher.group(3) + " 200 Connection established\r\n");
outputStreamWriter.write("Proxy-agent: Simple/0.1\r\n");
outputStreamWriter.write("\r\n");
outputStreamWriter.flush();
Thread remoteToClient = new Thread() {
#Override
public void run() {
forwardData(forwardSocket, clientSocket);
}
};
remoteToClient.start();
try {
if (previousWasR) {
int read = clientSocket.getInputStream().read();
if (read != -1) {
if (read != '\n') {
forwardSocket.getOutputStream().write(read);
}
forwardData(clientSocket, forwardSocket);
} else {
if (!forwardSocket.isOutputShutdown()) {
forwardSocket.shutdownOutput();
}
if (!clientSocket.isInputShutdown()) {
clientSocket.shutdownInput();
}
}
} else {
forwardData(clientSocket, forwardSocket);
}
} finally {
try {
remoteToClient.join();
} catch (InterruptedException e) {
e.printStackTrace(); // TODO: implement catch
}
}
} finally {
forwardSocket.close();
}
}
} catch (IOException e) {
e.printStackTrace(); // TODO: implement catch
} finally {
try {
clientSocket.close();
} catch (IOException e) {
e.printStackTrace(); // TODO: implement catch
}
}
}
private static void forwardData(Socket inputSocket, Socket outputSocket) {
try {
InputStream inputStream = inputSocket.getInputStream();
try {
OutputStream outputStream = outputSocket.getOutputStream();
try {
byte[] buffer = new byte[4096];
int read;
do {
read = inputStream.read(buffer);
if (read > 0) {
outputStream.write(buffer, 0, read);
if (inputStream.available() < 1) {
outputStream.flush();
}
}
} while (read >= 0);
} finally {
if (!outputSocket.isOutputShutdown()) {
outputSocket.shutdownOutput();
}
}
} finally {
if (!inputSocket.isInputShutdown()) {
inputSocket.shutdownInput();
}
}
} catch (IOException e) {
e.printStackTrace(); // TODO: implement catch
}
}
private String readLine(Socket socket) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
int next;
readerLoop:
while ((next = socket.getInputStream().read()) != -1) {
if (previousWasR && next == '\n') {
previousWasR = false;
continue;
}
previousWasR = false;
switch (next) {
case '\r':
previousWasR = true;
break readerLoop;
case '\n':
break readerLoop;
default:
byteArrayOutputStream.write(next);
break;
}
}
return byteArrayOutputStream.toString("ISO-8859-1");
}
}
}
Here comes the Problem:
Everything works fine but only on the local network. I cannot manage to get this to work without port forwarding. Since all devices are on their mobile cellular data I need a way to be able to connect to the device anyway.
How do the mentioned apps manage to connect to the devices?
Explanation
I'm currently trying to create a Multiplayer Game with Java where up to five Players can play together.
The problem is that when I'm trying to connect multiple Clients to my Server I get an Exception and the Server doesn't work anymore.
With one Client at a time, everything works fine.
So what I need is a Server that can handle up to five players at a time and the clients should always get some new game data from the Server
every few seconds (Connected Players, etc.).
The "Game data" in the code below is the String I'm sending through the Object Stream.
Normally I would send an Object which has all the game data, but with the String, I get the same problem.
I'm struggling with the problem that only one Client can connect without any errors occurring for some days now and I didn't find a solution to my problem.
I saw that there are things like java.nio or the ExecutorService, but I didn't really understand those that much, so I don't know if they can help.
I have made a smaller program that simulates the same problem I get with my bigger program.
To Start the Server, you need to Start the GameMultiPlayerCreate.java Class, and for the Client, the Client.java class.
I'm new to Sockets so if something is unnecessary or if something can be made better please let me know.
So the Error I'm getting when I connect two or more Clients is:
java.io.StreamCorruptedException: invalid stream header: 00050131
at java.io.ObjectInputStream.readStreamHeader(Unknown Source)
at java.io.ObjectInputStream.<init>(Unknown Source)
at Server.waitForData(Server.java:89) //I highlighted that in the code with a comment
at Server.loopWaitForData(Server.java:49)
at Server.run(Server.java:34)
at java.lang.Thread.run(Unknown Source)
Code
GameMultiPlayerCreate.java: Should start the Server threads if a Client connects
public class GameMultiPlayerCreate {
ServerSocket socketServer = null;
static String settingIp = "localhost";
static String settingPort = "22222";
static byte settingPlayers = 5;
public static int connectedPlayers = 0;
public static void main(String[] args) {
try {
GameMultiPlayerCreate objGameMultiPlayerCreate = new GameMultiPlayerCreate();
objGameMultiPlayerCreate.createServer();
} catch (NumberFormatException | IOException | InterruptedException e) {
e.printStackTrace();
}
}
public void createServer() throws NumberFormatException, UnknownHostException, IOException, InterruptedException {
while (connectedPlayers < settingPlayers) {
socketServer = new ServerSocket(Integer.parseInt(settingPort), 8, InetAddress.getByName(settingIp));
System.out.println("Server is waiting for connection...");
Socket socket = socketServer.accept();
new Thread(new Server(socket)).start();
Thread.sleep(5000);
socketServer.close();
}
}
}
Server.java: This is the Class of which a new Thread should be created for each connected Client (Client Handler)
public class Server implements Runnable {
protected static Socket socket = null;
private int loops;
private int maxLoops = 10;
private int timeout = 10000;
protected static boolean killThread = false;
private boolean authenticated = true; //true for testing
protected static String ip;
protected static int port;
public Server(Socket socket) throws IOException {
Server.socket = socket;
}
public void run() {
try {
socket.setSoTimeout(timeout);
} catch (SocketException e) {
System.out.println("Error while trying to set Socket timeout. ");
System.out.println("Closing Thread..." + Thread.currentThread());
disconnectClient();
}
if (!killThread) {
GameMultiPlayerCreate.connectedPlayers = GameMultiPlayerCreate.connectedPlayers + 1;
loopWaitForData();
}
}
private void disconnectClient() {
System.out.println("Kicking Client... " + Thread.currentThread());
killThread = true;
GameMultiPlayerCreate.connectedPlayers = GameMultiPlayerCreate.connectedPlayers - 1;
}
public void loopWaitForData() {
while (!killThread) {
System.out.println(maxLoops + ", " + loops);
if (maxLoops - loops > 0) {
try {
waitForData();
} catch (SocketTimeoutException e) {
System.out.println("Error occurred while waiting for Data. Thread disconnected? Sending reminder. " + Thread.currentThread());
if (!authenticated) {
System.out.println("Kicking Client: Not authenticated");
disconnectClient();
} else {
commandReminder();
}
} catch (ClassNotFoundException | IOException e) {
loops = loops + 1;
System.out.println("Error occurred while waiting for Data. Waiting for more Data. " + Thread.currentThread());
e.printStackTrace();
loopWaitForData();
}
} else if (maxLoops - loops == 0) {
System.out.println("Error occurred while waiting for Data. Maximum trys reached. Disbanding connection. " + Thread.currentThread());
disconnectClient();
loops = loops + 1;
} else {
System.out.println("Closing Thread..." + Thread.currentThread());
disconnectClient();
}
}
}
private void commandReminder() {
System.out.println("Reminder");
try {
String code = new String("0");
ObjectOutputStream outputObject = new ObjectOutputStream(Server.socket.getOutputStream());
outputObject.writeObject(code);
} catch (IOException e) {
System.out.println("Error occurred while trying to authenticate Client: " + e + " in " + Thread.currentThread());
}
}
public void waitForData() throws IOException, ClassNotFoundException {
String code;
System.out.println("Waiting for Data...");
//Next line is where the error occurres
ObjectInputStream inputObject = new ObjectInputStream(socket.getInputStream());
while ((code = (String) inputObject.readObject()) != null) {
System.out.println("Received Data...");
System.out.println("Input received: " + code);
return;
}
}
}
Client.java: This is the Client
public class Client {
public static Socket socket = new Socket();
private int loops = 0;
private int maxLoops = 10;
private static boolean killThread = false;
private String ip;
private int port;
public Client(String receivedIp, String receivedPort) {
ip = receivedIp;
port = Integer.parseInt(receivedPort);
try {
System.out.println("Trying to connect to Server...");
socket.connect(new InetSocketAddress(ip, port));
System.out.println("Connected!");
} catch (IOException e) {
System.out.println("Error occurred while trying to connect to Server.");
}
loopWaitForData();
}
public static void main(String[] args) {
#SuppressWarnings("unused")
Client objClient = new Client("localhost", "22222");
}
public void loopWaitForData() {
while (!killThread) {
System.out.println(maxLoops + ", " + loops);
if (maxLoops - loops > 0) {
try {
waitForData();
} catch (IOException | ClassNotFoundException e) {
loops = loops + 1;
try {
Thread.sleep(2000);
} catch (InterruptedException e1) {
}
System.out.println("Error occurred while waiting for Data. Waiting for more Data. " + Thread.currentThread());
e.printStackTrace();
loopWaitForData();
}
} else if (maxLoops - loops == 0){
System.out.println("Error occurred while waiting for Data. Maximum trys reached. Disbanding connection. " + Thread.currentThread());
try {
socket.close();
} catch (IOException e) {
System.out.println("Failed to close Socket " + Thread.currentThread());
}
loops = loops + 1;
} else {
System.out.println("Closing Thread..." + Thread.currentThread());
killThread = true;
}
}
}
public void waitForData() throws IOException, ClassNotFoundException {
InputStream input = socket.getInputStream();
ObjectInputStream inputObject = new ObjectInputStream(input);
String code;
System.out.println("Waiting for Data...");
while ((code = (String) inputObject.readObject()) != null) {
System.out.println("Received Data...");
System.out.println("Input received: " + code);
answer();
return;
}
}
private void answer() {
try {
String code = new String("1");
ObjectOutputStream outputObject = new ObjectOutputStream(socket.getOutputStream());
outputObject.writeObject(code);
} catch (IOException e) {
System.out.println("Error occurred while trying to answer: " + e + " in " + Thread.currentThread());
}
}
}
So, I'm fairly new to sockets and data streams... And I am absolutely baffled by this issue I'm having. I've searched for hours trying to find a solution, assuming that other people might have had the same issue I'm having, but I've found absolutely nothing helpful so far.
I'm writing a very simple multithreaded server/client program that is supposed to open a serverSocket, and accept connections from clients, storing them in a simple arraylist (I'll change the storage process once I actually get messages to send), and then a message handler thread parses the list, and checks if a user has written to the server. If the user has written something, the program then displays the resulting message to the console. My program successfully writes to the server socket through the DataOutputStream, but when I attempt to read from the corresponding DataInputStream on the server side, it says the stream is empty, and my program will continue to loop. I've checked that the DataOutputStream receives the data through DataOutputStream.size(), and that the DataInputStream I am attempting to read data from corresponds to the correct DataOutputStream I mentioned before.
User Code:
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketException;
public class User {
private String hashID;
private Socket connection;
private static int hashVal = 0;
DataInputStream consoleInputStream;
String consoleInput = "";
public User(Socket conn) throws SocketException {
hashID = hashUserKey();
connection = conn;
connection.setSoTimeout(1500);
consoleInputStream = new DataInputStream(System.in);
}
private static String hashUserKey() { //placeholder for now
hashVal++;
return("Guest" + hashVal);
}
public Socket getSocket() {
return this.connection;
}
public String getID() {
return this.hashID;
}
public boolean disconnect() {
try {
consoleInputStream.close();
connection.close();
return true;
} catch (IOException e) {
System.err.println(hashID + " was unable to successfully disconnect");
return false;
}
}
public void startConnection() {
new Thread() {
#Override
public void run() {
while(!connection.isClosed()) {
try {
consoleInput = consoleInputStream.readLine();
if(consoleInput != null || consoleInput != "") {
writeToServer(consoleInput);
}
} catch (IOException e) {
System.err.println("Was not able to read console input");
}
}
System.out.println("You were disconnected, have a nice day!");
return;
}
}.start();
}
private boolean writeToServer(String toWrite) {
try {
String msg = hashID + ">>>: " + toWrite;
DataOutputStream outStream = new DataOutputStream(connection.getOutputStream());
outStream.writeUTF(toWrite + "\r\n");
outStream.flush();
consoleInput = "";
System.out.println(msg + "\t was written to " + connection.getInetAddress() + ":" + connection.getPort());
return true;
} catch (IOException e) {
System.err.println(hashID + " was unable to write to server");
return false;
}
}
#Override
public boolean equals(Object o) {
User t = (User) o;
if(t.hashID == this.hashID) {
return true;
}
return false;
}
}
Server Code:
import java.net.*;
import java.io.*;
import java.util.*;
public class TestServer {
private static ServerSocket server;
private static TestLogger logger;
private static Thread serverHandlerThread;
private static Thread messageHandlerThread;
private static ArrayList<User> users;
private static volatile boolean hasBeenStopped = false;
public static boolean startServer(int port) {
logger = new TestLogger();
logger.log("Attempting to create default shutdown behavior for server");
Runtime.getRuntime().addShutdownHook(new Thread() {
#Override
public void run() {
if(!hasBeenStopped) {
logger.warn("Server was shut down without running stopServer(), running by default");
stopServer();
}
}
}
);
logger.log("Shutdown behaivor created. Now attempting to set up user database"/*TODO create a real database*/);
users = new ArrayList<User>();
logger.log("Attempting to start server");
try {
server = new ServerSocket(port);
logger.log("Server successfully started at " + server.getInetAddress() + ":" + server.getLocalPort() +", now attempting to start user connection handler");
serverHandlerThread = new Thread() {
#Override
public void run() {
this.setName("serverHandlerThread");
while(!server.isClosed()) {
try {
Socket temp = server.accept();
logger.log("Connection accepted from " + temp.getInetAddress());
System.out.println("Connection accepted from " + temp.getInetAddress());
startUserConnection(new User(temp));
} catch (SocketException e) {
logger.warn("Server was closed while in accept phase");
} catch (IOException e) {
e.printStackTrace();
}
}
logger.log(this.getName() + " was stopped, server socket was closed successfully");
return;
}
};
serverHandlerThread.start();
logger.log("Server thread successfully started, listening for connections on: " + server.getInetAddress().toString() + ":" + port);
logger.log("Attempting to start message handler thread to read user inputs");
messageHandlerThread = new Thread() {
#Override
public void run() {
this.setName("messageHandlerThread");
while(!server.isClosed()) {
if(users.isEmpty()) {
continue;
}
for(int i = 0; i < users.size(); i++) {
User temp = users.get(i);
try {
System.out.println(new DataInputStream(temp.getSocket().getInputStream()).readUTF());
} catch (IOException e) {
System.err.println("Nothing to read from client: " + temp.getID());
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
};
messageHandlerThread.start();
return true;
} catch (IOException e) {
logger.error("Could not bind server socket to port.");
return false;
}
}
public static boolean stopServer() {
logger.log("Started shut down process");
if(serverHandlerThread == null || !serverHandlerThread.isAlive()) {
logger.warn("Thread has not been started yet or has already been killed");
return false;
}
else {
stopAllUserConnections();
try {
server.close();
hasBeenStopped = true;
while(serverHandlerThread.isAlive()) {
}
logger.log("Server was successfully shut down");
return true;
} catch (IOException e) {
logger.error("Could not close server socket");
return false;
}
}
}
private static void startUserConnection(User user) {
logger.log("Connected new user from " + user.getSocket().getInetAddress());
users.add(user);
System.out.println(user.getID() + " was added to list");
user.startConnection();
}
private static boolean stopUserConnection(User user) {
logger.log("Attempting to disconnect user, address: " + user.getSocket().getInetAddress());
for(User u : users) {
if(u.equals(user)) {
u.disconnect();
return true;
}
}
logger.warn("Could not find user with address: " + user.getSocket().getInetAddress());
return false;
}
private static boolean stopAllUserConnections() {
logger.log("Attempting to disconnect all users from the server");
if(users.isEmpty()) {
logger.warn("No users available to disconnect");
return false;
}
for(User u : users) {
u.disconnect();
}
users.clear();
return true;
}
public static void main(String args[]) {
startServer(*the_port*);
Client c = new Client();
c.connect("0.0.0.0", *the_port*);
}
}
Client Code:
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
public class Client {
public boolean connect(String serverName, int port) {
try {
System.out.println("Attempting to connect");
Socket sock = new Socket(serverName, port);
System.out.println("Connected");
return true;
} catch (UnknownHostException e) {
System.err.println("Could not resolve " + serverName + ":" + port);
}
catch (IOException e) {
e.printStackTrace();
}
return false;
}
}
So as I said above, this works fine up until I attempt to read the data written to the server. No matter what I do, the server's call to readUTF() on the socket always throws an IOException and checking the bytes ready to read by using DataInputStream.available() returns 0 as well. My sample output from my most recent run is as follows:
Attempting to connect
Connected
Connection accepted from *the_address*
Guest1 was added to list
Nothing to read from client: Guest1
Nothing to read from client: Guest1
Nothing to read from client: Guest1
test
Guest1>>>: test was written to *the_address:another_port*
Nothing to read from client: Guest1
Nothing to read from client: Guest1
Nothing to read from client: Guest1
I know my code may be terribly optimized, and I'll work on fixing that later, but right now, all I want to know is why my DataInputStream is empty after flushing the corresponding DataOutputStream, and how I can successfully send UTF data between them.
First some Information regarding my Setup.
I have a S8 Cellphone, where i run this App, based upon the AR-Devkit demo from Google.
public void closeSocket(DatagramSocket socket) {
if (socket != null && socket.isConnected() ) {
while (!socket.isConnected()) {
socket.disconnect();
try {
Thread.sleep(SpringAR.TIME_OUT_IN_BROADCAST);
} catch (InterruptedException e) {
Log.d(SpringAR.protocollDebugLogPrefix, " Socket Closing interrupted");
e.printStackTrace();
}
}
}
if (socket != null && !socket.isClosed()) {
socket.close();
while (!socket.isClosed()) {
try {
Thread.sleep(SpringAR.TIME_OUT_IN_BROADCAST);
} catch (InterruptedException e) {
Log.d(SpringAR.protocollDebugLogPrefix, " Socket Closing interrupted");
e.printStackTrace();
}
}
}
}
public DatagramSocket createSocket(InetAddress ipAddress, int port) {
try {
DatagramSocket socket = new DatagramSocket(null);
InetSocketAddress address = new InetSocketAddress(ipAddress, port);
socket.setReuseAddress(true);
socket.bind(address);
return socket;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public DatagramSocket getBroadcastListenerSocket() throws IOException {
InetSocketAddress anyAdress = new InetSocketAddress(InetAddress.getByName("0.0.0.0"), 9000);
DatagramSocket socket = new DatagramSocket(null);
socket.setSoTimeout(30);
socket.setReuseAddress(true);
socket.bind(anyAdress);
return socket;
}
public DatagramSocket getBroadcastSenderSocket(DatagramSocket oldSocket) {
DatagramSocket socket = null;
try {
ARDeviceAddress = InetAddress.getByName(comonUtils.getIPAddress(true));
socket = getSocket(oldSocket, ARDeviceAddress, SpringAR.UDP_SERVER_PORT, null);
socket.setBroadcast(true);
socket.setSoTimeout(SpringAR.TIME_OF_FRAME_IN_MS);
} catch (IOException e) {
e.printStackTrace();
}
return socket;
}
public DatagramSocket getSocket(DatagramSocket oldSocket, InetAddress ipAddress, int port, InetAddress targetAddress) {
if (oldSocket != null ) {
closeSocket(oldSocket);
}
DatagramSocket socket = null;
try {
socket = createSocket(ipAddress, port);
socket.setBroadcast(false);
socket.setSoTimeout(SpringAR.TIME_OF_FRAME_IN_MS);
if (targetAddress != null)
socket.connect(targetAddress, port);
} catch (SocketException e) {
e.printStackTrace();
}
return socket;
}
public class DatagramReciever extends Thread {
private String datagramToSend = "";
private boolean newDatagramToSend = false;
private DatagramPacket snd_packet;
DatagramSocket senderSocket = null;
DatagramSocket listenerSocket = null;
private DatagramSocket broadCastListenerSocket;
//Buffer gettters and setters
private int writeBuffer = 0;
private SpringAR.comStates oldState;
int getReadBuffer() {
if (writeBuffer == 1) return 0;
return 1;
}
void switchBuffer() {
recieveByteIndex = 0;
writeBuffer = getReadBuffer();
}
public String dbg_message = "";
//Management Communication Headers
public void kill() {
closeSocket(senderSocket);
closeSocket(listenerSocket);
closeSocket(broadCastListenerSocket);
}
public void run() {
try {
initializeBroadcastConnection();
while (true) {
//Recieving Datagramm
DatagramPacket rcv_packet = new DatagramPacket(rcv_message[writeBuffer], rcv_message[writeBuffer].length);
boolean NewMessageArrived = true;
try {
listenerSocket.receive(rcv_packet);
} catch (SocketTimeoutException e) {
NewMessageArrived = false;
}
//Watchdog
handleWatchDogTimer(State);
//TODO Delete String conversion
if (NewMessageArrived) {
dbg_message = new String(rcv_message[writeBuffer], 0, rcv_packet.getLength(), "US-ASCII");
Log.d(SpringAR.dataDebugLogPrefix, "" + rcv_packet.getAddress().getHostAddress() + ": " + dbg_message.trim() + " of " + rcv_packet.getLength() + "length ");
}
if (validatePackageSender(rcv_packet)) {
connectionStateMachine(rcv_message, rcv_packet);
}
//Sending Datagram
if (newDatagramToSend && hostIpAddress != null) {
//Log.d(SpringAR.protocollDebugLogPrefix, "Server sending: " + datagramToSend);
byte[] snd_message = datagramToSend.getBytes();
try {
snd_packet = packSendPackageByState(snd_message);
assert (snd_packet != null);
senderSocket.send(snd_packet);
newDatagramToSend = false;
} catch (IOException e1) {
e1.printStackTrace();
//causes Caused by: android.system.ErrnoException: sendto failed: EINVAL (Invalid argument)
Log.e(SpringAR.protocollDebugLogPrefix, "Server Error in State: " + State.name());
break;
}
}
}
} catch (IOException e1) {
e1.printStackTrace();
}
}
private void initializeBroadcastConnection() throws IOException {
ARDeviceAddress = InetAddress.getByName(comonUtils.getIPAddress(true));
senderSocket = getSocket(null, ARDeviceAddress, SpringAR.UDP_SERVER_PORT, null);
broadCastListenerSocket = getBroadcastListenerSocket();
listenerSocket = broadCastListenerSocket;
Log.d(SpringAR.protocollDebugLogPrefix, "initializeBroadcastConnection completed");
}
// handles management traffic like configurstion files
private void connectionStateMachine(byte[][] payload, DatagramPacket rcv_packet) throws IOException {
//Reset triggered by Host
if (comonUtils.indexOf(payload[writeBuffer], SpringAR.recieveResetHeaderByte) != SpringAR.STRING_NOT_FOUND) {
State = SpringAR.comStates.STATE_resetCommunication;
}
Log.d(SpringAR.protocollDebugLogPrefix, "ConnectionStateMachine: " + State.name());
switch (State) {
case STATE_resetCommunication: {
messageCounter = 0;
listenerSocket = broadCastListenerSocket;
hostIpAddress = comonUtils.getBroadcastAddress(context);
senderSocket = getBroadcastSenderSocket(senderSocket);
setSendToSpringMessage(SpringAR.sendResetHeader);
State = SpringAR.comStates.STATE_broadCastHeader;
return;
}
case STATE_broadCastHeader: {
if (comonUtils.indexOf(payload[writeBuffer], SpringAR.recieveHostReplyHeaderByte) != SpringAR.STRING_NOT_FOUND) {
Log.d(SpringAR.protocollDebugLogPrefix, " Host Reply Header recieved");
//Extract the hostIp
String hostIpAdressAsString = new String(payload[writeBuffer]);
hostIpAdressAsString = hostIpAdressAsString.replace(SpringAR.recieveHostReplyHeader, "").trim();
Log.d(SpringAR.dataDebugLogPrefix, hostIpAdressAsString);
hostIpAddress = InetAddress.getByName(hostIpAdressAsString);
//Set Connection from broadcast to target
ARDeviceAddress = InetAddress.getByName(comonUtils.getIPAddress(true));
Log.d(SpringAR.protocollDebugLogPrefix, " New Device Adress " + ARDeviceAddress);
senderSocket = getSocket(senderSocket, ARDeviceAddress, SpringAR.UDP_SERVER_PORT, hostIpAddress);
listenerSocket = senderSocket;
State = SpringAR.comStates.STATE_sendCFG;
return;
}
setSendToSpringMessage(SpringAR.sendBroadcasteHeader);
delayByMs(SpringAR.TIME_OUT_IN_BROADCAST);
return;
}
case STATE_sendCFG: {
if ( SpringAR.STRING_NOT_FOUND != comonUtils.indexOf(payload[writeBuffer], SpringAR.recieveCFGHeaderByte )) {
State = SpringAR.comStates.STATE_sendRecieveData;
return;
}
setSendToSpringMessage(SpringAR.formConfigurationMessage());
return;
}
case STATE_sendRecieveData: {
if ( SpringAR.STRING_NOT_FOUND != comonUtils.indexOf(payload[writeBuffer], SpringAR.recieveDataHeaderByte)) {
writeRecievedDataToBuffer(payload[writeBuffer], rcv_packet.getLength());
}
break;
}
default:
Log.d(SpringAR.protocollDebugLogPrefix, "Connection State Machine invalid state");
}
}
https://github.com/PicassoCT/arcore-android-sdk/blob/6c9b48a3d520e039cd48bc2af7354ccdec857736/arcore-android-sdk/samples/hello_ar/app/src/main/java/com/google/ar/core/examples/app/common/tcpClient/Server.java
All the testing is happening in a home-WiFi Setup, where the desktop with the host-application is directly attached to the WiFi-Router.
What is working thus far:
The device can broadcast its presence.
The host can broadcast its configuration.
The Device can not communicate from IP to IP on the host. Both sides have fixed IP set.
I can communicate with the App PacketSender with the host-Application, and ruled a failure on its part out.
I also built a smaller debug-loop, to only send udp-packets back and forth, which also worked.
Thank you for your time
Change this line:
socket.setSoTimeout(30);
to
socket.setSoTimeout(1000);
You've got a fairly complex state machine going here, and it's difficult to discern what is happening without having logs to look at. I would summarize your state machine like this:
Broadcast a configuration message
Spend 30ms listening for a response
If no response is received, block for SpringAR.TIME_OF_FRAME_IN_MS (not included in your code; I assume it is 1000ms), and loop back to #1
If a response was received, send a reply directly to the peer, and go to #2
#4 is the step that isn't happening. The likely reason (based on the Wireshark dump) is that it's taking 68ms for ARDevice's response to reach "Host". You only gave it 30ms. There could be a number of reasons it's taking so long, but that's beyond the scope of your question.
I i am making a server/client but there seems to be a problem. I cannot seem to connect when i click the button.Please help.Not sure what i did wrong.Feel free to edit code to fix it then comment please.I have a connect button,and a send button. I think it has something to do with the highlighted code but it could be anything. I know this isnt very specific but basically heres the code and it doesnt work. I cant connect . please help!
Client
public class chat_client extends javax.swing.JFrame {
String username;
Socket sock;
BufferedReader reader;
PrintWriter writer;
ArrayList<String>userList = new ArrayList();
Boolean isConnected = false;
public chat_client() {
initComponents();
getContentPane().setBackground(Color.white);
this.setIconImage(new ImageIcon(getClass()
.getResource("dogeIcon.jpg")).getImage());
this.setLocationRelativeTo(null);
}
public class IncomingReader implements Runnable{
public void run(){
String stream;
String[] data;
String done = "Done", connect = "Connect",
disconnect = "Disconnect", chat = "Chat";
try {
while ((stream = reader.readLine()) != null){}
data = stream.split("^");
if (data[2].equals(chat)){
txtChat.append(data[0] + ":" + data[1] + "\n");
} else if (data[2].equals(connect)){
txtChat.removeAll();
userAdd(data[0]);
} else if (data[2].equals(disconnect)){
userRemove(data[0]);
} else if (data[2].equals(done)){
userList.setText("");
writeUsers();
}
} catch(Exception ex){
}
}
}
public void ListenThread(){
Thread IncomingReader = new Thread(new IncomingReader());
IncomingReader.start();
}
public void userAdd(String data){
userList.add(data);
}
public void userRemove(String data){
txtChat.append(data + " has disconnected \n");
}
public void writeUsers(){
String[] tempList = new String[(userList.size())];
userList.toArray(tempList);
for (String token:tempList){
userList.append(token + "\n");
}
}
public void sendDisconnect(){
String bye = (username + "^ ^Disconnected");
try{
writer.println(bye);
writer.flush();
} catch(Exception e){
txtChat.append("Could Not Send Disconnect Message \n");
}
}
public void Disconnect(){
try{
txtChat.append("Disconnected\n");
sock.close();
} catch(Exception ex){
txtChat.append("Failed to disconnect\n");
}
isConnected = false;
txtUser.setEditable(true);
userList.setText("");
}
(This is the highlighted part where i think the problem is)
***private void connectActionPerformed(java.awt.event.ActionEvent evt) {
if (isConnected == false){
username = txtUser.getText();
txtUser.setEditable(false);
try{
sock = new Socket("localhost", 1023);
InputStreamReader streamreader
= new InputStreamReader(sock.getInputStream());
reader = new BufferedReader(streamreader);
writer = new PrintWriter(sock.getOutputStream());
writer.println(username + "^has connected.^Connect");
writer.flush();
isConnected = true;
} catch(Exception ex){
txtChat.append("Cannot Connect! Try Again\n");
txtUser.setEditable(true);
}
ListenThread();
} else if (isConnected == true){
txtChat.append("You is connected bra\n");
}
}***
(Ends here-the problem/highlighted part)
private void btn_SendActionPerformed(java.awt.event.ActionEvent evt) {
String nothing = "";
if ((txtMsg.getText()).equals(nothing)){
txtMsg.setText("");
txtMsg.requestFocus();
} else {
try{
writer.println(username + "^" + txtMsg.getText() + "^"
+ "Chat");
writer.flush();
} catch (Exception ex){
txtChat.append("Message was not sent\n");
}
txtMsg.setText("");
txtMsg.requestFocus();
}
A couple things:
You're getting a java.net.ConnectionException (see below) because the connection is being refused. This could be because the server you are trying to connect to is not running, the server is not accepting client connections, the server is not accessible by the client, or you are connecting to the wrong port number.
It is generally bad coding practice to catch Exception directly. You want to either catch the most specific exception that ranges across the variety of exceptions that can be thrown (in this case, IOException) or catch each possible one individually, which is the preferred method. Catch the most specific exceptions before the more general ones so that they are not masked by them. Furthermore it is a good idea to use the Throwable class's getMessage() method so that you can figure out the reason for the exception being thrown. For example:
} catch (java.net.ConnectException ex) {
System.err.println("ConnectException: " + ex.getMessage()); // May return "Connection refused", "Connection timed out", "Connection reset", etc.
} catch (java.rmi.UnknownHostException ex) {
System.err.println("UnknownHostException: " + ex.getMessage()); // Returns the name of the host you were attempting to connect to
} catch (...) {
// code here
} catch (java.io.IOException ex) {
System.err.println("IOException: " + ex.getMessage()); // May return a problem with the BufferedReader or InputStreamReader or PrintWriter
}
Of course, the statements in the catch clause can be modified to your liking.