Very simple UDP server/client on Android wont work - java

I'm trying to do a very simple client on android to send an UDP message to a server on Android.
Here's my simple UDP client:
public class MyUDPClient {
private static String TAG = "MyUDPClient";
private DatagramSocket udpSocket;
private InetAddress serverAddress;
private int port;
private Scanner scanner;
public MyUDPClient(String destinationAddr, int port) throws SocketException, UnknownHostException {
this.serverAddress = InetAddress.getByName(destinationAddr);
this.port = port;
udpSocket = new DatagramSocket(this.port);
scanner = new Scanner(System.in);
}
public void send(String message) throws IOException {
DatagramPacket p = new DatagramPacket(
message.getBytes(), message.getBytes().length, serverAddress, port);
Log.d(TAG, "udp client send: " + message + " to serverAddress " + serverAddress);
this.udpSocket.send(p);
}
public void connect() {
Log.d(TAG, "UDP connection to port " + port);
}
}
And here's my simple UDP server:
public class MyUDPServer {
private String TAG = "MyUDPServer";
private int port;
private DatagramSocket udpSocket;
OnMessageCallback onMessageCallback;
public MyUDPServer(int port) throws SocketException {
this.udpSocket = new DatagramSocket(port);
this.port = port;
}
public interface OnMessageCallback {
public void on(String message);
}
private void listen() throws Exception {
Log.d(TAG, "listening UDP server on port " + port);
String msg;
while (true) {
byte[] buf = new byte[256];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
// blocks until a packet is received
udpSocket.receive(packet);
msg = new String(packet.getData()).trim();
Log.d(TAG, "message received; " + msg);
onMessageCallback.on(msg);
}
}
public void start() throws Exception {
listen();
}
public void setMessageCallback(OnMessageCallback onMessageCallback) {
this.onMessageCallback = onMessageCallback;
}
}
The problem is that the server never receives any messages. I start the client like this:
MyUDPClient myUDPClient = new MyUDPClient("192.168.1.6", 8887);
Since it's on an emulator, I try to filter the IP 192.168.1.6 and even though I call myUDPClient.send("message"), on WireShark I receive nothing, which indicates that the message is not even leaving the computer where the emulator tuns.
What am I doing wrong?
I've created a WebSocket client/server on Android and it worked fine.
I have these 2 permissions:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

In your MyUDPClient-class simply change to avoid a "double" binding that causes the exception:
public MyUDPClient(String destinationAddr, int port) throws SocketException, UnknownHostException {
super();
this.serverAddress = InetAddress.getByName(destinationAddr);
this.port = port;
udpSocket = new DatagramSocket(this.port);
scanner = new Scanner(System.in);
}
to
public MyUDPClient(String destinationAddr, int port) throws SocketException, UnknownHostException {
super();
this.serverAddress = InetAddress.getByName(destinationAddr);
this.port = port;
udpSocket = new DatagramSocket();
//udpSocket = new DatagramSocket(this.port);
scanner = new Scanner(System.in);
}

Problem was that I was gatting NetworkOnMainThreadException but it was being logged to another part of the logcat that I wasn't filtering for.
Doing the myudpClient.send("myMessage") from a separate thread solved the problem. That's why I couldn't even see the output on Wireshark, it wasn't even sending.

Related

Server doesn't detect second client connecting

The problem i have is that when i open a second client, the server doesn't seem to detect that a second client was opened. With the first time the client being opened it works fine and the server detects that a client has been connected.
Server:
public class Server {
Socket previousSocket = null;
private static int port = 9001;
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("[SERVER] Server successfully launched on port: " + port);
DatagramSocket UDPSocket = new DatagramSocket(9002);
Socket previousSocket = null;
while (true) {
Socket newSocket = serverSocket.accept();
System.out.println("new client connected");
if (previousSocket == null) {
previousSocket = newSocket;
System.out.println("1 st client");
} else {
System.out.println("2 nd client");
previousSocket = null;
}
byte[] data = new byte[500];
DatagramPacket received = new DatagramPacket(data, data.length);
while(true) {
UDPSocket.receive(received);
String receivedData = new String(received.getData());
System.out.println(receivedData);
}
}
}
}
Client:
public ChatClient() throws UnknownHostException, IOException {
Socket socket = new Socket("127.0.0.1", 9001);
Scanner scanner = new Scanner(System.in);
DatagramSocket UDPSocket = new DatagramSocket();
while(scanner.hasNextLine()) {
String message = scanner.nextLine();
InetAddress ip = InetAddress.getByName("127.0.0.1");
DatagramPacket packet = new DatagramPacket(message.getBytes(), message.getBytes().length, ip, 9002);
UDPSocket.send(packet);
}
}
The following loop will never end, that's the reason for your problem
while(true) {
UDPSocket.receive(received);
String receivedData = new String(received.getData());
System.out.println(receivedData);
}

How to assign buffer size dynamically based on incomming UDP packet size?

This is first time I am working on UDP communication and I have no idea does my knowledge inadequate or document I am referring to does not have enough information.
I have a client server application. Server supposed to send an alert message, Client supposed to establish a connection, be alive as long as the app is alive, receive the notification and extract the actual message (to be shown to users). I am able to send the message and receive it by my client.
I have a question:
I assigned 2K to my receiver's buffer (byte[] buf = new byte[2048];). It might be sufficent for most cases. However, what should I do if backend sends something more than 2048 characters? Doc says the length is dynamic therefore I have no upper bound.
My client code:
public class UDPClient {
private DatagramSocket udpSocket;
private InetAddress serverAddress;
private int port;
private Scanner scanner;
private UDPClient(String destinationAddr, int port) throws IOException {
this.serverAddress = InetAddress.getByName(destinationAddr);
this.port = port;
udpSocket = new DatagramSocket(this.port);
scanner = new Scanner(System.in);
}
public static void main(String[] args) throws IOException {
UDPClient sender = new UDPClient("192.168.1.102", 8888);
print("-- Running UDP Client at " + InetAddress.getLocalHost() + " --");
sender.listen();
}
private void listen() throws IOException {
String msg;
String in = scanner.nextLine();
DatagramPacket p = new DatagramPacket(in.getBytes(), in.getBytes().length, serverAddress, port);
this.udpSocket.send(p);
while (true) {
byte[] buf = new byte[2048];
DatagramPacket packet = new DatagramPacket(buf, buf.length, serverAddress, port);
// blocks until a packet is received
udpSocket.receive(packet);
msg = new String(packet.getData()).trim();
print("Message from " + packet.getAddress().getHostAddress() + ": " + msg);
parseNotification(msg);
}
}
private static void print(String message) {
System.out.println(message);
}
private void parseNotification(String notification) {
byte[] actual = notification.getBytes();
print("Actual length: " + actual.length);
byte[] response = UDPResponse.hexStringToByteArray(notification);
Message message = getMessageFromEASNotification(response);
print("Final message in json:");
print(message.toString());
}
}

UDP : Skip the response of timeout request

I have a UDP process that required to define the receiving port and IP address.
Upon testing I figured out that having the same reception IP and port at the same time of requests might result to data loss/switching.
I came up with the idea that If there's an existing request with same data, the incoming/2nd request will wait for the first one to finish the transaction and then do the request.
Since this is a servlet and I'm worrying for multiple requests in different browser at the same time, I used Semaphore in a singleton pattern to return single instance. My DatagramSocket is in singleton pattern as well to avoid "already bind" error.
My code is doing well with no data switching in a happy path scenario where in it will just send a request and response with no error.
I set a socket timeout and execute a timeout exception scenario,here's what I got
First request =====> UDP server
Timeout exception <==== UDP server
Second Request=====> UDP server
First response <==== UDP server
I'm receiving the first response on my 2nd request and so on.
How will I reject/skip the response of the timeout request? Please note that I am not allowed to add an identifier on my response as per requirement.
Here's my code for your reference :
private void calludp(String ip, String targetIp, String port, String targetPort, String timeout, byte[] message)
throws IOException, SAXException, ParserConfigurationException, InterruptedException {
String key = targetIp + ":" + targetPort;
UDPReceptionData udpReceptionData = null;
udpReceptionData = getReceptionLock(key, receptionMap);
Semaphore semaphore = udpReceptionData.getSemaphore();
DatagramSocket datagramSocket = udpReceptionData.getDatagramSocket();
semaphore.acquire();
try {
send(ip, targetIp, port, targetPort, timeout, message, datagramSocket);
} finally {
semaphore.release();
}
}
private void populateUDPReceptionMap(String port, String target) {
if ((target != null && !target.isEmpty()) && (port != null && !port.isEmpty())) {
Semaphore semaphore = new Semaphore(1);
DatagramSocket datagramSocket = getSocketMap(port);
UDPReceptionData udpReceptionData = new UDPReceptionData(datagramSocket, semaphore);
this.receptionMap.put(target + ":" + port, udpReceptionData);
}
}
private DatagramSocket getSocketMap(String port) {
System.out.println(socketMap.toString());
DatagramSocket datagramSocket = null;
if (port != null && !port.isEmpty()) {
if (!socketMap.containsKey(port)) {
try {
datagramSocket = new DatagramSocket(Integer.parseInt(port));
this.socketMap.put(port, datagramSocket);
} catch (NumberFormatException | SocketException e) {
e.printStackTrace();
}
} else {
datagramSocket = socketMap.get(port);
}
}
return datagramSocket;
}
public UDPReceptionData getReceptionLock(String ipPort, Map<String, UDPReceptionData> receptionMap) {
System.out.println(receptionMap.toString());
return receptionMap.get(ipPort);
}
public byte[] send(String target, String receptionTarget, String port, String receptionPort, String timeout,
byte[] message, DatagramSocket clientSocket) throws IOException {
String messageResponse = ""; // Message response to be return to the
// caller
int intTimeout = Integer.parseInt(timeout);
DatagramPacket receivePacket = null;
try {
InetAddress ipAddress = InetAddress.getByName(target);
int intPort = Integer.parseInt(port);
byte[] receiveData = new byte[1024];
DatagramPacket sendPacket = new DatagramPacket(message, message.length, ipAddress, intPort);
clientSocket.send(sendPacket);
// receive the data
receivePacket = new DatagramPacket(receiveData, receiveData.length);
try {
clientSocket.setSoTimeout(intTimeout);
clientSocket.receive(receivePacket);
} catch (SocketTimeoutException e) {
throw new SocketTimeoutException("Socket Timeout Exception");
}
messageResponse = new String(receivePacket.getData());
String mes = new String(message, "UTF-8");
System.out.println("FROM SERVER:" + messageResponse + " :::: " + mes);
} finally {
// commented out the close since this is a single instance DatagramSocket
// clientSocket.close();
}
return receivePacket.getData();
}
UDPReceptionData.java
import java.net.DatagramSocket;
import java.util.concurrent.Semaphore;
public class UDPReceptionData {
private Semaphore semaphore;
private DatagramSocket datagramSocket;
public UDPReceptionData(DatagramSocket datagramSocket, Semaphore semaphore) {
this.datagramSocket = datagramSocket;
this.semaphore = semaphore;
}
public DatagramSocket getDatagramSocket() {
return this.datagramSocket;
}
public Semaphore getSemaphore() {
return this.semaphore;
}
}
UDPServer.java
private void udpSender(DatagramSocket serverSocket) throws IOException, InterruptedException {
while (true) {
byte[] responseMessage = new byte[1024];
byte[] requestMessage = new byte[1024];
DatagramPacket receivePacket = new DatagramPacket(responseMessage, responseMessage.length);
serverSocket.receive(receivePacket);
String receivedMessage = new String(receivePacket.getData());
System.out.println("RECEIVED: " + receivedMessage);
InetAddress IPAddress = receivePacket.getAddress();
int port = receivePacket.getPort();
requestMessage = receivedMessage.getBytes();
// Set the sending packet to designated IP Address and port
DatagramPacket sendPacket = new DatagramPacket(requestMessage, requestMessage.length, IPAddress, port);
//delay for 4 seconds for timeout testing
Thread.sleep(4000);
serverSocket.send(sendPacket);
}
}
main method
public static void main(String[] args) {
UDPProcess m = new UDPProcess();
m.populateUDPReceptionMap("9090", "localhost");
String request = "localhost:9090:message1";
String request2 = "localhost:9090:message2";
new Thread() {
public void run() {
try {
m.calludp("localhost", "localhost", "7979", "9090", "2000", request.getBytes());
} catch (IOException | SAXException | ParserConfigurationException | InterruptedException e) {
e.printStackTrace();
}
}
}.start();
new Thread() {
public void run() {
try {
m.calludp("localhost", "localhost", "7979", "9090", "60000", request2.getBytes());
} catch (IOException | SAXException | ParserConfigurationException | InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
Result :
java.net.SocketTimeoutException: Socket Timeout Exception
at com.comp.proj.connector.UDPProcess.send(UDPProcess.java:221)
at com.comp.proj.connector.UDPProcess.calludp(UDPProcess.java:135)
at com.comp.proj.connector.UDPProcess.access$0(UDPProcess.java:118)
at com.comp.proj.connector.UDPProcess$1.run(UDPProcess.java:51)
FROM SERVER:localhost:9090:message2 :::: localhost:9090:message1
You need sequence numbers in your requests and responses. You need to ignore responses from a prior sequence number. That can happen even without a timeout, as UDP datagrams can be duplicated (or more).

java.net.BindException: Address already in use: Cannot bind

I am sorry, I have searched but seem that all the answers dont fix my problem. I got this error when trying to create a ServerSocket to reply to multiple client message.
My server code:
package Server;
import java.net.*;
import java.io.*;
public class Server {
public final static int defaultPort = 7;
public static void main(String[] args) {
try {
ServerSocket ss = new ServerSocket(defaultPort);
int i = 0;
while (true) {
try {
System.out.println("Server is running on port "
+ defaultPort);
Socket s = ss.accept();
System.out.println("Client " + i + " connected");
RequestProcessing rp = new RequestProcessing(s, i);
i++;
rp.start();
} catch (IOException e) {
System.out.println("Connection Error: " + e);
}
}
} catch (IOException e) {
System.err.println("Create Socket Error: " + e);
} finally {
}
}
}
class RequestProcessing extends Thread {
Socket channel;
int soHieuClient;
public RequestProcessing(Socket s, int i) {
channel = s;
clientNo = i;
}
public void run() {
try {
byte[] buffer = new byte[6000];
DatagramSocket ds = new DatagramSocket(7);
while (true) {
DatagramPacket incoming = new DatagramPacket(buffer,
buffer.length);
ds.receive(incoming);
String theString = new String(incoming.getData(), 0,
incoming.getLength());
System.out.println("Client " + clientNo
+ " sent: " + theString);
if ("quit".equals(theString)) {
System.out.println("Client " + clientNo
+ " disconnected");
ds.close();
break;
}
theString = theString.toUpperCase();
DatagramPacket outsending = new DatagramPacket(
theString.getBytes(), incoming.getLength(),
incoming.getAddress(), incoming.getPort());
System.out.println("Server reply to Client "
+ clientNo + ": " + theString);
ds.send(outsending);
}
} catch (IOException e) {
System.err.println(e);
}
}
}
and my Client code:
package Client;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.Socket;
public class Client extends Object {
public final static int serverPort = 7;
public static void main(String[] args) {
try {
DatagramSocket ds = new DatagramSocket();
InetAddress server = InetAddress.getByName("192.168.109.128");
Socket s = new Socket("192.168.109.128", 7);
String theString = "";
do {
System.out.print("Enter message: ");
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(isr);
theString = br.readLine();
byte[] data = theString.getBytes();
DatagramPacket dp = new DatagramPacket(data, data.length,
server, serverPort);
ds.send(dp);
System.out.println("Sent to server server: " + theString);
byte[] buffer = new byte[6000];
DatagramPacket incoming = new DatagramPacket(buffer,
buffer.length);
ds.receive(incoming);
System.out.print("Server reply: ");
System.out.println(new String(incoming.getData(), 0, incoming
.getLength()));
} while (!"quit".equals(theString));
s.close();
} catch (IOException e) {
System.err.println(e);
}
}
}
With the first Client connect, it works smoothly. But from the second Client, it throws java.net.BindException: Address already in use: Cannot bind.
Second Client can also send and receive message, but the Client No is still 0.
Server is running on port 7
Client 0 connected
Server is running on port 7
Client 0 sent: msg 0
Server reply to Client 0: MSG 0
Client 1 connected
Server is running on port 7
java.net.BindException: Address already in use: Cannot bind
Client 0 sent: msg 1 <<-- this one is sent from client 1 but Client No is 0
Server reply to Client 0: MSG 1
So, in RequestProcessing.run you decide to ignore the socket received at constructor and open a DatagramSocket on the same port as the one you are listening. What did you expect it will happen?
class RequestProcessing extends Thread {
Socket channel;
int soHieuClient;
public RequestProcessing(Socket s, int i) {
// *****************
// The processor should be using this socket to communicate
// with a connected client *using TCP Streams*
channel = s;
clientNo = i;
}
public void run() {
try {
byte[] buffer = new byte[6000];
// *****************************
// But, instead of using the this.channel, your code
// decides to ignore the TCP socket,
// then open another UDP *"server-side like"* socket.
// First time it's OK, but the second thread attempting
// to open another DatagramSocket on the same port will fail.
// It's like attempting to open two TCP ServerSockets on the
// same port
DatagramSocket ds = new DatagramSocket(7);
[Extra]
You will need to decide what protocol you'll be using: if you use a ServerSocket/Socket pair, then probably you want TCP communications, so no DatagramSockets.
If you want UDP communication, the ServerSocket/Socket has little to do with your approach and you'll need to use DatagramSocket. Construct it:
with a port on the serverside - and do it only once.
without any port for the client side then qualify each and every DatagramPackets with the server address and port.
See a tutorial on Oracle site on Datagram client/server configurations.
Everytime you receive a new client TCP connection on your main server socket, you spin up another instance of a RequestProcessing class. The first time you start the RequestProcessing instance thread, it successfully binds to UDP port 7. But then the second client connects and you try to spin up another instance of RequestProcessing while another one already exists. That's not going to work.
You should probably amend you protocol such that the RequestProcessing class picks a new port each time and signals back through to the TCP socket which port was chosen.
But if it was me, I would do this. Have a single RequestProcessing instance for all clients. Given that your UDP echo socket is just sending back a response to the address from which the packet arrived from, you only need one instance of this class.
A TCP solution:
An utility class (I'm too lazy to write the same code in multiple places):
public class SocketRW {
Socket socket;
BufferedReader in;
PrintWriter out;
public SocketRW(Socket socket)
throws IOException
{
super();
this.socket = socket;
if(null!=socket) {
this.in=new BufferedReader(new InputStreamReader(socket.getInputStream()));
this.out=new PrintWriter(socket.getOutputStream());
}
}
public String readLine()
throws IOException {
return this.in.readLine();
}
public void println(String str) {
this.out.println(str);
}
public Socket getSocket() {
return socket;
}
public BufferedReader getIn() {
return in;
}
public PrintWriter getOut() {
return out;
}
}
Server code - no more datagrams, just using Input/Output streams from the sockets, wrapped as Reader/Writer using the utility
public class TCPServer
implements Runnable // in case you want to run the server on a separate thread
{
ServerSocket listenOnThis;
public TCPServer(int port)
throws IOException {
this.listenOnThis=new ServerSocket(port);
}
#Override
public void run() {
int client=0;
while(true) {
try {
Socket clientConn=this.listenOnThis.accept();
RequestProcessing processor=new RequestProcessing(clientConn, client++);
processor.start();
} catch (IOException e) {
break;
}
}
}
static public void main(String args[]) {
// port to be provided as the first CLI option
TCPServer server=new TCPServer(Integer.valueOf(args[0]));
server.run(); // or spawn it on another thread
}
}
class RequestProcessing extends Thread {
Socket channel;
int clientNo;
public RequestProcessing(Socket s, int i) {
channel = s;
clientNo = i;
}
public void run() {
try {
SocketRW utility=new SocketRW(this.channel);
while (true) {
String theString=utility.readLine().trim();
System.out.println("Client " + clientNo
+ " sent: " + theString);
if ("quit".equals(theString)) {
System.out.println("Client " + clientNo
+ " disconnected");
this.channel.close();
break;
}
theString = theString.toUpperCase();
utility.println(theString);
}
} catch (IOException e) {
System.err.println(e);
}
}
}
Client code - no more datagram sockets, using the same IO streams of the socket.
class TCPClient
implements Runnable // just in case you want to run multithreaded clients
{
Socket socket;
public TCPClient(InetAddress serverAddr, int port)
throws IOException {
this.socket=new Socket(serverAddr, port);
}
public void run() {
String theString="";
InputStreamReader isr = new InputStreamReader(System.in);
try {
SocketRW utility=new SocketRW(this.socket);
BufferedReader br = new BufferedReader(isr);
do {
System.out.print("Enter message: ");
theString = br.readLine().trim();
utility.println(theString);
System.out.println("Sent to server server: " + theString);
String received=utility.readLine();
System.out.println("Server reply: "+received);
} while (!"quit".equals(theString));
}
catch(IOException e) {
e.printStackTrace();
}
}
static public void main(String[] args) {
int port=Integer.valueOf(args[0]); // will throw if its no OK.
TCPClient client=new TCPClient(
InetAddress.getByName("192.168.109.128"),
port
);
client.run();
}
}

Client in Client-Server Chat CPU load 30-50%

A client connects to the Server, the server sends to the client 1 message and the client must wait for another message. While client waiting in while(true) loop it loads the CPU to 50%. I try to do this as simple I can to learn how it works.
P.S. All catch() already hiden to minimize code here.
Client:
public class SocketClient
{
String host;
int port;
static Socket connection;
BufferedReader bfr;
public SocketClient(String host, int port)
{
this.port = port;
this.host = host;
}
public void connect() throws UnknownHostException, IOException, Exception
{
connection = new Socket(new String(host), port);
System.out.println("Client is ready.");
bfr = new BufferedReader(new InputStreamReader(connection.getInputStream()));
}
public void readInput() throws IOException
{
String input;
if(input = bfr.readLine()) != null)
{
System.out.println(input);
}
}
public static void main(String[] args) throws UnknownHostException, IOException, Exception
{
SocketClient socketClient = new SocketClient("localhost", 19999);
try {
socketClient.connect();
try
{
while(true)
{
socketClient.readInput();
}
}
}
}
}
Server:
public class MultipleSocketServer implements Runnable {
private Socket connection;
private String TimeStamp;
private int ID;
static PrintWriter writer;
private static File file;
public static void main(String[] args)
throws FileNotFoundException, UnsupportedEncodingException
{
int port = 19999;
int count = 0;
file = new File("E:/test.txt");
writer = new PrintWriter(file, "UTF-8");
try
{
ServerSocket socket = new ServerSocket(port);
System.out.println("MultipleSocketServer Initialized");
while (true)
{
Socket connection = socket.accept();
Runnable runnable = new MultipleSocketServer(connection, ++count);
Thread thread = new Thread(runnable);
thread.start();
}
}
}
MultipleSocketServer(Socket s, int i) {
this.connection = s;
this.ID = i;
}
public void run() {
try {
System.out.println("Connected: " + connection.getLocalSocketAddress() + " at port " + connection.getPort());
writer.println(ID + ": " + connection.getLocalSocketAddress() + " at port " + connection.getPort());
writer.flush();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(connection.getOutputStream()));
writer.write("MultipleSocketServer repsonded at " + new java.util.Date().toString());
writer.write("\n");
writer.flush();
}
finally {
try {
connection.close();
}
}
}
}
Analysis
It seems the server side closes connection after sending the data.
The client side has the following infinite loop:
while (true)
{
socketClient.readInput();
}
The loop can cause the CPU consumption: the method bfr.readLine() method call will return null immediately after the connection is closed.
Solution
Please consider changing the loop of the client side to read until "the end-of-connection":
String input;
while ((input = bfr.readLine()) != null) {
System.out.println(input);
}

Categories

Resources