Welcome!

Welcome to the Official BlackBerry Support Community Forums. This is your resource to discuss support topics with your peers, and learn from each other. New to the forum? Please visit the ‘Getting Started’ link below.
inside custom component

Java Development

Reply
New Developer
malatorr
Posts: 20
Registered: ‎09-24-2009
Accepted Solution

UDP sending and receiving problems

Hello, I´m working on a java class to simplify udp connections between blackberry and other devices using udp datagrams.

 

I'm able to send packages. They arrive destination. But it does not receive any datagram...

 

 

 

UI (TestA.java):

 

 

/**
 * TestA.java
 * Copyright (C) 2001-2004 Research In Motion Limited. All rights reserved.
 */

import p2p.*;
import net.rim.device.api.ui.*;
import net.rim.device.api.ui.component.*;
import net.rim.device.api.ui.container.*;



public class TestA extends UiApplication implements ConnectionManagerListener{
	public ConnectorManager connectorManager;
	
	private boolean server = false;
	private String host = "192.168.1.255";
		
	private int destPort=5010;
	//private int srcPort =5010;  --> using same port
	private HelloWorldScreen helloWorldScreen;
	private boolean connected=false;
	private int sendCount=0;
	
	public static void main(String[] args){
		TestA theApp = new TestA();
		theApp.enterEventDispatcher();		
	}
	
	public TestA() {
		connectorManager = new ConnectorManager(this);
		helloWorldScreen = new HelloWorldScreen(this,connectorManager);
		pushScreen(helloWorldScreen);
	}
	
	public void connectOrServe(){
		setDestPort(helloWorldScreen.getPort());
		setHost(helloWorldScreen.getHost());
		
		if(server){
			connectorManager.startServer(getDestPort()
					,getDestPort());
			System.out.println("conecting as server");
		}else{ // client
			System.out.println("conecting as client");
			connectorManager.connectToHost(getHost(), getDestPort()
					,getDestPort());
		}		
	}
	
	public String getHost() {
		return host;
	}
	public void setHost(String host) {
		this.host = host;
	}
	public void notifyErrorSending(String s) {
		helloWorldScreen.setText("ERROR: sending data:"+s);
		
	}
	public void onDisconnect() {
		helloWorldScreen.setText("disconnected");
	}
	
	public void onReceiveData(String txt) {
		helloWorldScreen.setText(txt);
		System.out.println("received: "+txt);
	}
	
	public void notifyConnectedToClientOK(String remoteHost, int portOut,
			int portIn) {
		
		System.out.println("conecting to client OK");
		setConnected(true);
		
	}
	public void notifyConnectedToServerOK(String remoteHost, int portOut,
			int portIn) {
		System.out.println("connecting to server OK");
		setConnected(true);
		
		
	}
	public void notifyErrorReceiving() {
		helloWorldScreen.setText("ERROR: receiving data");	
		System.out.println("ERROR: receiving data");
	}
	public void notifyMyIP(String ip) {
		System.out.println("my IP is:"+ip);
	}
	public void notifyMyPort(int p) {
		System.out.println("source port is:"+p);
	}

	public boolean isConnected() {
		return connected;
	}

	public void setConnected(boolean connected) {
		System.out.println("setting connected to: "+connected);
		this.connected = connected;
	}

	public int getSendCount() {
		return sendCount;
	}

	public void setSendCount(int sendCount) {
		System.out.println("setting sendCount to: "+sendCount);
		this.sendCount = sendCount;
	}

	public int getDestPort() {
		return destPort;
	}

	public void setDestPort(int destPort) {
		this.destPort = destPort;
	}	
}

final class HelloWorldScreen extends MainScreen implements FieldChangeListener {
	private RichTextField tf; 
	private EditField host;
	private EditField port;
	private RichTextField errors;
	private ButtonField connect;
	//private ObjectChoiceField clientesChoice;
	//private String choicestrs[] = new String[0];
	private ButtonField exit;
	private EditField toSend;
	private ButtonField sendBtn;
	private ButtonField disconnect;
	private ConnectorManager myConn;
	private TestA _testa;
	
	public HelloWorldScreen(TestA testa, ConnectorManager con) {
		super();
		_testa = testa;
		myConn = con;
		LabelField title = new LabelField("TestA sample", LabelField.ELLIPSIS
				| LabelField.USE_ALL_WIDTH);
		setTitle(title);
		tf = new RichTextField("TestA");
		
		host = new EditField("host: ",testa.getHost());
		port = new EditField("port: ","5010");
		errors = new RichTextField();
		tf.setEditable(true);
		host.setEditable(true);
		port.setEditable(true);
		errors.setEditable(true);
		connect = new ButtonField("connect!");
		exit = new ButtonField("exit");
		toSend = new EditField("send txt: ","test");
		sendBtn = new ButtonField("Send!");
		disconnect = new ButtonField("disconnect");
		
		// ObjectChoiceField
		
		//clientesChoice = new ObjectChoiceField("Clientes conectados: ", choicestrs, 0);
		//add(clientesChoice);
		
		//choice.setChoices()
		
		add(tf);
		add(host);
		add(port);
		add(toSend);
		add(errors);
		add(connect);
		add(disconnect);
		add(sendBtn);
		add(exit);
		
		sendBtn.setChangeListener(this);
		exit.setChangeListener(this);
		connect.setChangeListener(this);
		disconnect.setChangeListener(this);
		
	}
	public String getTextToSend() {
		return toSend.getText();
	}

	public void setText(String txt){
		tf.setText(txt);		
	}
	public void setHost(String h){
		host.setText(h);
	}
	public void setPort(int p){
		port.setText(""+p);
	}
	public void setErrors(String e){
		errors.setText(e);		
	}
	public boolean checkFields(){
		if(host.getTextLength() < 2) return false;
		if(port.getTextLength() < 2) return false;
		return true;
	}
	public String getHost(){
		return host.getText();
	}
	public int getPort(){
		return Integer.parseInt( port.getText() );
		
	}
	public void fieldChanged(Field arg0, int arg1) {
		if(arg0 == exit ){
			System.out.println("TestA:exit btn pressed");
			myConn.closeConnection();
			System.exit(0);
		}
		if(arg0 == connect){
			System.out.println("TestA:connect btn pressed");
			if( checkFields()){
				if (!_testa.isConnected() ){
					System.out.println("fieldChanged:conecting!");
					_testa.connectOrServe();
				}
			}else{
				setErrors("ERROR: must fill dest host & port fields.");
			}
		}
		if(arg0 == sendBtn){
			System.out.println("TestA:sendBtn pressed");
			myConn.sendData(getTextToSend()+_testa.getSendCount());
			_testa.setSendCount(_testa.getSendCount()+1);
			
			return;
		}
		if(arg0 == disconnect){
			System.out.println("TestA:disconnect pressed");
			myConn.closeConnection();
		}		
	}
	public void setHost(EditField host) {
		this.host = host;
	}
}

 

ConnectorManager.java:

 

package p2p;

import java.io.IOException;
import java.util.Vector;

import javax.microedition.io.Connector;
import javax.microedition.io.Datagram;
import javax.microedition.io.UDPDatagramConnection;



public class ConnectorManager {
	private static int defaultPacketSize = 2048;  // is this OK???
	private static String protocol = "udp";
	
	private String _remoteHost;
	private int _destPort;
	private int _srcPort;
	private Object _listener;
	private int _maxPacketSize;
	
	private UDPDatagramConnection _connection;
	
	//flags
	private boolean _isConnectedOk=false;
	 
	// operating mode:
	private static int SERVER = 1;
	private static int CLIENT = 0;
	private int _connectionMode;
	private String _URL_CLIENT;
	private String _URL_SERVER;
	
	
	private SenderThread _senderThread;
	private ReceiverThread _receiverThread;
	private ConnectThread _connectThread;
	
	/* constructors & listeners
	 * 
	 */
	
	public ConnectorManager(Object listener){
		_listener = listener;	
		setMaxPacketSize(defaultPacketSize);
	}
	
	public int getMaxPacketSize(){
		return _maxPacketSize;		
	}
	
	public void setMaxPacketSize(int maxPacketSize){
		_maxPacketSize = maxPacketSize;
	}
	
	/*
	 * Server methods
	 * */
	
	// Starts listening at port _port;
	public void startServer(int srcPort, int destPort){
		_URL_SERVER  = protocol+"://:"+ destPort + ";" + srcPort + ";interface=wifi;deviceside=true";
		_connectionMode = SERVER;
		
		System.out.println("URL como SERVER:"+_URL_SERVER);
		
		 _connectThread = new ConnectThread();
		_connectThread.run();	
	}
	
	
	/*
	 * Client methods
	 */
	
	
	public void connectToHost(String remoteHost, int destPort, int srcPort){
		if(_connectThread != null){
			System.out.println("ConnectThread already running!");
			_connectThread.stopConnection();
			_connectThread.interrupt();
			
			try{
				if(_connection != null)
					_connection.close();
					_connection = null;
			}catch(Exception e){
				System.out.println("ERROR: unnable to close conection.");
				e.printStackTrace();
			}
		}
		
		_remoteHost = remoteHost;
		_destPort = destPort;
		_srcPort = srcPort;

		_URL_CLIENT  = protocol+"://" + remoteHost + ":"+ destPort + ";" + srcPort + ";interface=wifi;deviceside=true";
		_URL_SERVER = protocol+"://"  + ":"+ destPort + ";" + srcPort + ";interface=wifi;deviceside=true";
		
		System.out.println("URL as client:"+_URL_CLIENT);
		
		_connectionMode = CLIENT;
		_connectThread = new ConnectThread();
		
		_connectThread.run();	
	}
	
	public void closeConnection(){
		try {
			System.out.println("closing conection!");
			_isConnectedOk = false;
			
			
			_senderThread.quit();
			_connectThread.interrupt();
						
			_connectThread =null;
			_senderThread = null;
			_receiverThread = null;
			_connection.close();
			
			
		}catch (IOException e) {
			System.err.println(e.toString());
		}
	}
		
	/* P2P methods
	 * 
	 */
	
	public void sendData(String s){
		if( ! _isConnectedOk){
			notifyErrorSending("snDt:"+s);
			return;
		}	
		System.out.println("sendData: agregando a queue: "+s);
		_senderThread.addToQueue(s);		
	}
	
	/* this methods sends information back to the UI
	 * 
	 */
	public void notifyConnectedToServerOK(){
		((ConnectionManagerListener)_listener).notifyConnectedToServerOK(_remoteHost, _destPort, _srcPort);
	}
	
	public void notifyConnectedToClientOK(){
		((ConnectionManagerListener)_listener).notifyConnectedToClientOK(_remoteHost, _destPort, _srcPort);
	}
	
	public void notifyErrorSending(String s){
		((ConnectionManagerListener)_listener).notifyErrorSending(s);
	}
	
	public void notifyErrorReceiving(){
		((ConnectionManagerListener)_listener).notifyErrorReceiving();
	}
	
	public void notifyMyIP(String ip){
		((ConnectionManagerListener)_listener).notifyMyIP(ip);		
	}
	
	public void notifyMyPort(int p){
		((ConnectionManagerListener)_listener).notifyMyPort(p);
	}
	
	
	// threads:
	
	private class ConnectThread extends Thread {

		public void run() {
			try {
				String URL = _URL_CLIENT;
				
				if(_connectionMode == SERVER){
					URL = _URL_SERVER;
				}
				
				System.out.println("ConnectThread: connecting to URL="+URL);
				
				_connection = (UDPDatagramConnection)Connector.open(URL);
				
				_isConnectedOk = true;
				
				notifyMyIP(_connection.getLocalAddress());
				notifyMyPort(_connection.getLocalPort());

				_senderThread = new SenderThread();
				_receiverThread = new ReceiverThread();
				
				
				if(_connectionMode == SERVER){
					notifyConnectedToClientOK();
				}
				if(_connectionMode == CLIENT){
					notifyConnectedToServerOK();					
				}
				
			} catch (IOException e) {
				_isConnectedOk = false;
				System.err.println("ERROR: ConnectThread.run()" + e.toString());
				e.printStackTrace();
			}
		}

		public void stopConnection() {
			try {
				_connection.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			
		}
	}
	
	// receiver thread
	private class ReceiverThread implements Runnable{
		public ReceiverThread(){
			new Thread( this ).start();
		}

		public void run(){
			Datagram datagram;
			byte []data;
			
			while(_isConnectedOk ){	
				System.out.println("ReceiverThread run loop ...starting");
				try {
					data = new byte[getMaxPacketSize()];
					
					datagram = _connection.newDatagram(data.length);
					datagram.reset();
					
					datagram.setData(data, 0, data.length);
					
					_connection.receive(datagram);
					
					String receivedText = new String(data);
					
					System.out.println("awaiting data...");
					((ConnectionManagerListener)_listener).onReceiveData(receivedText);
					System.out.println("...data arrived.");
					
				}catch(IOException e){
					e.printStackTrace();
					notifyErrorReceiving();
				}
			}
						
		}
		
	}
	
	// sender thread
	public class SenderThread implements Runnable {
		private boolean quit = false;
		private Vector queue = new Vector();

		public SenderThread(){
			new Thread( this ).start();
		}
		
		private void sendQueueData(String s){
			try {
				System.out.println("SenderThread.run.sendQueueData "+s);
				//Datagram datagram = _connection.newDatagram(s.getBytes(),s.length(),_URL_CLIENT);
				Datagram datagram = _connection.newDatagram(s.getBytes(),s.length());
				System.out.println("sending (using connection_ URL) to:"+_URL_CLIENT);
				_connection.send(datagram);
				
			}catch(IOException e){
				notifyErrorSending("snQdt:"+s +"\n"+ e.toString());
				e.printStackTrace();
			}
		}

		public void run(){
			String s;

			while( !quit ){
				s = null;

				synchronized( queue ){
					if( queue.size() > 0 ){
						s = (String)queue.elementAt( 0 );
						queue.removeElementAt( 0 );
						
					} else {
						try {
							// nothing to send, lets wait
							queue.wait();
						}
						catch( InterruptedException e ){
							e.printStackTrace();
						}
					}
				}

				if( s != null ){
					// time to send
					sendQueueData(s);
				}
			}
		}

		public boolean addToQueue( String s ){
			synchronized( queue ){
				if( !quit ){
					System.out.println("addToQueue: adding data to queue: "+s);
					queue.addElement( s );
					queue.notify();
					return true;
				}

				return false;
			}
		}

		public void quit(){
			synchronized( queue ){
				System.out.println("SenderThread: quit()");
				quit = true;
				queue.notify();
			}
		}
	}
	
}

 needed interface for ConnectorManager.java:

 

package p2p;

public interface ConnectionManagerListener {

	public void onReceiveData(String txt);
	public void onDisconnect();
	public void notifyConnectedToServerOK(String remoteHost, int portOut, int portIn);
	public void notifyConnectedToClientOK(String remoteHost, int portOut, int portIn);
	public void notifyErrorSending(String s);
	public void notifyErrorReceiving();
	public void notifyMyIP(String h);
	public void notifyMyPort(int p);
}

 

 

 

Please use plain text.
Developer
Posts: 1,474
Registered: ‎04-14-2009

Re: UDP sending and receiving problems

[ Edited ]

1. Are you getting any exceptions?

 

2. Have you tested this against a known good client and server. For example, NetCat (nc) can be used in both roles.

 

3. I presume you're running a client or a server on a BlackBerry. Where is the peer running?

 

P.S. Not sure whether this applies here, but keep in mind that UDP doesn't work well on handheld software v4.5.0.82 and below.

Please use plain text.
Developer
Aviator168
Posts: 705
Registered: ‎09-10-2009
My Carrier: Verizon

Re: UDP sending and receiving problems

I don't know if this is bug in BB or not.

 

If you don't call getdata on the datagram after receive, you will not get the data. Worse yet, after while, the OS will kick out the application. I learned this a hard way since I was expecting that byte array to be filled with data after receive.

Please use plain text.
New Developer
malatorr
Posts: 20
Registered: ‎09-24-2009

Re: UDP sending and receiving problems

 

1. Are you getting any exceptions?

 

No exceptions 

 

2. Have you tested this against a known good client and server. For example, NetCat (nc) can be used in both roles.

 

I have a client/server running in two iphones, they can talk each other. The problem is when the iphone sends datagrams to the BB.

 

3. I presume you're running a client or a server on a BlackBerry. Where is the peer running?

 

I'm using a BB 8900 v4.6.1.250, will udp receiving work? sending works well in the upper code.

 

P.S. Not sure whether this applies here, but keep in mind that UDP doesn't work well on handheld software v4.5.0.82 and below.

 

 

 

Please use plain text.
New Developer
malatorr
Posts: 20
Registered: ‎09-24-2009

Re: UDP sending and receiving problems

@aviator168, I´m not sure I understand your suggestion. Can you check this code? (I put the getdata like you tell me)

 

 

 

while(_isConnectedOk ){	
				System.out.println("ReceiverThread run loop ...starting");
				try {
					data = new byte[getMaxPacketSize()];
					
					datagram = _connection.newDatagram(data.length);
					datagram.reset();
					
					datagram.setData(data, 0, data.length);
					
					_connection.receive(datagram);
					
					data = datagram.getData();
					
					String receivedText = new String(data);
					
					System.out.println("awaiting data...");
					((ConnectionManagerListener)_listener).onReceiveData(receivedText);
					System.out.println("...data arrived.");
					
				}catch(IOException e){
					e.printStackTrace();
					notifyErrorReceiving();
				}
			}

 

 

I'm going to check if this works now.

 

thanks

 

 

Please use plain text.
Developer
Aviator168
Posts: 705
Registered: ‎09-10-2009
My Carrier: Verizon

Re: UDP sending and receiving problems

[ Edited ]

Yes. That's exactly what I meant.

 

You can use a bit of optimization by moving (look below) outside of the while loop.

 

Oh. On the getData, use a different variable other than data so the datagram is kept the same on the receive.

data = new byte[getMaxPacketSize()];

datagram = _connection.newDatagram(data.length);
datagram.reset();

datagram.setData(data, 0, data.length);
Please use plain text.
Developer
Posts: 1,474
Registered: ‎04-14-2009

Re: UDP sending and receiving problems

What length are the UDP payloads your are transmitting, roughly (below 1024 bytes or above)?

Please use plain text.
Developer
Posts: 1,474
Registered: ‎04-14-2009

Re: UDP sending and receiving problems

[ Edited ]

1. The maximum UDP payload length that a BlackBerry can usually send is around 1300 bytes, if I recall correctly. I guess the same limit applies to what it can receive (it's perfectly fine to have a receive buffer larger than the limit, it's just that datagrams larger than the limit may not be received or may be truncated depending on various implementations of the UDP/IP stack).

 

2. Even if you manage to receive a datagram, you invoke onReceivedData on the listener, but that particular listener implementation attempts to modify the GUI without holding the event lock, which will terminate the receive thread and log the stack trace in the Event Log. Check the Event Log -- it might contain stack traces telling about this issue.

 

P.S. According to javadocs, you need to have a slash after the source port: e.g., udp://<dest ip>:<dest port>;<src port>/;interface=wifi...

 

P.P.S. You also seem to construct a ConnectThread, but then, instead of starting it, you invoke that object's run method on the calling thread (which seems to mean the UI/event thread).

 

P.P.P.S. After you receive a datagram, you don't check how long it is -- you create a String using the whole length of your receive buffer (2048 bytes).

Please use plain text.
Developer
Aviator168
Posts: 705
Registered: ‎09-10-2009
My Carrier: Verizon

Re: UDP sending and receiving problems

I missed the part of his code that in the receive thread he is creating a datagram of zero length. He won't get any data any how.

 

Datagram dg = dgConnector.newDatagram(1300);

 

dgConnector.receive(dg);

 

byte[] rData = dg.getData();

 

This will receive upto 1300 bytes. If the host send more than 1400 bytes, the last 100 bytes will be lost. He has an uninitialized byte array. The default length is 0 or in some JVMs, will generate an exception when he passed the length of that byte array to create the datagram.

 

Please use plain text.
Developer
Posts: 1,474
Registered: ‎04-14-2009

Re: UDP sending and receiving problems

[ Edited ]

You're right, Aviator168! The Datagram.reset() is spoiling everything (it's zeroing the length of the receive buffer). Let's hope that was the root cause of the issue!

 

The code you posted in the message above should be OK. You also need to read the offset (usually 0) (Datagram.getOffset()) and the length (Datagram.getLength()) of the received datagram in the array returned by the Datagram.getData() method. For example, new String(dg.getData, dg.getOffset(), dg.getLength(), <character encoding>).

Please use plain text.