Sending data from Java server to ESP8266 - java

This is my first post here so bear with me if I make any mistakes...
Okay so I'm simply trying to have a Java program running on my computer send information to an ESP8266 running the arduino software. First off, here's the code.
ESP8266(Arduino):
#include <ESP8266WiFi.h>
#define NAME "********"
#define PASS "********"
const char* host = "192.168.1.6";
WiFiClient client;
void setup() {
Serial.begin(115200);
Serial.println();
WiFi.begin(NAME, PASS);
Serial.print("Connecting");
while(WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();
Serial.print("Connected, IP address: ");
Serial.println(WiFi.localIP());
if (client.connect(host, 9090)) {
Serial.print("Connected to: ");
Serial.println(host);
String data = "No Message";
client.print("ESP8266 connected!");
if(client.available()) {
data = client.readStringUntil('\n');
}
Serial.print("Host message: ");
Serial.println(data);
client.stop();
} else {
client.stop();
}
}
void loop() {
}
Java Server:
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws IOException {
ServerSocket listener = new ServerSocket(9090);
try{
while(true){
Socket socket = listener.accept();
socket.setKeepAlive(true);
try{
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
out.write("Hello from Java!\n");
} finally {
socket.close();
}
}
} finally {
listener.close();
}
}
}
The problem is that client.available() never returns true, even when information is sent by the Java server to the client. I have tried sending data from the Arduino to the server and that works just fine. I also made a quick Java program that could run as a client and ran this on another computer on the network. The Java server and client in this case communicated just fine. So the problem lies somewhere in the Arduino, but I'm at a loss as to what it is.

The issue here appears to have been with
client.print()
Replacing that with
client.println()
Fixed my issue.
I believe this was somehow stalling the input buffer server side as it was waiting for a newline but never getting one. I'll post the updated code that works below for any having this issue.
ESP8266:
#include <ESP8266WiFi.h>
#define NAME "********"
#define PASS "********"
const char* host = "10.0.0.15";
WiFiClient client;
void setup() {
Serial.begin(115200);
Serial.println();
/* Set Client up as station */
WiFi.mode(WIFI_STA);
WiFi.begin(NAME, PASS);
/* Connect to the network */
Serial.print("Connecting");
while(WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println();
Serial.print("Connected, IP address: ");
Serial.println(WiFi.localIP());
}
void loop() {
if (client.connect(host, 9090))
{
Serial.print("Connected to: ");
Serial.println(host);
/* Send "connected" to the server so it knows we are ready for data */
client.println("deviceconnected"); //USE client.println()!!
Serial.println("Host message: \n");
/* Wait for data for 5 seconds at most before timing out */
unsigned long timeout = millis();
while(client.available() == 0)
{
if(millis() - timeout > 5000)
{
Serial.println("Timeout to server!");
break;
}
}
/* Read in the data in the stream */
while(client.available() > 0)
{
Serial.println(client.readStringUntil('\n'));
}
client.stop();
}
else
{
client.stop();
}
delay(5000);
}
Java Server:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws IOException {
ServerSocket listener = new ServerSocket(9090);
try{
while(true){
Socket socket = listener.accept();
socket.setKeepAlive(true);
System.out.println("Client Connected");
try{
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println("Client response: " + in.readLine());
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
System.out.println("Sending Message...");
out.write("Hello\n from Java!\n");
out.flush();
} finally {
socket.close();
}
}
} finally {
listener.close();
}
}
}

That part of your code belongs to the loop() event:
if (client.connect(host, 9090)) {
Serial.print("Connected to: ");
Serial.println(host);
String data = "No Message";
client.print("ESP8266 connected!");
if(client.available()) {
data = client.readStringUntil('\n');
}
Serial.print("Host message: ");
Serial.println(data);
client.stop();
} else {
client.stop();
}
The setup() function is run once only. After it moves on to loop() and stays there. And your loop() function is empty...

I had the same problem, here is my solution:
Java only updates the outputstream when you add out.flush(); after out.write();
Also
client.available()
is returning 0 until you read something. For example
String first_line = client.readStringUntil('\n');
After this you can use the available() method.
Test with the String "Hello,\nthis is a text\n":
client.println(client.available()); // 0
String first_line = client.readStringUntil('\n');
client.println(first_line); // Hello,
client.println(client.available()); // 17
To send all messages back to the client:
client.print("First: ");
client.println(first_line);
while(client.available() > 0) {
client.println("Next: " + client.readStringUntil('\n'));
}
Output:
First: Hello,
Next: this is a text
Next:

Related

Java TCP client repetitive connections result in EMFILE error

My Java application establishes TCP connection with a server and communicates with it every second by sending and receiving messages. Both server and client are run on the same Mac. In about 15-20 minutes, my server crashes with error "Errno::EMFILE Too many files open". Here is my client code:
package testtcp;
import java.awt.Dimension;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class TestTCP extends JPanel
{
JFrame frame = new JFrame("Button Demo");
ScheduledExecutorService executorService;
private Socket socket = null;
private DataInputStream input = null;
private DataOutputStream output = null;
private BufferedReader br = null;
private boolean isMapUpdating = false;
public TestTCP()
{
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setPreferredSize(new Dimension(300,300));
frame.add(this);
JButton b1 = new JButton("BLACK");
b1.setPreferredSize(new Dimension(150,50));
b1.setFocusPainted(false); // get rid of border around text
add(b1);
b1.addActionListener((java.awt.event.ActionEvent evt) ->
{
startAcarsConnection();
});
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public void startAcarsConnection()
{
start();
}
public void start()
{
System.out.println("THREAD START");
// Default timer rate
int timerRate = 1;
executorService = Executors.newSingleThreadScheduledExecutor();
executorService.scheduleAtFixedRate(new Runnable()
{
#Override
public void run()
{
// Create new TCP connection if the map is not currently updating
if(isMapUpdating == false)
{
isMapUpdating = true;
communicateWithServer();
}
}
}, 0, timerRate, TimeUnit.SECONDS);
}
public void stop()
{
executorService.shutdown();
}
public void communicateWithServer()
{
// Create a message to the server
String messageToServer = makeMessageToServer();
// Connect to the client and receive the response
String messageFromServer = connectToClient(messageToServer);
SwingUtilities.invokeLater(() ->
{
messageReceived(messageFromServer);
});
}
public String connectToClient(String messageToServer)
{
String data = "";
// Message from the server that should terminate TCP connection
String terminator = "END_IGOCONNECT_DATA";
try
{
// Create socket and streams
socket = new Socket("192.168.1.2", 7767);
input = new DataInputStream( socket.getInputStream());
output = new DataOutputStream( socket.getOutputStream());
//Send message to the server
output.writeBytes(messageToServer);
System.out.println("MESSAGE TO SERVER FROM CONNECT TO CLIENT: "+messageToServer);
//Read Response
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
StringBuilder sb = new StringBuilder();
String s = "";
int value;
// Process the message from the server and add to the StringBuilder
while((value = br.read()) != -1)
{
// converts int to character
char c = (char)value;
sb.append(c);
if(sb.toString().contains(terminator))
{
break;
}
}
// Create the final string
data = sb.toString();
}
catch (UnknownHostException e)
{
System.out.println("Sock:"+e.getMessage());
// Close Connection
cancelConnection();
// Pop-up message that the airport was not found
String message = "Application was not able to establish connection with X-Plane.\n"
+ "Check whether IP Address and Port number were correctly entered in Settings.\n"
+ "Check whether connection is not being blocked by your firewall.";
JOptionPane.showMessageDialog(new JFrame(), message, "TCP Connection Error: UnknownHostException",
JOptionPane.ERROR_MESSAGE);
data = "ERROR";
}
catch (EOFException e)
{
System.out.println("EOF:"+e.getMessage());
// Close Connection
cancelConnection();
// Pop-up message that the airport was not found
String message = "Application was not able to establish connection with X-Plane.\n"
+ "Check whether IP Address and Port number were correctly entered in Settings.\n"
+ "Check whether connection is not being blocked by your firewall.";
JOptionPane.showMessageDialog(new JFrame(), message, "TCP Connection Error: EOFException",
JOptionPane.ERROR_MESSAGE);
data = "ERROR";
}
catch (IOException e)
{
System.out.println("IO:"+e.getMessage());
// Close Connection
cancelConnection();
// Pop-up message that the server was not found
if(!e.getMessage().equals("Socket closed"))
{
String message = "Application was not able to establish connection with X-Plane.\n"
+ "Check whether IP Address and Port number were correctly entered in Settings.\n"
+ "Check whether connection is not being blocked by your firewall.";
JOptionPane.showMessageDialog(new JFrame(), message, "TCP Connection Error: IOException",
JOptionPane.ERROR_MESSAGE);
}
// "Connection reset"
data = "ERROR";
}
finally
{
// TO DO!!! DISABLED FOR NOW!! closeSocketPax();
}
return data;
}
public void cancelConnection()
{
executorService.shutdown();
closeSocketPax();
SwingUtilities.invokeLater(() ->
{
System.out.println("Cancel Connection");
});
}
private void closeSocketPax()
{
try
{
if(socket!=null) { socket.close();}
if(input != null) { input.close();}
if(output != null) { output.close();}
if(br != null) { br.close();}
}
catch (IOException ex)
{
String message = "Error closing socket.";
JOptionPane.showMessageDialog(new JFrame(), message, "TCP Connection Error: IOException",
JOptionPane.ERROR_MESSAGE);
}
socket = null;
input = null;
output = null;
br = null;
}
private String makeMessageToServer()
{
return "MESSAGE TO SERVER";
}
private void messageReceived(String message)
{
System.out.println("MESSAGE RECEIVED: "+message);
isMapUpdating = false;
}
public static void main(String[] args)
{
new TestTCP();
}
}
I have been trying to solve this for almost a month already!
Does anyone see a problem in the code and know how to mitigate the problem? Greatly appreciated!
Each connection you create uses a file descriptor. In any operating system there is a limit to the number of descriptors your process can have. For example, in the Linux environment I'm on the limit is 1024. Different O/S's have different limits but in Unix derived environments like Linux and Mac O/S you can run ulimit -n to see what the limit is.
In your code you do:
socket = new Socket("192.168.1.2", 7767);
in the connectToClient method. Each time you do that and you don't close the socket you use up a file descriptor. Eventually you reach the O/S defined limit and you get, in Mac O/S the Errno::EMFILE error.
You have two choices to fix this. The first is pretty much what you have commented out - close the connection when you're done with it. However, as you indicate in the comments this is occurring very frequently and you don't want to incur the overhead of opening and closing constantly.
That brings us to the second choice - reuse the connection. A socket can send data over and over again if the protocol you're designing handles it. Send the data back and forth over the protocol and reuse the Socket.
A warning though - if your physical connection is somehow severed - for example, you switch from Ethernet to Wi-Fi - your service will still need to deal with possible errors. Your code has most of that but you may want to consider closing and attempting to reconnect when this occurs.

Getting error in client-server programming

I have created client Server program in java. While I run program I should get port number and IP address but I am getting an error while I run Client.java. Below is my both files.
Server.java
package serverpro;
import java.io.*;
import static java.lang.ProcessBuilder.Redirect.to;
import java.net.*;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Server extends Thread {
public static final int PORT_NUMBER = 12345;
protected Socket socket;
public static void main(String[] args) {
ServerSocket server = null;
try {
server = new ServerSocket(PORT_NUMBER);
while (true) {
new Server(server.accept());
}
}
catch(IOException ex) {
System.out.println("Unable to start server or acccept connections ");
System.exit(1);
}
finally {
try {
server.close();
}
catch(IOException ex) {
// not much can be done: log the error
// exits since this is the end of main
}
}
}
private Server(Socket socket) {
this.socket = socket;
start();
}
// the server services client requests in the run method
public void run() {
InputStream in = null;
OutputStream out = null;
BufferedReader inReader = new BufferedReader(newInputStreamReader(in));
// the constructor argument “true” enables auto-flushing
PrintWriter outWriter = new PrintWriter(out, true);
outWriter.println("Echo server: enter bye to exit.");
//outWriter.println(“Echo server: enter ‘bye’ to exit.”);
while (true) {
// readLine blocks until a line-terminated string is available
String inLine;
try {
inLine = inReader.readLine();
} catch (IOException ex) {
Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
}
// readLine returns null if the client just presses <return>
try {
in = socket.getInputStream();
out = socket.getOutputStream();
// ... do useful stuff ...
}
catch(IOException ex) {
System.out.println("Unable to get Stream from ");
}
finally {
try {
in.close();
out.close();
socket.close();
}
catch(IOException ex) {
// not much can be done: log the error
}
}
}
}
}
Client.java
package serverpro;
import java.io.*;
import java.net.*;
public class Client {
public static void main(String args[]) throws IOException {
new Client(args[0]);
}
public Client(String host) throws IOException {
Socket socket;
try {
socket = new Socket(host, Server.PORT_NUMBER);
}
catch(UnknownHostException ex) {
System.out.println(host + " is not a valid host name.");
return;
}
catch(IOException ex) {
System.out.println("Error connecting with" + host);
return;
}
// … initialize model, GUI, etc. ...
InputStream in = null;
OutputStream out = null;
try {
in = socket.getInputStream();
out = socket.getOutputStream();
// ... do useful stuff ...
}
finally {
try {
in.close();
out.close();
socket.close();
}
catch(IOException ex) {
// not much can be done ...
}
}
}
}
Here is the error code I am getting while running client.java file
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 1
at serverpro.Client.main(Client.java:13)
/Users/Puja Dudhat/Library/Caches/NetBeans/8.2/executor- snippets/run.xml:53: Java returned: 1
BUILD FAILED (total time: 0 seconds)
Your code expects one argument passed into the main method, which appears to be your client port, stored at args[0]. Therefore, you have to provide one to the main method. An example for setting port=12345:
java Server 12345
If you'd need more arguments, (e.g. a value at args[1]), then simply add another argument when launching main:
java Server 12345 secondArg
Assuming you are not passing required command-line argument. When I ran this code it did run fine, provided the argument required is passed or hard-coded; namely:
public static void main(**String args[]**) throws IOException {
new Client(**args[0]**);
}
if you are running both server and client on same machine then you can pass localhost as command line argument
java Client localhost
Alternatively, you can hard code host value(note : this is not good practice though),
public static void main(String args[]) throws IOException {
new Client("localhost");
}
Also as a suggestion, you can use ide like eclipse or intellij to debug your code step by step. you can go through online video tutorials for java and many are available on youtube

Get full URL path from browser with using ServerSocket and Socket classes

I'm trying write my individual HTTP Server and I need a help .
What is the method of ServerSocket or Socket class can to invoke on the URL and brining it into a code.
For example, if I write following link <b>http://localhost:8080/coupon/add?name=coupon name</b> in browser, I would be want to get this link into my code.
Maybe who know how can I do this?
my simple code:
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class HTTPServer {
public static void main(String[] args) {
new HTTPServer().startServer();
}
public void startServer() {
try (ServerSocket serverSocket = new ServerSocket(8080)) {
System.out.println("Server is started");
while (true) {
Socket socket = serverSocket.accept();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Thanks
All your code does right now is set up a TCP server.
HTTP is a Layer 7 protocol.
Once you accept the connection from the client, HTTP can be used for communication over that TCP socket.
You'd have to parse the HTTP request that the client sends, and from that, you'd know the URL.
In your case, you said:
I write following link http://localhost:8080/coupon/add?name=coupon name in browser
Your browser will send an HTTP request like the following example:
GET /coupon/add?name=coupon+name HTTP/1.0
Host: localhost:8080
In reality, there will be more HTTP headers there, as well as a trailing \r\n, but for our sake, let's keep it simple.
Note that special characters like space are URL-encoded, however space is also encoded as + in the query string - it could be either + or %20 depending on the client.
Hopefully it's reasonably clear to you from this explanation how you get the URL from this HTTP request.
The only missing part from the actual full link is the http:// part. The distinction between HTTP and HTTPS is not part of the HTTP protocol - it's above the socket layer but below the HTTP protocol layer. If you had SSL sockets, you'd know that on the server side, and determine based on whether it was an SSL socket or a "plain" socket, whether it was http or https.
Hope that helps.
I improved for testing the startServer method for getting information.
I'm trying to include the data that comes from URL from any browsers to URI or URL class of JAVA.
This impossible ? Maybe who know how can I do this ?
public void startServer() {
try (ServerSocket serverSocket = new ServerSocket(8080)) {
System.out.println("Server is started");
while (true) {
Socket socket = serverSocket.accept();
System.out.println("SERVER SOCKET TESTS:");
System.out.println("getChannel: " + serverSocket.getChannel());
System.out.println("getInetAddress: " + serverSocket.getInetAddress());
System.out.println("getLocalPort: " + serverSocket.getLocalPort());
System.out.println("getLocalSocketAddress: " + serverSocket.getLocalSocketAddress());
System.out.println();
System.out.println("CLIENT SOCKET TESTS:");
System.out.println("getChannel: " + socket.getChannel());
System.out.println("getLocalAddress: " + socket.getLocalAddress());
System.out.println("getLocalPort: " + socket.getLocalPort());
System.out.println("getLocalSocketAddress: " + socket.getLocalSocketAddress());
System.out.println("getRemoteSocketAddress: " + socket.getRemoteSocketAddress());
System.out.println("getInetAddress: " + socket.getInetAddress());
System.out.println("getInputStream: " + socket.getInputStream());
System.out.println("getOutputStream: " + socket.getOutputStream());
System.out.println();
System.out.println("URI - GET INFORMATION:");
URI uri = new URI("httpscheme://world.hello.com/thismy?parameter=value");
System.out.println(uri.getHost());
System.out.println(uri.getPath());
System.out.println(uri.getQuery());
System.out.println(uri.getScheme());
}
} catch (Exception e) {
System.out.println("error");
}
}
little test:
when I run code and after that open the browser and I write in my browser, for example: http://localhost:8080 I get information, but I don't understand following:
why the serverSocket object in getInetAddress method (serverSocket.getInetAddress) have an IP4 and it 0.0.0.0 (why not a standard local ip that defined on my computer) and the socket object of getInetAddress method (socket.getInetAddress) have an IP6 and it 0:0:0:0:0:0:0:1 . How can I get a standard host name localhost how to get the URI class (with chunks of data of link)?
The port is gated nice: 8080.
The problem for getting URL path , solved.
package pk6HttpServer;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.MalformedURLException;
import java.net.ServerSocket;
import java.net.Socket;
/**
* Created by Morris on 08/10/16.
*/
public class HTTPServer {
private static String headerData;
public static void main(String[] args) {
new HTTPServer().startServer();
}
public void startServer() {
try (ServerSocket serverSocket = new ServerSocket(8080)) {
boolean isClosed = false;
System.out.println("Server is started");
while (true) {
Socket socket = serverSocket.accept();
try {
try (InputStream raw = socket.getInputStream()) { // ARM
System.out.println("=================BEFORE STARTING READING HEADER =======================");
System.out.println("Collecting data to string array...");
headerData = getHeaderToArray(raw);
//
System.out.println("+++++++++++++++++ AFTER ENDING READING HEADER +++++++++++++++++++++++");
System.out.println("Hello World");
}
} catch (MalformedURLException ex) {
System.err.println(socket.getLocalAddress() + " is not a parseable URL");
} catch (IOException ex) {
System.err.println(ex.getMessage());
}
}
} catch (Exception ex) {
System.out.println("error# " + ex.getMessage());
}
}
public static String getHeaderToArray(InputStream inputStream) {
String headerTempData = "";
// chain the InputStream to a Reader
Reader reader = new InputStreamReader(inputStream);
try {
int c;
while ((c = reader.read()) != -1) {
System.out.print((char) c);
headerTempData += (char) c;
if (headerTempData.contains("\r\n\r\n"))
break;
}
} catch (IOException ex) {
System.err.println(ex.getMessage());
}
headerData = headerTempData;
return headerTempData;
}
}

Java TCP Multi-thread issue

My program works fine and many users can connect and send commands to the server.
But when a user spams the server with commands the server blocks out all other clients and the server doesn't receive messages from clients other than the one that spammed.
Why is this?
TCPAccept Connections
package game.server;
import java.io.IOException;
import java.net.Socket;
public class TCPAcceptConnections implements Runnable
{
public static Socket clientSocket = null;;
int clientID = -1;
public void run()
{
while(Main.TCP)
{
try
{
clientSocket = TCPServer.serverSocket.accept();
System.out.println("Client Connected.");
clientID++;
new TCPClientManager(clientSocket, clientID).run();
}
catch (IOException e)
{
System.out.println("Couldn't create client socket.");
System.exit(-1);
}
}
}
}
TCPClientManager:
package game.server;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class TCPClientManager implements Runnable
{
Socket client;
int clientID;
static PrintWriter out;
static BufferedReader in;
String inputLine, outputLine;
boolean destroy = false;
public TCPClientManager(Socket cs, int id)
{
try
{
client = cs;
clientID = id;
out = new PrintWriter(client.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(client.getInputStream()));
} catch(IOException e)
{
e.printStackTrace();
}
}
public void run()
{
System.out.println("Created TCPManager for client.");
String command;
while(!destroy)
{
try
{
if((command = in.readLine()) != null) //If received something
{
System.out.println("Commad received: " + command);
System.out.println(" " + Commands.proccessCommand(command));
System.out.println("Command proccessed");
}
else
{
client.close();
destroy = true;
}
} catch (IOException e)
{
try
{
client.close();
} catch (IOException e1)
{
e1.printStackTrace();
destroy = true;
}
System.out.println("Client lost connection.");
destroy = true;
}
}
System.out.println("TCPManager for client destroyed.");
}
}
Commands:
package game.server;
public class Commands
{
public static String proccessCommand(String command)
{
if(command.equalsIgnoreCase("cp"))
{
System.out.println("Creating player...");
System.out.println(" Retrieved client");
return "Player Created";
}
else
{
return "Unkown command: " + command;
}
}
}
If you get an unknown command, you should log it and close the connection.
But you have a more severe problem. You aren't stopping the client handler when it reads null. So once a client disconnects the read will spin futilely forever. If readLine() returns null you must close the socket and exit the loop. If you get any IOException you must also close the socket.

Create an Object, pass the Object to another Object's constructor, call wait() on Object, then notify() in Java

I'm trying to handle multiple connections on the same port of my server. I'm doing this by instantiating an Object and passing it into the constructor for another class, which implements Runnable. Then I set up a socket in the Runnable class and call notify() on the passed Object after a Client connects on the port. This should then allow the server to restart its loop, creating another instance of the Runnable class after being notified. However, currently the wait() isnt being reached until after the client is closed. Here are the 3 relevant classes I have:
Server class:
package server;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.HashMap;
public class Server {
public static void main(String[] args){
HashMap<String, PortDummy> portDummies = new HashMap<String, PortDummy>();
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(8000);
} catch (IOException e1) {
e1.printStackTrace();
}
for(;;){
Object block = new Object();
PortDummy dummy = new PortDummy(serverSocket, block, portDummies);
System.out.println("Running dummy port...");
dummy.start();
try {
synchronized(block){
System.out.println("Waiting...");
block.wait();
System.out.println("Block notified.");
}
} catch (InterruptedException e) {
System.out.println("Can't be interrupted!");
e.printStackTrace();
}
}
}
}
PortDummy (Runnable) class:
package server;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
public class PortDummy extends Thread {
private Object block;
private HashMap<String, PortDummy> portDummies;
private String clientName = null;
ServerSocket serverSocket;
BufferedReader socketIn;
PrintWriter socketOut;
public PortDummy(ServerSocket serverSocket, Object block, HashMap<String, PortDummy> portDummies){
this.block = block;
this.portDummies = portDummies;
this.serverSocket = serverSocket;
}
#Override
public void run() {
try {
System.out.println("Starting dummy port...");
Socket clientSocket = serverSocket.accept();
System.out.println("Connection made.");
synchronized(block){
System.out.print("Notifying...");
block.notify();
System.out.println("...done.");
}
socketIn = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
socketOut = new PrintWriter(clientSocket.getOutputStream(), true);
String inContent;
boolean loggedIn = false;
while((inContent = socketIn.readLine()) != null){
socketOut.println("Server Echo: " + inContent);
if(inContent.startsWith("/exit")){
if(loggedIn){
portDummies.remove(clientName);
System.out.println(clientName + " signed out. Removed from portDummies.");
}
else{
System.out.println("Closing...");
}
}
else if(inContent.startsWith("/register")){
System.out.println("/register accepted");
if(!loggedIn){
if(registerUser(inContent)){
System.out.println("Successfully registered.");
socketOut.println(clientName + " successfully registered.");
loggedIn = true;
}else{
socketOut.print("That user already exists.");
}
}
else{
socketOut.print("Already logged in.");
}
}
else if(inContent.startsWith("/tell")){
if(!loggedIn){
socketOut.println("You need to log in.");
}
else{
String[] parts = inContent.split("\\w");
String[] withoutCommand = new String[parts.length-1];
for(int i = 1; i<parts.length-1; i++){
withoutCommand[i] = parts[i];
}
String[] messageParts = new String[withoutCommand.length-1];
String message = "";
for(int j = 1; j<withoutCommand.length-1; j++){
message += withoutCommand[j] + " ";
}
String recipient = withoutCommand[0];
sendMessage(recipient, message);
}
}
else if(inContent.startsWith("/help")){
socketOut.print("/help ~~~~~~~ List all commands. \n " +
"/register <username> ~~~~~~~ Register a new user with 'username'. \n " +
"/tell <username> <message> ~~~~~~~ Send 'username' text 'message'. \n " +
"/exit ~~~~~~~ Log out.");
}
}
System.out.println("Shutting down client connections...");
socketOut.close();
socketIn.close();
clientSocket.close();
serverSocket.close();
} catch (IOException e) {
System.out.println("IOException!");
e.printStackTrace();
}
}
private boolean registerUser(String text){
System.out.println("Registering user...");
String user = text.substring(10);
if((user != null) && !(portDummies.containsKey(user))){
portDummies.put(user, this);
clientName = user;
System.out.println(user + " registered.");
return true;
}
return false;
}
private void sendMessage(String username, String message){
if(portDummies.containsKey(username)){
PortDummy recip = portDummies.get(username);
recip.getSocketOutput().println(clientName + ": " + message);
}
else{
socketOut.write("User " + username + " doesn't exist.");
}
}
public PrintWriter getSocketOutput(){
return socketOut;
}
}
Client class:
package client;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
import java.io.IOException;
public class Client {
protected String username;
public static void main(String[] args){
try{
Socket serverSocket = new Socket("localhost", 8000);
BufferedReader socketIn = new BufferedReader(new InputStreamReader(serverSocket.getInputStream()));
PrintWriter socketOut = new PrintWriter(serverSocket.getOutputStream(), true);
Scanner keyboardInputScanner = new Scanner(System.in);
String keyboardInput, serverInput;
System.out.println("Welcome to Chris Volkernick's Server IM Client! \n" +
"Type '/register <username>' to register, '/list' to list connected users," +
"\n or '/tell <username> <message>' to send a user a message. '/help' lists these commands. (Type '/exit' to sign out.)");
while((keyboardInput = keyboardInputScanner.nextLine()) != null){
System.out.println("Input '" + keyboardInput + "' read on client side.");
if(keyboardInput.equals("/exit")){
socketOut.println("/exit");
socketOut.close();
socketIn.close();
serverSocket.close();
}else{
socketOut.println(keyboardInput);
while((serverInput = socketIn.readLine()) != null){
System.out.println(serverInput);
}
}
}
keyboardInputScanner.close();
}catch(IOException e){
System.out.println("IOException!");
e.printStackTrace();
}
}
}
Am I doing something wrong with the wait() and/or notify()?
EDIT: I also tried changing the implements Runnable to extends Thread then changing the .run() in the server to .start(), but that gives me this error:
java.net.BindException: Address already in use: JVM_Bind
at java.net.PlainSocketImpl.socketBind(Native Method)
at java.net.PlainSocketImpl.bind(PlainSocketImpl.java:365)
at java.net.ServerSocket.bind(ServerSocket.java:319)
at java.net.ServerSocket.<init>(ServerSocket.java:185)
at java.net.ServerSocket.<init>(ServerSocket.java:97)
at server.PortDummy.run(PortDummy.java:28)
EDIT 2: It seems to be working the way it should now in terms of starting new threads. However, I'm now running into another problem: After I enter a command on the client side of any given client, I can't enter additional commands. The first command will work fine (minus /exit; haven't quite figured out how that should work yet), just can't do anything after that. For example, I can register (sign in) but after that nothing else. I can go into another instance of Client and list all the current users (works), but again, after that I cannot enter additional commands. Any idea what may be happening to cause this?
The problem is that your child threads are trying to listen on port 8000, but the parent thread is already doing that. What you need to do is pass accept a connection from the original socket and then give it to the child thread. I'm not exactly sure how to do this in Java, but I suspect it's just..
Put this in your main thread:
ServerSocket serverSocket = new ServerSocket(8000);
Socket clientSocket = serverSocket.accept();
And then once you get that, pass clientSocket to your Thread.
That way there's only one socket listening on port 8000, but you can make child threads handle each connection.
When using wait and notify, realize that notifies aren't queued, so if the notify happens before the wait occurs, you will never exit the wait. Therefore you should never perform naked waits, that is there should always be some condition you test to see if you should wait.
sychronized(block) {
while (!available) {
block.wait();
}
}
and
synchronized(block) {
available = true;
block.notifyAll();
}
etc
package so_7775790;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.logging.Logger;
/**
* barebones server -- ctl-C to kill it ;)
*/
public class Server implements Runnable {
final static Logger log = Logger.getLogger(Server.class.getSimpleName());
public static void main(String[] args) {
final int port = 8000;
final String tname = "my-server-thread";
final Server server = new Server(port);
try {
Thread tserver = new Thread(server, tname);
tserver.start();
tserver.join();
} catch (Exception e) {
log.severe(e.getMessage());
}
}
// -------------------------------------------------
// server
// -------------------------------------------------
final int port;
public Server(int port) {
this.port = port;
}
public void run() {
try{
final ServerSocket srvsocket = new ServerSocket(port);
log.info(String.format("Server started # %s\n", srvsocket));
while(!Thread.currentThread().isInterrupted()){
Socket newclient = srvsocket.accept();
// spawn thread and hand off new client to handler
new Thread(new ClientHandler(newclient)).start();
}
}
catch (Exception e) {
log.severe(e.getMessage());
}
log.info("server stopped");
}
// -------------------------------------------------
// client handler
// -------------------------------------------------
static class ClientHandler implements Runnable {
final Socket socket;
public ClientHandler(final Socket socket) {
assert socket != null : "serverthread is null";
this.socket = socket;
}
#SuppressWarnings("unused")
#Override final
public void run() {
log.info(String.format("new client # %s\n", socket.getRemoteSocketAddress()));
try {
final InputStream in = socket.getInputStream();
final OutputStream out = socket.getOutputStream();
// NOTE: this is just a stub busy loop!
for(;;) {
/* your protocol impl here .. */
}
} catch (Exception e) {
log.severe(e.getMessage());
}
finally {
try {
socket.close();
}
catch (Exception e) {
log.severe(e.getMessage());
}
}
}
}
}

Categories