Java TCP client repetitive connections result in EMFILE error - java

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.

Related

Socket connection not workin in flutter release apk

I am new to working with sockets, and I am working on this project where a connection between my android flutter app and a java server is needed, to do this I am trying socket programming.
The server code is fairly simple, I create a new thread for every client connected and I give them a bunch of URLs, later on, this should be replaced by a query result. here is the java code:
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class CrawlitServer {
// The port number on which the server will listen for incoming connections.
public static final int PORT = 6666;
//main method
public static void main(String[] args) {
System.out.println("The server started .. ");
// Create a new server socket
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(PORT);
} catch (Exception e) {
System.out.println("Error: " + e.getMessage());
}
// Listen for incoming connections and create a new thread for each one
while (true) {
try {
new CrawlitServerThread(serverSocket.accept()).start();
}
catch (Exception e) {
System.out.println("Error: " + e.getMessage());
}
}
}
public static class CrawlitServerThread extends Thread {
private final Socket socket;
public CrawlitServerThread(Socket socket) {
this.socket = socket;
}
public void run() {
List<String> list = new ArrayList<>();
//assign a value to list
list.add("http://www.google.com");
list.add("http://www.yahoo.com");
list.add("http://www.bing.com");
list.add("http://www.facebook.com");
list.add("http://www.twitter.com");
list.add("http://www.linkedin.com");
list.add("http://www.youtube.com");
list.add("http://www.wikipedia.com");
list.add("http://www.amazon.com");
list.add("http://www.ebay.com");
list.add("http://stackoverflow.com");
list.add("http://github.com");
list.add("http://quora.com");
list.add("http://reddit.com");
list.add("http://wikipedia.org");
try {
// Get the input stream from the socket
DataInputStream inputStream = new DataInputStream(socket.getInputStream());
Scanner scanner = new Scanner(inputStream);
DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream());
PrintWriter writer = new PrintWriter(outputStream, true);
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
System.out.println("Received Message from client: " + line);
writer.println(list + "\n");
}
}
catch (Exception e) {
System.out.println("Error: " + e.getMessage());
}
}
}
}
Now I run this server and connect to it using sockets in Flutter, I give it the IP address I get from the ipconfig command, and here is the dart code:
import 'dart:async';
import 'dart:io';
//Utilities that manage connections with server sockets.
//ServerUtil Class
class ServerUtil {
static const port = 6666;
static const host = MY_IP_GOES_HERE;
static late Socket socket;
static bool connected = false;
//a list of urls returned by the server
static List<String> urls = [];
//Constructor
ServerUtil() {
//Initialize the socket.
Socket.connect(host, port).then((Socket sock) {
socket = sock;
connected = true;
socket.listen(dataHandler,
onError: errorHandler, onDone: doneHandler, cancelOnError: false);
//send a message to the server.
}).catchError((e) {
print("Unable to connect: $e");
});
}
//Query method that sends a message to the server. The server will return a list of urls.
//The urls will be added to the urls list.
//The urls list will be returned.
static Future<List<String>> query(String userQuery) async {
urls.clear();
//check if socket is connected.
if (connected) {
//send the query to the server.
socket.writeln(userQuery);
await Future.delayed(const Duration(milliseconds: 200));
print(urls);
return urls;
}
//if socket is not connected, wait for 5 seconds and try again.
await Future.delayed(const Duration(milliseconds: 50));
return query(userQuery);
}
//Handles data from the server.
void dataHandler(data) {
//String of received data.
String dataString = String.fromCharCodes(data).trim();
//remove first and last character from the string.
dataString = dataString.substring(1, dataString.length - 1);
//remove all the whitespace characters from the string.
dataString = dataString.replaceAll(RegExp(r'\s+'), '');
urls = dataString.split(',');
}
//Handles errors from the server.
void errorHandler(error, StackTrace trace) {
print(error);
}
//Handles when the connection is done.
void doneHandler() {
socket.destroy();
}
}
This works perfectly fine while using a debug apk running it on my real Note 9 device. The problem however is that when I build a release apk and try it out, nothing happens.
The way I set it up is that I wait for the query method in an async and then I send the result to a new screen and push that screen into the navigator.
But in the release apk nothing happens, the new screen doesn't load.
So this leads me to my first question:
Is there a way to debug a release apk? see what exceptions it throws or print some stuff to console?
I have the server running on my Laptop, and the app runs on my phone which is on the same WIFI network.
My second question is:
Do I need to enable some sort of option with my router or my laptop to allow my phone to connect? it does connect in debug mode without any modifications
I tried some random things, like using 'localhost' instead of my IP, as I would normally connect say with a java client for example, but it didn't work.
My last question is:
Does the release apk or like android OS prevent connections to local hosts, maybe because it thinks it is not secure? but then it still connects in debug mode.
Thank you for your time.

Java Publisher Sever chat program

I am trying to create a chat application which has one publisher, one server and multiple subscribers. The publisher(Sending to port 8000) sends a message to the server(listening on port 8000 and 5000) and which forwards it further to the subscriber(listening on port 5000).
Now so far I can create multiple publishers and the communication between server and publisher is working, however, I am not able to send it to the subscriber the message sent by the publisher
Server Side Code
package serverclient;
import java.io.*;
import java.net.*;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Server extends Thread{
private Socket socket;
private int clientNumber;
public Server(Socket socket, int clientNumber){
this.socket = socket;
this.clientNumber = clientNumber;
if(socket.getLocalPort() == 5000)System.out.print("\nSubscriber "+ clientNumber +" is connected to the server");
if(socket.getLocalPort() == 8000)System.out.print("\nPublisher "+ clientNumber +" is connected to the server");
}
#Override
public void run(){
try {
BufferedReader dStream = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
while(true){
synchronized(this){
String clMessage = dStream.readLine();
System.out.println("\n"+clMessage);
// if(socket.getLocalPort() == 5000){
out.println("Hey the server is sending the message to subscriber");
// }
//out.println("Hey the publisher has sent the message : " + clMessage);
}
}
} catch (IOException ex) {
System.out.print("\nError has been handled 1\n");
}finally{
try {
socket.close();
} catch (IOException ex) {
System.out.print("\nError has been handled 2\n");
}
}
}
public static void main(String [] args) throws IOException{
int subNumber = 0;
int pubNumber = 0;
ServerSocket servSockpub = new ServerSocket(8000);
ServerSocket servSocksub = new ServerSocket(5000);
try {
while (true) {
Server servpub = new Server(servSockpub.accept(),++pubNumber);
servpub.start();
System.out.print("\nThe server is running on listen port "+ servSockpub.getLocalPort());
Server servsub = new Server(servSocksub.accept(),++subNumber);
servsub.start();
System.out.print("\nThe server is running on listen port "+ servSocksub.getLocalPort());
}
} finally {
servSockpub.close();
servSocksub.close();
}
}
}
publisher code
package serverclient;
import java.net.*;
import java.io.*;
public class Publisher {
public static void main (String [] args) throws IOException{
Socket sock = new Socket("127.0.0.1",8000);
// reading from keyboard (keyRead object)
BufferedReader keyRead = new BufferedReader(new InputStreamReader(System.in));
// sending to client (pwrite object)
OutputStream ostream = sock.getOutputStream();
PrintWriter pwrite = new PrintWriter(ostream, true);
InputStream istream = sock.getInputStream();
BufferedReader receiveRead = new BufferedReader(new InputStreamReader(istream));
System.out.println("Start the chitchat, type and press Enter key");
String receiveMessage,sendMessage;
while(true)
{
sendMessage = keyRead.readLine(); // keyboard reading
pwrite.println(sendMessage); // sending to server
pwrite.flush(); // flush the data
if((receiveMessage = receiveRead.readLine()) != null) //receive from server
{
System.out.println(receiveMessage); // displaying at DOS prompt
}
else{
System.out.print("Null");
}
}
}
}
subscriber
package serverclient;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
public class Subscriber {
public static void main (String [] args) throws IOException{
Socket sock = new Socket("127.0.0.1",5000);
// receiving from server ( receiveRead object)
InputStream istream = sock.getInputStream();
BufferedReader receiveRead = new BufferedReader(new InputStreamReader(istream));
System.out.println("Recive side");
String receiveMessage, sendMessage;
while(true)
{
System.out.print("Hey man " + receiveRead.readLine() + "\n");
if((receiveMessage = receiveRead.readLine()) != null) //receive from server
{
System.out.println(receiveMessage); // displaying at DOS prompt
}
else{
System.out.print("Null");
}
}
}
}
Any help is appreciated. I just want to figure out why subscriber is not reciveing message
There are many possibilities to handle realm time communication issues. I myself prefer the use of Events / EventListeners.
Currently in your program there is no communication between the Server as such and the threads which handle the subscriber connection.
Also on a side node: even with a proper communication between publisher connection threads and subscriber connection threads it won't work now since you are using the same Server class. This does not only violate the Single-Responsibility-Principle but will also prevent the server from ever sending a message to the Subscriber.
Let's say you have establish a connection and your server class is now connected with the subscriber. What will happen?
The subscriber will loop until there is a message on the input stream of his socket. Good that is exactly what we want. But what does the server do? The truth is exactly the same. The first few statements in the try block of your Server's run method are to create a BufferedReader and read from it until a message receives. And now we have a socket on each site which will infinitly wait for some kind of message to arrive (which will obviously never happen since both are waiting for something).
To prevent this you should check if there is anything to read on the stream first:
while ( true )
{
if ( socket.getInputStream().available() != 0 )
{
// reading logic goes here....
synchronized ( this )
{
String clMessage = dStream.readLine();
System.out.println( "\n" + clMessage );
out.println( "Hey the server is sending the message to subscriber" );
}
}
// what shall be done when not reading.
}
Now the second part. If you want to communicate between threads you need to implement some logic to do so. As stated above I love the concept of Listeners so i will show an example where I make use of them:
MessageReceivedListener.java
import java.util.EventListener;
public interface MessageReceivedListener
extends EventListener
{
public void onMessageReceived( String message );
}
Note: The interface does not have to extend EventListener since EventListener
is just a tagging interface. I myself still prefer to have this as a reminder for what purpose the interface is there.
Server.java (excerpt)
// New constructor since we will pass a Listener now. Also new local variable for it.
public Server( Socket socket, int clientNumber, MessageReceivedListener mrl )
{
this.socket = socket;
this.clientNumber = clientNumber;
this.mrl = mrl;
if ( socket.getLocalPort() == 5000 )
System.out.print( "\nSubscriber " + clientNumber + " is connected to the server" );
if ( socket.getLocalPort() == 8000 )
System.out.print( "\nPublisher " + clientNumber + " is connected to the server" );
}
The new constructor provides a way to pass the MessageReceivedListener to the Server object. Alternatively you can alsocreate a setter for it.
synchronized ( this )
{
String clMessage = dStream.readLine();
System.out.println( "\n" + clMessage );
out.println( "Hey the server is sending the message to subscriber" );
mrl.onMessageReceived( clMessage );
}
This is where the magic happens. After whe receive the message we just pass it to the onMessageReceived(String message) method of the listener. But what does it do exactly? This is what we define when creatinga Server object.
Here are two examples, one with anonymous classes (Java 7 and before) and on with lambdas (Java 8 and later).
Example Java 7 and earlier
Server servpub = new Server( servSockpub.accept(), ++pubNumber,
new MessageReceivedListener()
{
#Override
public void onMessageReceived( String message )
{
// call nother local method
// this method would need to be a static method of Server
// because it's in the scope of your server class
sendMessageToSubscribers(message);
}
} );
Here we pass an anonymous class as our MessageReceivedListener object and define it's behaviour (in this case just calling another method which will handle the rest.
Now since our MessageReceivedListener interface does only contain one method we can also see it as a functional interface and therefore use lambdas to shorten the code and improve readability.
Example with Lambda (Java 8 and later)
Server servpub = new Server( servSockpub.accept(), ++pubNumber, Server::sendMessageToSubscribers);
In this specific case we only have one argument which we want to pass to a method and therefore can use a method reference.
How to actually implement the method sendMessageToSubs(String message) is up to you. But you need to keep track of how many Threads with subscriber connections have been created and how you want to reference them.

Sending data from Java server to ESP8266

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:

Messaging between Android and Desktop

I'm trying to make an Android app that's able to send a message to a computer and receive one from it. It's pretty basic. The thing is, I have accomplished this through multicasting, although not exactly. My app is able to receive messages from the computer (which uses a java application I made to receive and send the messages). But, when I try to send a message from the device to the computer, the message doesn't arrive to the computer. I mean, to the application.
Both the desktop app and the Android app use the same Client - Server classes. This is what gets me so confused. Because, as I am using the same classes, why does it work one way but not the other? I just don't no.
The desktop app runs on windows.
Also, when the Android app receives a message, it receives it the following way: "Message 1���������������������������..." when the message should be received: "Message 1". I don't know if this could be relevant.
The code is the following:
Server Class:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class MulticastSocketServer implements Runnable{
final static String INET_ADDR = "224.0.0.3";
final static int PORT = 8888;
static String msg;
public MulticastSocketServer(String message) throws UnknownHostException, InterruptedException {
msg = message;
Thread thread = new Thread(this);
thread.start();
}
#Override
public void run() {
// TODO Auto-generated method stub
// Get the address that we are going to connect to.
InetAddress addr = null;
try {
addr = InetAddress.getByName(INET_ADDR);
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// Open a new DatagramSocket, which will be used to send the data.
try (DatagramSocket serverSocket = new DatagramSocket()) {
msg += "\\0";
for (int i = 0; i < 10; i++) {
// Create a packet that will contain the data
// (in the form of bytes) and send it.
DatagramPacket msgPacket = new DatagramPacket(msg.getBytes(),
msg.getBytes().length, addr, PORT);
serverSocket.send(msgPacket);
System.out.println("Server sent packet with msg: " + msg);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
serverSocket.disconnect();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
Client Class:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.UnknownHostException;
import java.util.Timer;
import java.util.TimerTask;
public class MulticastSocketClient implements Runnable {
final static String INET_ADDR = "224.0.0.3";
final static int PORT = 8888;
Connection360 conn;
public MulticastSocketClient (Connection360 connection) throws UnknownHostException {
conn = connection;
Thread thread = new Thread(this);
thread.start();
}
#Override
public void run() {
try{
// Get the address that we are going to connect to.
InetAddress address = InetAddress.getByName(INET_ADDR);
// Create a buffer of bytes, which will be used to store
// the incoming bytes containing the information from the server.
// Since the message is small here, 256 bytes should be enough.
byte[] buf = new byte[256];
// Create a new Multicast socket (that will allow other sockets/programs
// to join it as well.
try (final MulticastSocket clientSocket = new MulticastSocket(PORT)){
//Joint the Multicast group.
clientSocket.joinGroup(address);
System.out.println("Connected");
//while (true) {
// Receive the information and print it.
DatagramPacket msgPacket = new DatagramPacket(buf, buf.length);
Timer timer = new Timer("tmr");
timer.schedule(new TimerTask() {
#Override
public void run() {
clientSocket.disconnect();
}
},10000);
clientSocket.receive(msgPacket);
String msg = new String(buf, 0, buf.length);
System.out.println("Socket 1 received msg: " + msg.substring(0, msg.indexOf("\\0")));
conn.MessageReceived(msg.substring(0, msg.indexOf("\\0")));
clientSocket.disconnect();
//}
} catch (IOException ex) {
ex.printStackTrace();
}
}catch (UnknownHostException ex){
}
}
}
This classes are the ones I made for the desktop app. The classes I made for the Android app are the same, but I had to change the System.out.println() to Log.v(). As for the rest, it's exactly the same.
So, if you happen to know what could be happening, I would really appreciate your assistance with the topic.
Thank you!
When you read the incoming packet, you don't use its size but the size of the buffer instead:
String msg = new String(buf, 0, buf.length);
// should be:
String msg = new String(buf, 0, msgPacket.getLength());
// or even better:
String msg = new String(msgPacket.getData());
If the incoming packet is shorter, the rest of the buffer contains random data which is what you got. Java strings are not NUL-terminated so msg.indexOf("\\0") does not work.

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