How do I run these two loops simultaneously? (Java) - java

I'm trying to write a simple console program that allows me to send and receive String messages. The problem I am encountering though, is that I don't know how to run the receiving code and the sending code simultaneously.
Individually, the classes are working. I can receive packets and send packets, but making them run at once seems impossible to me.
I've looked into multi-threading but since my knowledge is still very basic, I can't seem to understand how it really works.
This is the code I'm currently using. I wrote the Dialog class myself and found the other two classes on the internet.
Dialog class:
import java.util.Scanner;
public class Dialog {
Scanner scanner = new Scanner(System.in);
User user = new User();
Network net = new Network();
ThreadReceive tr = new ThreadReceive();
ThreadSend ts = new ThreadSend();
public void run() {
System.out.println("WELCOME");
System.out.print("Port: ");
while(!user.setPort(giveInput())) {
System.out.println("Enter a valid port.");
}
System.out.print("IP: ");
user.setIP(giveInput());
System.out.println();
System.out.println("--- CONVERSATION STARTED ---");
tr.receive(user.getIP(), user.getPort()); // Starts receiving loop (within ThreadReceive class).
while (true) { // Starts sending loop.
ts.sendMessage(giveInput(), user.getIP(), user.getPort()); // Sends packet when input is given.
}
}
private String giveInput() {
String input = scanner.nextLine();
return input;
}
}
Receiving class:
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class ThreadReceive extends Thread {
public void receive(String ip, int port) {
try {
// Create a socket to listen on the port.
DatagramSocket dsocket = new DatagramSocket(port);
// Create a buffer to read datagrams into. If a
// packet is larger than this buffer, the
// excess will simply be discarded!
byte[] buffer = new byte[2048];
// Create a packet to receive data into the buffer
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
// Now loop forever, waiting to receive packets and printing them.
while (true) {
// Wait to receive a datagram
dsocket.receive(packet);
// Convert the contents to a string, and display them
String msg = new String(buffer, 0, packet.getLength());
System.out.println(packet.getAddress().getHostName() + ": " + msg);
// Reset the length of the packet before reusing it.
packet.setLength(buffer.length);
}
}
catch (Exception e) {
System.err.println(e);
}
}
}
Sending class:
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class ThreadSend extends Thread {
public void sendMessage(String message, String ip, int port) {
try {
byte[] data = message.getBytes();
InetAddress address = InetAddress.getByName(ip);
DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
DatagramSocket datagramSocket = new DatagramSocket();
datagramSocket.send(packet);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
Also, is there any way to test if I can receive packets? I've been testing it with a friend but it would be much more convenient to do it myself.
Thanks!

You are not using Threads correctly.
The logic should be in the run method.
I suggest you use a queue such as an ArrayBlockingQueue to pass parameters to your threads. For instance, you could have a method to add elements to this queue
public void addMessage(String message) {
synchronized(inputQueue) {
inputQueue.offer(r);
inputQueue.notify();
}
}
And the run method will use these elements as so :
public void run() {
try {
while(!running)
synchronized (inputQueue) {
inputQueue.wait(); // you can have a timeout also...
String message = this.inputQueue.poll();
// use the message item....
// in your case send it to the other user.
}
}
}
} catch (Exception e) {
/////// your exception handler
}
}
Also Remember to start your threads :
Thread t = new MyThread();
t.start(); /// Start the thread !!!
PS : The messages can be any object here is used strings as i based this on some of my code where i'm using a
Queue<String>

Check out Beej's Guide to Network Programming: http://beej.us/guide/bgnet/output/html/singlepage/bgnet.html -- This will give you some more examples to take a look at. As far as testing goes, you could set up a virtual machine or use another computer you own. Back when I had to learn networking in school we would ssh into two separate Linux boxes to test our code.
EDIT: Also to make sure you are receiving correctly you could make the sender and receiver both print out the packet data they receive. Or you could just have them print a simple string to say the packet was received.
You also might want to check out TCP rather than UDP if you are wanting a continuous stream. UDP just creates a datagram packet and sends it out on the network, whereas TCP creates a persistant connection between two hosts.

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.

BufferedReader readLine() blocking until buffer is full

I'm writing a client/server pair of applications. The server runs multiple threads that collect data and adds it to a BlockingQueue. The socket code loops over the queue and sends whatever data it finds to the client. The data is a string and I append a line separator so that the client can read it using BufferedReader.readLine().
My problem is that instead of readLine() returning on each line that's available it waits until the entire buffer is full before spitting out all the complete lines in the buffer. With the default 8K buffer this means I get data via the client in 8K chunks, which is highly undesirable. I've attached MRE code that represents this. I have confirmed via logging in my actual application that the BufferedWriter is writing the data as soon as it's available from the queue, but to be honest I don't know if the delay is coming after this on the sending side, or is truly on the reading side. If you run the MRE you'll see that the data is displayed approximately 170 lines at a time by the client.
I've searched online for this phenomenon for a couple of days and the one snippet that I could find of a similar issue suggests that maybe it's something to do with the underlying InputStreamReader and/or StreamDecoder, but that is starting to get beyond my expertise. (See this link)
So my question is whether I'm implementing the BufferedReader correctly and how can I resolve the issue I'm seeing so that I get each incoming line without unnecessary delays.
package serverTest;
import java.util.concurrent.BlockingQueue;
public class ServerTest {
public static void main(String[] args) {
int port = 54321;
ServerSocketComms server = new ServerSocketComms(port);
BlockingQueue<String> queue = server.getQueue();
new Thread(server).start();
ClientSocketComms client = new ClientSocketComms("localhost", port);
new Thread(client).start();
for(int i = 0; i < 1000; i++) { // should give about 10 seconds of output
try {
queue.put("" + i + " - All work and no play makes Jack a dull boy");
// Slow things down enough to show what's happening
Thread.sleep(10);
// 48 characters should fill the 8K buffer in approximately 2 seconds
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
package serverTest;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class ServerSocketComms implements Runnable {
private final BlockingQueue<String> queue = new LinkedBlockingQueue<>();
private final int port;
public ServerSocketComms(int port) {
this.port = port;
}
#Override
public void run() {
// Open server socket and wait for connection
try {
ServerSocket serverSocket = new ServerSocket(port);
Socket socket = serverSocket.accept();
// Continually loop over blocking data queue until stopped
BufferedWriter dataOut = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
while(socket.isConnected()) {
dataOut.write(queue.take());
dataOut.newLine(); // delimit strings with a line separator
}
// Loop never exits because client socket never completes because of BufferedReader issue
// so sockets never close and application never terminates
socket.close();
serverSocket.close();
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
public BlockingQueue<String> getQueue() {
// Return a reference to the sending queue to be populated by other threads
return this.queue;
}
}
package serverTest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
public class ClientSocketComms implements Runnable {
private final String server;
private final int port;
public ClientSocketComms(String server, int port) {
this.server = server;
this.port = port;
}
#Override
public void run() {
// Open socket to server and wait for incoming data
try {
Socket socket = new Socket(server, port);
BufferedReader dataIn = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// Continually loop over incoming data until stopped
String data;
while((data = dataIn.readLine()) != null) {
// Should print out every line as it's received,
// but instead waits until buffer is full
// (outputs about 170 lines at a time)
System.out.println(data);
}
// Close socket and thread will die
// (but loop never ends because buffer doesn't get completely refilled)
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Your server is using a BufferedWriter:
BufferedWriter dataOut = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
This one does the buffering that you do not like. It seems the default buffer size is the 8k that you are seeing although that is not documented in the API and could change. Try flushing the buffer using dataOut.flush() if at some point in time you want to ensure everything stored in the buffer so far is sent out to the client immediately. Have a look at the BufferedWriter API for details.
BTW, I have not checked whether there are any other problems in your code. But the above is definitely one.

Receive DJI Tello's video stream using Java

I'm trying to write a java application for controlling the DJI Tello drone.
I'm already able to send simple commands to the drone by using java.net.DatagramSocket as client.
The Tello SDK says:
It says I have to use java.net.DatagramSocket as the server to receive the video stream.
This is my try to receive the video stream:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
public class TelloCamera extends Thread {
private boolean isStreamOn;
private DatagramSocket serverSocket;
private byte[] receiveData = new byte[1470];
public TelloCamera() {
isStreamOn = true;
}
public void run() {
try {
serverSocket = new DatagramSocket(11111);
} catch (SocketException e) {
e.printStackTrace();
return;
}
while (isStreamOn) {
receiveData = new byte[1470];
try {
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
serverSocket.receive(receivePacket);
String z = new String(receivePacket.getData());
System.out.println(z);
} catch (IOException e) {
e.printStackTrace();
}
isStreamOn = false;
}
serverSocket.close();
}
public boolean isStreamOn() {
return isStreamOn;
}
public void setStreamOn(boolean streamOn) {
isStreamOn = streamOn;
}
}
Main:
package tellotest;
public class maintellotest {
public static void main(String[] args) {
TelloCommander tello = new TelloCommander();
tello.sendCommand("command");
tello.sendCommand("streamon");
TelloCamera camera = new TelloCamera();
camera.start();
}
}
Does anybody know why I get no string printed to console?
You have to send "streamon" command at 8889 port to Tello first and then listen at 11111 port. But in your code code, there is no code for sending the command. Try adding the code to send command first, send "command" then send "streamon" then listen on port 11111.
I've also searched for a solution to this and came across a well implemented Tello java library, JTello # https://github.com/xrv0/JTello.
In the library the author make use of JCodec (http://jcodec.org/) to decode the H.246 stream:
For example:
// byte[] message refers to the content of the datagram received from the drone over port 1111
// Allocate output frame of max size
Picture out = Picture.create(1920, 1088, ColorSpace.YUV420);
Picture real = decoder.decodeFrame(ByteBuffer.wrap(message), out.getData());
You need to create an InetSocketAddress with IP "0.0.0.0" and port 11111 and then bind the socket to it. And also remove isStreamOn = false from the while loop.

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.

improving speed of comunication in java sockets

I am trying to improve the speed at which the sockets transfer information but i am unsure how to do so. the pourpose of the code is to transfer a number, the date, and a short xml which is being sent in the form of a string.
this is the server code
import java.net.*;
import java.io.*;
public class SSocket extends Thread
{
private ServerSocket serverSocket;
public SSocket(int port) throws IOException
{
serverSocket = new ServerSocket(port);
serverSocket.setSoTimeout(100000);
}
public void run()
{
System.out.println("Waiting for client on port " + serverSocket.getLocalPort() + "...");
while(true)
{
try
{
Socket server = serverSocket.accept();
DataInputStream in = new DataInputStream(server.getInputStream());
int cor=in.readInt();
int i=0;
String transaccion = in.readUTF();
String fecha = in.readUTF();
System.out.println(cor);
System.out.println(transaccion);
System.out.println(fecha);
DataOutputStream out =
new DataOutputStream(server.getOutputStream());
if(transaccion!=null && fecha != null && cor>0){
out.writeInt(cor);
}
else {
out.writeInt(-1);
}
if (i==100){
out.flush();
i=0;
}
i++;
server.close();
}catch(SocketTimeoutException s)
{
System.out.println("Socket timed out!");
break;
}catch(IOException e)
{
e.printStackTrace();
break;
}
}
}
public static void main(String [] args)
{
int port = 1337;
try
{
Thread t = new SSocket(port);
t.start();
}catch(IOException e)
{
e.printStackTrace();
}
}
}
the code for the client is
import java.net.*;
import java.io.*;
public class ClientSocket
{
public static void send(int correl, String transaccion, String fecha)
{
String serverName = "localhost";
int port = 1337;
try
{
Socket client = new Socket(serverName, port);
int i=0;
OutputStream outToServer = client.getOutputStream();
DataOutputStream out =
new DataOutputStream(outToServer);
out.writeInt(correl);
out.writeUTF(transaccion);
out.writeUTF(fecha);
InputStream inFromServer = client.getInputStream();
DataInputStream in =
new DataInputStream(inFromServer);
int corin=in.readInt();
if(corin>0){
Envio.updater(corin);
}
else {
}
if (i==100){
out.flush();
i=0;
}
i++;
client.close();
}catch(IOException e)
{
e.printStackTrace();
}
}
}
i have done some reading on the mater and it seems that posible solutions are to use either a buffer or swich to a datagram. however my experience on working with sockets is rather limited and i am unsure which would be best to use for this situation or if there is another option i havent yet considered. this code will be moving many transactions and i wish to do it in as short time as posible.
thanks in advance
ps. sorry for my bad english it is not my first language
Datagrams imply UDP, which is an unreliable delivery protocol so you're not guaranteed to get all content. That's probably not what you want; I'd stay with plain Sockets (which use TCP, which has reliable delivery).
Will the same client be calling send() repeatedly and connecting to the same server each time? That is, will there be many messages going across a single connection, or will each message be to a different server, with only a single message (or only a few) going to each of the many servers? If there's just one server that a client is going to connect to and if a given client is going to send lots of messages, you should keep the Socket open between send() calls; setting up and tearing down Sockets is expensive, so you're paying a high price for making a new connection each time.
Also, your server appears to only be able to handle a single connection at a time: you accept a connection, read from it, and then close it and accept a new one. So to make this work for more than one client, you'll need to separate the logic for accepting connections onto a different thread from the logic that reads data. If you'll only have a few clients at a time, you can just start a new thread to read from each socket as you create it for a new client; if you'll have lots of clients (thousands), you'll probably need to look at NIO for its ability to service multiple sockets from a single thread. But I suspect you're a long way from having that problem, if you ever do, so I'd just spawn a new thread for each socket.

Categories