sending sms(text) from java code to mobile phone in windows - java

Is it possible to send sms from windows machine to mobile phone.
I have searched a lot and got the following code.
Sender.java
package sms;
import java.util.Date;
public class Sender implements Runnable {
private static final long STANDARD=500;
private static final long LONG=2000;
private static final long VERYLONG=20000;
SerialConnection mySerial =null;
static final private char cntrlZ=(char)26;
String in, out;
Thread aThread=null;
private long delay=STANDARD;
String recipient=null;
String message=null;
private String csca="+6596845999"; // the message center
private SerialParameters defaultParameters= new SerialParameters ("COM2",9600,0,0,8,1,0);
public int step;
public int status=-1;
public long messageNo=-1;
public Sender(String recipient, String message){
this.recipient=recipient;
this.message=message;
}
/**
* connect to the port and start the dialogue thread
*/
public int send () throws Exception{
SerialParameters params = defaultParameters;
mySerial =new SerialConnection (params);
mySerial.openConnection();
aThread=new Thread(this);
aThread.start() ;
//log("start");
return 0;
}
/**
* implement the dialogue thread,
* message / response via steps,
* handle time out
*/
public void run(){
boolean timeOut=false;
long startTime=(new Date()).getTime();
while ((step <7) && (!timeOut)){
// log(""+((new Date()).getTime() - startTime);
//check where we are in specified delay
timeOut=((new Date()).getTime() - startTime)>delay;
//if atz does not work, type to send cntrlZ and retry, in case a message was stuck
if (timeOut && (step==1)) {
step=-1;
mySerial.send( ""+cntrlZ);
}
//read incoming string
String result= mySerial.getIncommingString() ;
// log ("<- "+result+"\n--------");
int expectedResult=-1;
try{
//log ("Step:"+step);
switch (step){
case 0:
mySerial.send("atz");
delay=LONG;
startTime=(new Date()).getTime();
break;
case 1:
delay=STANDARD;
mySerial.send("ath0");
startTime=(new Date()).getTime();
break;
case 2:
expectedResult=result.indexOf("OK");
//log ("received ok ="+expectedResult);
if (expectedResult>-1){
mySerial.send("at+cmgf=1");
startTime=(new Date()).getTime();
}else{
step=step-1;
}
break;
case 3:
expectedResult=result.indexOf("OK");
// log ("received ok ="+expectedResult);
if (expectedResult>-1){
mySerial.send("at+csca=\""+csca+"\"");
startTime=(new Date()).getTime();
}else{
step=step-1;
}
break;
case 4:
expectedResult=result.indexOf("OK");
// log ("received ok ="+expectedResult);
if (expectedResult>-1){
mySerial.send("at+cmgs=\""+recipient+"\"");
startTime=(new Date()).getTime();
}else{
step=step-1;
}
break;
case 5:
expectedResult=result.indexOf(">");
// log ("received ok ="+expectedResult);
if (expectedResult>-1){
mySerial.send(message+cntrlZ);
startTime=(new Date()).getTime();
}else{
step=step-1;
}
delay=VERYLONG;//waitning for message ack
break;
case 6:
expectedResult=result.indexOf("OK");
//read message number
if (expectedResult>-1){
int n=result.indexOf("CMGS:");
result=result.substring(n+5);
n=result.indexOf("\n");
status=0;
messageNo=Long.parseLong(result.substring(0,n).trim() );
log ("sent message no:"+messageNo);
}else{
step=step-1;
}
break;
}
step=step+1;
aThread.sleep(100);
}catch (Exception e){
e.printStackTrace();
}
}
mySerial.closeConnection() ;
//if timed out set status
if (timeOut ) {
status=-2;
log("*** time out at step "+step+"***");
}
}
/**
* logging function, includes date and class name
*/
private void log(String s){
System.out.println (new java.util.Date()+":"+this.getClass().getName()+":"+s);
}
}
SerialConnection.java
package sms;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.TooManyListenersException;
import javax.comm.CommPortIdentifier;
import javax.comm.CommPortOwnershipListener;
import javax.comm.NoSuchPortException;
import javax.comm.PortInUseException;
import javax.comm.SerialPort;
import javax.comm.SerialPortEvent;
import javax.comm.SerialPortEventListener;
import javax.comm.UnsupportedCommOperationException;
/**
A class that handles the details of a serial connection. Reads from one
TextArea and writes to a second TextArea.
Holds the state of the connection.
*/
public class SerialConnection implements SerialPortEventListener,
CommPortOwnershipListener {
private SerialParameters parameters;
private OutputStream os;
private InputStream is;
private KeyHandler keyHandler;
private CommPortIdentifier portId;
private SerialPort sPort;
private boolean open;
private String receptionString="";
public String getIncommingString(){
byte[] bVal= receptionString.getBytes();
receptionString="";
return new String (bVal);
}
public SerialConnection(SerialParameters parameters) {
this.parameters = parameters;
open = false;
}
/**
Attempts to open a serial connection and streams using the parameters
in the SerialParameters object. If it is unsuccesfull at any step it
returns the port to a closed state, throws a
<code>SerialConnectionException</code>, and returns.
Gives a timeout of 30 seconds on the portOpen to allow other applications
to reliquish the port if have it open and no longer need it.
*/
public void openConnection() throws SerialConnectionException {
// System.out.println("OK 0 ");
// Obtain a CommPortIdentifier object for the port you want to open.
try {
// System.out.println(parameters.getPortName());
portId = CommPortIdentifier.getPortIdentifier(parameters.getPortName());
} catch (NoSuchPortException e) {
// System.out.println("Yes the problem is here 1 ");
e.printStackTrace();
// throw new SerialConnectionException(e.getMessage());
}catch(Exception e)
{
// System.out.println("ErrorErrorErrorError");
e.printStackTrace();
}
//System.out.println(portId);
//System.out.println("OK 1 ");
// Open the port represented by the CommPortIdentifier object. Give
// the open call a relatively long timeout of 30 seconds to allow
// a different application to reliquish the port if the user
// wants to.
try {
sPort = (SerialPort)portId.open("SMSConnector", 30000);
} catch (PortInUseException e) {
throw new SerialConnectionException(e.getMessage());
}
//System.out.println("OK 2 ");
sPort.sendBreak(1000);
// Set the parameters of the connection. If they won't set, close the
// port before throwing an exception.
try {
setConnectionParameters();
} catch (SerialConnectionException e) {
sPort.close();
throw e;
}
// System.out.println("OK 3 ");
// Open the input and output streams for the connection. If they won't
// open, close the port before throwing an exception.
try {
os = sPort.getOutputStream();
is = sPort.getInputStream();
} catch (IOException e) {
sPort.close();
throw new SerialConnectionException("Error opening i/o streams");
}
//System.out.println("OK 4 ");
/*
// Create a new KeyHandler to respond to key strokes in the
// messageAreaOut. Add the KeyHandler as a keyListener to the
// messageAreaOut.
keyHandler = new KeyHandler(os);
messageAreaOut.addKeyListener(keyHandler);
*/
// Add this object as an event listener for the serial port.
try {
sPort.addEventListener(this);
} catch (TooManyListenersException e) {
sPort.close();
throw new SerialConnectionException("too many listeners added");
}
//System.out.println("OK 5 ");
// Set notifyOnDataAvailable to true to allow event driven input.
sPort.notifyOnDataAvailable(true);
// Set notifyOnBreakInterrup to allow event driven break handling.
sPort.notifyOnBreakInterrupt(true);
// Set receive timeout to allow breaking out of polling loop during
// input handling.
try {
sPort.enableReceiveTimeout(30);
} catch (UnsupportedCommOperationException e) {
}
//System.out.println("OK 6 ");
// Add ownership listener to allow ownership event handling.
portId.addPortOwnershipListener(this);
open = true;
}
/**
Sets the connection parameters to the setting in the parameters object.
If set fails return the parameters object to origional settings and
throw exception.
*/
public void setConnectionParameters() throws SerialConnectionException {
// Save state of parameters before trying a set.
int oldBaudRate = sPort.getBaudRate();
int oldDatabits = sPort.getDataBits();
int oldStopbits = sPort.getStopBits();
int oldParity = sPort.getParity();
int oldFlowControl = sPort.getFlowControlMode();
// Set connection parameters, if set fails return parameters object
// to original state.
try {
sPort.setSerialPortParams(parameters.getBaudRate(),
parameters.getDatabits(),
parameters.getStopbits(),
parameters.getParity());
} catch (UnsupportedCommOperationException e) {
parameters.setBaudRate(oldBaudRate);
parameters.setDatabits(oldDatabits);
parameters.setStopbits(oldStopbits);
parameters.setParity(oldParity);
throw new SerialConnectionException("Unsupported parameter");
}
// Set flow control.
try {
sPort.setFlowControlMode(parameters.getFlowControlIn()
| parameters.getFlowControlOut());
} catch (UnsupportedCommOperationException e) {
throw new SerialConnectionException("Unsupported flow control");
}
}
/**
Close the port and clean up associated elements.
*/
public void closeConnection() {
// If port is alread closed just return.
if (!open) {
return;
}
// Remove the key listener.
// messageAreaOut.removeKeyListener(keyHandler);
// Check to make sure sPort has reference to avoid a NPE.
if (sPort != null) {
try {
// close the i/o streams.
os.close();
is.close();
} catch (IOException e) {
System.err.println(e);
}
// Close the port.
sPort.close();
// Remove the ownership listener.
portId.removePortOwnershipListener(this);
}
open = false;
}
/**
Send a one second break signal.
*/
public void sendBreak() {
sPort.sendBreak(1000);
}
/**
Reports the open status of the port.
#return true if port is open, false if port is closed.
*/
public boolean isOpen() {
return open;
}
/**
Handles SerialPortEvents. The two types of SerialPortEvents that this
program is registered to listen for are DATA_AVAILABLE and BI. During
DATA_AVAILABLE the port buffer is read until it is drained, when no more
data is availble and 30ms has passed the method returns. When a BI
event occurs the words BREAK RECEIVED are written to the messageAreaIn.
*/
public void serialEvent(SerialPortEvent e) {
// Create a StringBuffer and int to receive input data.
StringBuffer inputBuffer = new StringBuffer();
int newData = 0;
// Determine type of event.
switch (e.getEventType()) {
// Read data until -1 is returned. If \r is received substitute
// \n for correct newline handling.
case SerialPortEvent.DATA_AVAILABLE:
while (newData != -1) {
try {
newData = is.read();
if (newData == -1) {
break;
}
if ('\r' == (char)newData) {
inputBuffer.append('\n');
} else {
inputBuffer.append((char)newData);
}
} catch (IOException ex) {
System.err.println(ex);
return;
}
}
// Append received data to messageAreaIn.
receptionString=receptionString+ (new String(inputBuffer));
//System.out.print("<-"+receptionString);
break;
// If break event append BREAK RECEIVED message.
case SerialPortEvent.BI:
receptionString=receptionString+("\n--- BREAK RECEIVED ---\n");
}
}
/**
Handles ownership events. If a PORT_OWNERSHIP_REQUESTED event is
received a dialog box is created asking the user if they are
willing to give up the port. No action is taken on other types
of ownership events.
*/
public void ownershipChange(int type) {
/*
if (type == CommPortOwnershipListener.PORT_OWNERSHIP_REQUESTED) {
PortRequestedDialog prd = new PortRequestedDialog(parent);
}
*/
}
/**
A class to handle <code>KeyEvent</code>s generated by the messageAreaOut.
When a <code>KeyEvent</code> occurs the <code>char</code> that is
generated by the event is read, converted to an <code>int</code> and
writen to the <code>OutputStream</code> for the port.
*/
class KeyHandler extends KeyAdapter {
OutputStream os;
/**
Creates the KeyHandler.
#param os The OutputStream for the port.
*/
public KeyHandler(OutputStream os) {
super();
this.os = os;
}
/**
Handles the KeyEvent.
Gets the <code>char</char> generated by the <code>KeyEvent</code>,
converts it to an <code>int</code>, writes it to the <code>
OutputStream</code> for the port.
*/
public void keyTyped(KeyEvent evt) {
char newCharacter = evt.getKeyChar();
if ((int)newCharacter==10) newCharacter = '\r';
System.out.println ((int)newCharacter);
try {
os.write((int)newCharacter);
} catch (IOException e) {
System.err.println("OutputStream write error: " + e);
}
}
}
public void send(String message) {
byte[] theBytes= (message+"\n").getBytes();
for (int i=0; i<theBytes.length;i++){
char newCharacter = (char)theBytes[i];
if ((int)newCharacter==10) newCharacter = '\r';
try {
os.write((int)newCharacter);
} catch (IOException e) {
System.err.println("OutputStream write error: " + e);
}
}
//System.out.println (">'" +message +"' sent");
}
}
SerialConnection.java
package sms;
public class SerialConnectionException extends Exception {
/**
* Constructs a <code>SerialConnectionException</code>
* with the specified detail message.
*
* #param s the detail message.
*/
public SerialConnectionException(String str) {
super(str);
}
/**
* Constructs a <code>SerialConnectionException</code>
* with no detail message.
*/
public SerialConnectionException() {
super();
}
}
SerialParameters.java
package sms;
import javax.comm.SerialPort;
/**
A class that stores parameters for serial ports.
*/
public class SerialParameters {
private String portName;
private int baudRate;
private int flowControlIn;
private int flowControlOut;
private int databits;
private int stopbits;
private int parity;
/**
Default constructer. Sets parameters to no port, 9600 baud, no flow
control, 8 data bits, 1 stop bit, no parity.
*/
public SerialParameters () {
this("",
9600,
SerialPort.FLOWCONTROL_NONE,
SerialPort.FLOWCONTROL_NONE,
SerialPort.DATABITS_8,
SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE );
}
/**
Paramaterized constructer.
#param portName The name of the port.
#param baudRate The baud rate.
#param flowControlIn Type of flow control for receiving.
#param flowControlOut Type of flow control for sending.
#param databits The number of data bits.
#param stopbits The number of stop bits.
#param parity The type of parity.
*/
public SerialParameters(String portName,
int baudRate,
int flowControlIn,
int flowControlOut,
int databits,
int stopbits,
int parity) {
this.portName = portName;
this.baudRate = baudRate;
this.flowControlIn = flowControlIn;
this.flowControlOut = flowControlOut;
this.databits = databits;
this.stopbits = stopbits;
this.parity = parity;
}
/**
Sets port name.
#param portName New port name.
*/
public void setPortName(String portName) {
this.portName = portName;
}
/**
Gets port name.
#return Current port name.
*/
public String getPortName() {
return portName;
}
/**
Sets baud rate.
#param baudRate New baud rate.
*/
public void setBaudRate(int baudRate) {
this.baudRate = baudRate;
}
/**
Sets baud rate.
#param baudRate New baud rate.
*/
public void setBaudRate(String baudRate) {
this.baudRate = Integer.parseInt(baudRate);
}
/**
Gets baud rate as an <code>int</code>.
#return Current baud rate.
*/
public int getBaudRate() {
return baudRate;
}
/**
Gets baud rate as a <code>String</code>.
#return Current baud rate.
*/
public String getBaudRateString() {
return Integer.toString(baudRate);
}
/**
Sets flow control for reading.
#param flowControlIn New flow control for reading type.
*/
public void setFlowControlIn(int flowControlIn) {
this.flowControlIn = flowControlIn;
}
/**
Sets flow control for reading.
#param flowControlIn New flow control for reading type.
*/
public void setFlowControlIn(String flowControlIn) {
this.flowControlIn = stringToFlow(flowControlIn);
}
/**
Gets flow control for reading as an <code>int</code>.
#return Current flow control type.
*/
public int getFlowControlIn() {
return flowControlIn;
}
/**
Gets flow control for reading as a <code>String</code>.
#return Current flow control type.
*/
public String getFlowControlInString() {
return flowToString(flowControlIn);
}
/**
Sets flow control for writing.
#param flowControlIn New flow control for writing type.
*/
public void setFlowControlOut(int flowControlOut) {
this.flowControlOut = flowControlOut;
}
/**
Sets flow control for writing.
#param flowControlIn New flow control for writing type.
*/
public void setFlowControlOut(String flowControlOut) {
this.flowControlOut = stringToFlow(flowControlOut);
}
/**
Gets flow control for writing as an <code>int</code>.
#return Current flow control type.
*/
public int getFlowControlOut() {
return flowControlOut;
}
/**
Gets flow control for writing as a <code>String</code>.
#return Current flow control type.
*/
public String getFlowControlOutString() {
return flowToString(flowControlOut);
}
/**
Sets data bits.
#param databits New data bits setting.
*/
public void setDatabits(int databits) {
this.databits = databits;
}
/**
Sets data bits.
#param databits New data bits setting.
*/
public void setDatabits(String databits) {
if (databits.equals("5")) {
this.databits = SerialPort.DATABITS_5;
}
if (databits.equals("6")) {
this.databits = SerialPort.DATABITS_6;
}
if (databits.equals("7")) {
this.databits = SerialPort.DATABITS_7;
}
if (databits.equals("8")) {
this.databits = SerialPort.DATABITS_8;
}
}
/**
Gets data bits as an <code>int</code>.
#return Current data bits setting.
*/
public int getDatabits() {
return databits;
}
/**
Gets data bits as a <code>String</code>.
#return Current data bits setting.
*/
public String getDatabitsString() {
switch(databits) {
case SerialPort.DATABITS_5:
return "5";
case SerialPort.DATABITS_6:
return "6";
case SerialPort.DATABITS_7:
return "7";
case SerialPort.DATABITS_8:
return "8";
default:
return "8";
}
}
/**
Sets stop bits.
#param stopbits New stop bits setting.
*/
public void setStopbits(int stopbits) {
this.stopbits = stopbits;
}
/**
Sets stop bits.
#param stopbits New stop bits setting.
*/
public void setStopbits(String stopbits) {
if (stopbits.equals("1")) {
this.stopbits = SerialPort.STOPBITS_1;
}
if (stopbits.equals("1.5")) {
this.stopbits = SerialPort.STOPBITS_1_5;
}
if (stopbits.equals("2")) {
this.stopbits = SerialPort.STOPBITS_2;
}
}
/**
Gets stop bits setting as an <code>int</code>.
#return Current stop bits setting.
*/
public int getStopbits() {
return stopbits;
}
/**
Gets stop bits setting as a <code>String</code>.
#return Current stop bits setting.
*/
public String getStopbitsString() {
switch(stopbits) {
case SerialPort.STOPBITS_1:
return "1";
case SerialPort.STOPBITS_1_5:
return "1.5";
case SerialPort.STOPBITS_2:
return "2";
default:
return "1";
}
}
/**
Sets parity setting.
#param parity New parity setting.
*/
public void setParity(int parity) {
this.parity = parity;
}
/**
Sets parity setting.
#param parity New parity setting.
*/
public void setParity(String parity) {
if (parity.equals("None")) {
this.parity = SerialPort.PARITY_NONE;
}
if (parity.equals("Even")) {
this.parity = SerialPort.PARITY_EVEN;
}
if (parity.equals("Odd")) {
this.parity = SerialPort.PARITY_ODD;
}
}
/**
Gets parity setting as an <code>int</code>.
#return Current parity setting.
*/
public int getParity() {
return parity;
}
/**
Gets parity setting as a <code>String</code>.
#return Current parity setting.
*/
public String getParityString() {
switch(parity) {
case SerialPort.PARITY_NONE:
return "None";
case SerialPort.PARITY_EVEN:
return "Even";
case SerialPort.PARITY_ODD:
return "Odd";
default:
return "None";
}
}
/**
Converts a <code>String</code> describing a flow control type to an
<code>int</code> type defined in <code>SerialPort</code>.
#param flowControl A <code>string</code> describing a flow control type.
#return An <code>int</code> describing a flow control type.
*/
private int stringToFlow(String flowControl) {
if (flowControl.equals("None")) {
return SerialPort.FLOWCONTROL_NONE;
}
if (flowControl.equals("Xon/Xoff Out")) {
return SerialPort.FLOWCONTROL_XONXOFF_OUT;
}
if (flowControl.equals("Xon/Xoff In")) {
return SerialPort.FLOWCONTROL_XONXOFF_IN;
}
if (flowControl.equals("RTS/CTS In")) {
return SerialPort.FLOWCONTROL_RTSCTS_IN;
}
if (flowControl.equals("RTS/CTS Out")) {
return SerialPort.FLOWCONTROL_RTSCTS_OUT;
}
return SerialPort.FLOWCONTROL_NONE;
}
/**
Converts an <code>int</code> describing a flow control type to a
<code>String</code> describing a flow control type.
#param flowControl An <code>int</code> describing a flow control type.
#return A <code>String</code> describing a flow control type.
*/
String flowToString(int flowControl) {
switch(flowControl) {
case SerialPort.FLOWCONTROL_NONE:
return "None";
case SerialPort.FLOWCONTROL_XONXOFF_OUT:
return "Xon/Xoff Out";
case SerialPort.FLOWCONTROL_XONXOFF_IN:
return "Xon/Xoff In";
case SerialPort.FLOWCONTROL_RTSCTS_IN:
return "RTS/CTS In";
case SerialPort.FLOWCONTROL_RTSCTS_OUT:
return "RTS/CTS Out";
default:
return "None";
}
}
}
But when i am trying to run the code, i am getting following error:-
Error loading SolarisSerial: java.lang.UnsatisfiedLinkError: no SolarisSerialParallel in java.library.path
Caught java.lang.UnsatisfiedLinkError: com.sun.comm.SolarisDriver.readRegistrySerial(Ljava/util/Vector;Ljava/lang/String;)I while loading driver com.sun.comm.SolarisDriver
is this code that i have taken OS specific. Or there are any other way to send sms(txt) from pc to mobile.
Please help me.

I think this is a very complicated approach.
If you are developing a commercial application, there are many companies which provide web services that you could use. You just send the sms text to the webservice, and they will send the sms for you, no need to reinvent the wheel.
Unless you are developing a webservice like this yourself!

Your code relies on a JNI driver class "com.sun.comm.SolarisDriver". Your program can't locate the dinamic library SolarisSerialParallel.
The Java virtual machine needs to be able to find the native library. To do this, set the library path adding the path to the library as follows:
Unix or Linux based systems:
LD_LIBRARY_PATH=$LD_LIBRARY_PATH;'/opt/whatever/SolarisSerialParallel.so'
export LD_LIBRARY_PATH
Windows (I guess that in this case, you'll need to search the appropiate DLL, as the one required at your question seems to be exclusive for Solaris environments):
set PATH=%path%;C:\whatever\SolarisSerialParallel.dll
Note that you can point it to the containing directory, if more libraries ar needed.
Reference: Java Native Interface Specification

Related

Creating a Server class and Client class for a custom chatroom in Java

Explanation of my Program
I have created a chat room server using Java that consists of 3 classes, a server class, a user class and a chat room class. I have also created a client class to interact with the Server. The server creates a user object every time a client connects to the server. Each user object keeps track of the chatrooms the user is in and runs a thread that listens for user input. A server object loops through each chatroom to see if it has been used in the last week.
The Problem
I'm wondering how to actually make a connection to my server with a client object. Currently I open two instances of eclipse. I run my server program in one, and my client program in the other, but I receive nothing in the console of either, which should happen because the server should send information to the client, which the client would then display on the console. The person on the client end could then give input that the client would take and send to the server.
I'm wondering why nothing is happening right now, and what improvements I can make.
Main Files
Server.java
/*
* Creates a server that can host up to 10 clients who can join chat rooms, post messages in chatrooms, and view posts made by other clients within the same chat room
*/
public class Server implements Runnable{
protected ArrayList<User> userList; //A list of users, where each user is a client connected to the server
protected LinkedList<Chatroom> chatRooms; //A list of chatrooms where each client can post messages in, where messages can be seen by all clients in the chatroom
private ServerSocket serverSocket; //The socket for the server
/*
* Constructor for the server class. Initializes the server attributes,
*/
public Server() {
this.userList = new ArrayList<User>(10);
this.chatRooms = new LinkedList<Chatroom>();
try {
this.serverSocket = new ServerSocket(5000);
}catch (IOException e) {
e.printStackTrace();
}
}
/*
* Creates a new user when a client connects to the server, and starts a user thread
*/
public void createUser() {
try {
Socket userSocket = serverSocket.accept();
Thread user = new Thread(new User(userSocket, this));
user.start();
} catch (IOException e) {
e.printStackTrace();
}
}
/*
* Creates a chatroom for clients to interact in
* #param roomName: The name of the chat room to be created
*/
protected Chatroom createChatRoom(String roomName) {
Chatroom room = new Chatroom(roomName);
return room;
}
/*
* Receives messages from clients and performs actions based on the requests of the client
* (non-Javadoc)
* #see java.lang.Thread#run()
*/
public void run() {
long currentTime;
while(true) {
try {
currentTime = System.currentTimeMillis() / 1000;
//Loop through each chatroom and check if the chatroom has been used(joined or had a message sent to it) and remove that chatroom if it hasn't been used in a week
for (int i = 0; i < chatRooms.size(); i++) {
if (currentTime - 604800 >= chatRooms.get(i).dateLastUsed) {
chatRooms.remove(i);
//Also remove the chatroom from clients lists of chatrooms
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String args[]) {
Server server = new Server ();
server.run();
}
}
Client.java
public class Client extends Thread{
private String ip = "127.0.0.1";
private int port = 5000 ;
private Socket socket;
private DataInputStream iStream;
private DataOutputStream oStream;
private String input;
public Client() {
try {
this.socket = new Socket(ip, port);
this.iStream = new DataInputStream(socket.getInputStream());
this.oStream = new DataOutputStream(socket.getOutputStream());
}catch (Exception e) {
e.printStackTrace();
}
}
/*
* Sends a message to the user
* #param message: The message to be sent to the user
*/
protected void send (String message) {
try {
oStream.writeUTF(message);
} catch (IOException e) {
e.printStackTrace();
}
}
/*
* Closes the connection to the client
*/
protected void close () {
try {
iStream.close();
oStream.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/*
* Runs a thread for the client to constantly receive the clients input(non-Javadoc)
* #see java.lang.Thread#run()
*/
public void run() {
try {
Scanner reader = new Scanner(System.in);
input = iStream.readUTF();
String userInput;//Check if there is input from the user
while (input != null) {
input = iStream.readUTF();
System.out.println(input);
userInput = reader.next();
oStream.writeUTF(userInput);
}
reader.close();
}catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String args[]) {
Client client = new Client();
client.run();
}
}
Objects used by Server.java
User.java
//Each user represents a client that has connected to the server
public class User implements Runnable{
private DataInputStream inputStream;
private DataOutputStream outputStream;
private Socket socket;
private String name;
protected LinkedList<Chatroom> chatRooms;
private String input;
private Server server;
/*
* User Constructor, create a user for each client connecting to the server
* #socket The socket that the user will be communicated through
* The client is prompted to create a name for themself, they are they prompted to do an action.
*/
public User(Socket socket, Server server) {
this.socket = socket;
this.server = server;
try {
inputStream = new DataInputStream(socket.getInputStream());
outputStream = new DataOutputStream(socket.getOutputStream());
outputStream.writeUTF("Enter a name");
this.name = inputStream.readUTF();
String message = "Create a chatroom: create \nList Chat Rooms: list \n Join Chat Room: join \n Leave Chat Room: Leave";
send(message);
} catch (IOException e) {
}
}
/*
* Returns the current amount of chatrooms this user is in
*/
protected int chatRoomLength () {
return this.chatRooms.size();
}
/*
* Gets the most recent input from the user
*/
protected String getInput() {
return input;
}
/*
* Puts a user/client in a chatroom
* #param cRoom: The chatroom that the user will join
*/
protected void joinRoom (Chatroom cRoom) {
chatRooms.add(cRoom);
}
/*
* Removes a user/client from a chatroom
*/
protected void leaveRoom (Chatroom c) {
chatRooms.removeFirstOccurrence(c);
}
/*
* Sends a message to the user
* #param message: The message to be sent to the user
*/
protected void send (String message) {
try {
outputStream.writeUTF(message);
} catch (IOException e) {
}
}
/*
* Closes the connection to the client
*/
protected void close () {
try {
inputStream.close();
outputStream.close();
socket.close();
} catch (IOException e) {}
}
/*
* Runs a thread for the client to constantly receive the clients input(non-Javadoc)
* #see java.lang.Thread#run()
*/
public void run() {
try {
input = inputStream.readUTF(); //Check if there is input from the user
//if the user has disconnected from the server, remove them from the list
if (input == null) {
this.close();
this.server.userList.remove(this);
}else if (input.equals("create")){ //create a chat room
this.send("Name the Chatroom");
input = this.getInput();
Chatroom c = this.server.createChatRoom(input);
this.joinRoom(c);
}else if (input.equals("list")) { //List the current chatrooms
String rooms = "";
for (int j = 0; j< server.chatRooms.size(); j++) {
rooms = rooms + server.chatRooms.get(j).getName() + "\n";
}
this.send(rooms);
}else if (input.equals("join")) { //Join the user to a chat room
int end = chatRooms.size();
if (end == 0) {
this.send("There's currently no chat rooms");
}else {
this.send("Which room would you like to join");
input = this.getInput();
for (int k = 0; k < end; k++) {
if (chatRooms.get(k).getName().equals(input)) {
Chatroom joinRoom = chatRooms.get(k);
this.joinRoom(joinRoom);
String message = "Chatroom " + input + " messages. \n";
//Print the current messages in the chatroom to the user
for (int j = 0; j < joinRoom.messages.size(); j++ ) {
message = message + joinRoom.messages.get(j) + "\n";
}
this.send(message);
} else if (k == end - 1) {
this.send("There's no chat rooms by that name");
}
}
}
}else if (input.equals("leave")) { //Remove the user from a chatroom
int end = this.chatRoomLength(); //if the chatroom list of the user is empty
if (end == 0) {
this.send("You are not in any Chat Rooms");
}else {
this.send("Which room would you like to leave");
input = this.getInput();
for (int m = 0; m < end; m++) { //find the chatroom by the same name
if (this.chatRooms.get(m).getName().equals(input)) {
this.chatRooms.remove(m);
this.send("Great! You've been removed from" + input);
} else if (m == end - 1) {
this.send("You're not in a chatroom named" + input);
}
}
}
}else { //All other input is interpreted as a message to be posted in the chatrooms that the user is in
int end = this.chatRoomLength();
if (end == 0) {
this.send("You can't write to any chat rooms because you are not in any");
}
for (int m = 0; m < end; m++) { //Add the users message to ALL the chatrooms the user is in
Chatroom c = this.chatRooms.get(m);
c.addMessage(input);
//Send this added message to all the users in this chatroom
for (int n = 0; n < c.users.size(); n++) {
User u = c.users.get(n);
u.send("Chatroom" + c.getName() + ":" + input);
}
}
}
}catch (IOException e) {
}
}
}
Chatroom.java
public class Chatroom {
private String name; //Name of the chatroom
protected LinkedList<String> messages; //List of text messages that have been sent by users to the chatroom and are displayed in the chatroom
protected long dateLastUsed; //The last time the chatroom was joined or had a message sent to it
protected LinkedList<User> users; //The clients/users that are currently in the chatroom
/*
* Chatroom constructor
* #param name The name of the chatroom, as determined by the user creating it
*/
public Chatroom(String name) {
dateLastUsed = System.currentTimeMillis() / 1000; //Sent the time that the chatroom was used last to the current UNIX Epoch time
messages = new LinkedList<String>();
this.name = name;
}
/*
* Adds a message into the chatroom
* #param message The message to be added to the chatroom
*/
protected void addMessage(String message) {
messages.add(message);
dateLastUsed = System.currentTimeMillis() / 1000;
}
/*
* Returns the name of the chatroom
* #return String equal to the name of the chatroom
*/
protected String getName() {
return this.name;
}
}
First of all, there is no call to createUser() which has the code to accept the socket connection.
Next is this code in run() function,
try {
Scanner reader = new Scanner(System.in);
input = iStream.readUTF();
String userInput;//Check if there is input from the user
while (input != null) {
input = iStream.readUTF();
System.out.println(input);
userInput = reader.next();
oStream.writeUTF(userInput);
}
Once the socket is accepted the server prints Enter a name which is stored input,
inside the while loop there is another readUTF() call.
The problem with readUTF() call is that it's blocking, ie. if there isn't a writeUTF() call from the server it waits for data.
I solved the problem by the following snippet,
try {
Scanner reader = new Scanner(System.in);
String userInput;//Check if there is input from the user
do {
input = iStream.readUTF();
System.out.println(input);
userInput = reader.next();
oStream.writeUTF(userInput);
} while (input != null);
reader.close();
}
Even this isn't the optimum solution as it would need Server to write something into the stream every time.
Another solution would be to read and write using different threads that way we would be able to create a full-duplex like chat, unlike this half duplex.
Then there were some NPE due to no initialization of chatRooms LinkedList,
Plus some logical errors like, not adding chatRooms to its List.
Wrong Code:
protected Chatroom createChatRoom(String roomName) {
Chatroom room = new Chatroom(roomName);
return room;
}
Corrected Code:
protected Chatroom createChatRoom(String roomName) {
Chatroom room = new Chatroom(roomName);
this.chatRooms.add(room);
return room;
}
The best way to solve all the bugs would be Github and a few contributors :p

Raspberry pi with java application high CPU usage

I have a java application running on my raspberry pi but it crashes most of the time. Whenever it crashes it usually has a very high CPU usage (> 100%) from java. How my application works: I have a RFID reader that reads tags and whenever a tag is read, a messageReceived method is called. That method stores the read tags in a specific set. Then I create a new thread which listens to a socket and while the socket is open and when the set has changed, the thread calls some javafx methods to open new screens. However, when I deploy the application to my raspberry pi, it crashes randomly and has a high CPU usage with java. Feel free to ask any questions if I forgot to explain anything .
Edit 1: my thread class.
Edit 2: My question now is: why do I have such a high CPU usage and how can I fix it.
public class RFIDThread implements Runnable {
/**
* The socket for the connection to the LLRP Reader
*/
private Socket socket;
private JSONArray valid_tags;
private JSONArray found_tags;
private TagsListController controller;
private RFIDSet rfidset;
/**
* Thread for constant reading of the stream
*
* #param socket
* #param controller
* #param tags
*/
public RFIDThread(Socket socket, TagsListController controller, JSONArray tags, RFIDSet rfidset) {
this.socket = socket;
this.controller = controller;
this.rfidset = rfidset;
this.found_tags = new JSONArray();
this.valid_tags = tags;
}
/**
* Runnable for this thread.
* First get all the found tags from the xml controller
* Then loop over the rfid set to find any new tags.
* If there are any, display them.
*/
#Override
public void run() {
CopyOnWriteArrayList<Tag> originalSet = new CopyOnWriteArrayList<>();
originalSet.addAll(rfidset.getSet());
boolean started = true;
if (socket.isConnected()) {
while (!socket.isClosed()) {
CopyOnWriteArrayList<Tag> set = new CopyOnWriteArrayList<>();
set.addAll(rfidset.getSet());
if(started || !originalSet.equals(set)) {
started = false;
CopyOnWriteArrayList<String> found_set = new CopyOnWriteArrayList<>();
found_set.addAll(controller.found_tags_list.getItems());
this.found_tags.clear();
this.found_tags.addAll(found_set);
for (Tag found_tag : set) {
if (found_tags.indexOf(found_tag.getId()) < 0) {
Integer index = valid_tags.indexOf(found_tag.getId());
if (index >= 0) {
Platform.runLater(() -> controller.showValid(found_tag.getId()));
} else {
Platform.runLater(() -> controller.showError(found_tag.getId()));
}
found_tags.add(found_tag.getId());
pause(5000);
}
}
originalSet = set;
pause(5000);
}
}
}
}
/**
* Close the socket
*/
public void shutdown() {
try {
this.socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private void pause(long ms) {
try {
Thread.sleep(ms);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Try moving the pause(5000); outside the if (started || !originalSet.equals(set)) { statement.
High cpu usage is usually a tight loop with no pause or I/O or waiting for stuff. In your case whenever the originalSet.equals(set) you will not pause.
You may prefer to just use:
if (started || !originalSet.equals(set)) {
// ...
} else {
pause(0);
}
or similar.

The Android libstreaming is broadcasting audio without video to server?

We are using Example 3 from this URL to broadcast streaming from Android camera device to server , we change code only to connect with our server not with Wowza server but the video is not arrive to server or by another meaning the input stream file is arriving empty to server for video but the audio is arriving perfectly , sometimes the connection with server is lost after some seconds , How we can know if this problem from server side or from client (code) side , We are using the same code here with change ip address and port number of our server.
https://github.com/fyhertz/libstreaming
The encoding process may be effect in this case?
Session Builder
public class SessionBuilder {
public final static String TAG = "SessionBuilder";
/** Can be used with {#link #setVideoEncoder}. */
public final static int VIDEO_NONE = 0;
/** Can be used with {#link #setVideoEncoder}. */
public final static int VIDEO_H264 = 1;
/** Can be used with {#link #setVideoEncoder}. */
public final static int VIDEO_H263 = 2;
/** Can be used with {#link #setAudioEncoder}. */
public final static int AUDIO_NONE = 0;
/** Can be used with {#link #setAudioEncoder}. */
public final static int AUDIO_AMRNB = 3;
/** Can be used with {#link #setAudioEncoder}. */
public final static int AUDIO_AAC = 5;
// Default configuration
private VideoQuality mVideoQuality = VideoQuality.DEFAULT_VIDEO_QUALITY;
private AudioQuality mAudioQuality = AudioQuality.DEFAULT_AUDIO_QUALITY;
private Context mContext;
private int mVideoEncoder = VIDEO_H263;
private int mAudioEncoder = AUDIO_AMRNB;
private int mCamera = CameraInfo.CAMERA_FACING_BACK;
private int mTimeToLive = 64;
private int mOrientation = 0;
private boolean mFlash = false;
private SurfaceView mSurfaceView = null;
private String mOrigin = null;
private String mDestination = null;
private Session.Callback mCallback = null;
// Removes the default public constructor
private SessionBuilder() {}
// The SessionManager implements the singleton pattern
private static volatile SessionBuilder sInstance = null;
/**
* Returns a reference to the {#link SessionBuilder}.
* #return The reference to the {#link SessionBuilder}
*/
public final static SessionBuilder getInstance() {
if (sInstance == null) {
synchronized (SessionBuilder.class) {
if (sInstance == null) {
SessionBuilder.sInstance = new SessionBuilder();
}
}
}
return sInstance;
}
/**
* Creates a new {#link Session}.
* #return The new Session
* #throws IOException
*/
public Session build() {
Session session;
session = new Session();
session.setOrigin(mOrigin);
session.setDestination(mDestination);
session.setTimeToLive(mTimeToLive);
session.setCallback(mCallback);
switch (mAudioEncoder) {
case AUDIO_AAC:
AACStream stream = new AACStream();
session.addAudioTrack(stream);
if (mContext!=null)
stream.setPreferences(PreferenceManager.getDefaultSharedPreferences(mContext));
break;
case AUDIO_AMRNB:
session.addAudioTrack(new AMRNBStream());
break;
}
switch (mVideoEncoder) {
case VIDEO_H263:
session.addVideoTrack(new H263Stream(mCamera));
break;
case VIDEO_H264:
H264Stream stream = new H264Stream(mCamera);
if (mContext!=null)
stream.setPreferences(PreferenceManager.getDefaultSharedPreferences(mContext));
session.addVideoTrack(stream);
break;
}
if (session.getVideoTrack()!=null) {
VideoStream video = session.getVideoTrack();
video.setFlashState(mFlash);
video.setVideoQuality(mVideoQuality);
video.setSurfaceView(mSurfaceView);
video.setPreviewOrientation(mOrientation);
video.setDestinationPorts(5006);
}
if (session.getAudioTrack()!=null) {
AudioStream audio = session.getAudioTrack();
audio.setAudioQuality(mAudioQuality);
audio.setDestinationPorts(5004);
}
return session;
}
/**
* Access to the context is needed for the H264Stream class to store some stuff in the SharedPreferences.
* Note that you should pass the Application context, not the context of an Activity.
**/
public SessionBuilder setContext(Context context) {
mContext = context;
return this;
}
/** Sets the destination of the session. */
public SessionBuilder setDestination(String destination) {
mDestination = destination;
return this;
}
/** Sets the origin of the session. It appears in the SDP of the session. */
public SessionBuilder setOrigin(String origin) {
mOrigin = origin;
return this;
}
/** Sets the video stream quality. */
public SessionBuilder setVideoQuality(VideoQuality quality) {
mVideoQuality = quality.clone();
return this;
}
/** Sets the audio encoder. */
public SessionBuilder setAudioEncoder(int encoder) {
mAudioEncoder = encoder;
return this;
}
/** Sets the audio quality. */
public SessionBuilder setAudioQuality(AudioQuality quality) {
mAudioQuality = quality.clone();
return this;
}
/** Sets the default video encoder. */
public SessionBuilder setVideoEncoder(int encoder) {
mVideoEncoder = encoder;
return this;
}
public SessionBuilder setFlashEnabled(boolean enabled) {
mFlash = enabled;
return this;
}
public SessionBuilder setCamera(int camera) {
mCamera = camera;
return this;
}
public SessionBuilder setTimeToLive(int ttl) {
mTimeToLive = ttl;
return this;
}
/**
* Sets the SurfaceView required to preview the video stream.
**/
public SessionBuilder setSurfaceView(SurfaceView surfaceView) {
mSurfaceView = surfaceView;
return this;
}
/**
* Sets the orientation of the preview.
* #param orientation The orientation of the preview
*/
public SessionBuilder setPreviewOrientation(int orientation) {
mOrientation = orientation;
return this;
}
public SessionBuilder setCallback(Session.Callback callback) {
mCallback = callback;
return this;
}
/** Returns the context set with {#link #setContext(Context)}*/
public Context getContext() {
return mContext;
}
/** Returns the destination ip address set with {#link #setDestination(String)}. */
public String getDestination() {
return mDestination;
}
/** Returns the origin ip address set with {#link #setOrigin(String)}. */
public String getOrigin() {
return mOrigin;
}
/** Returns the audio encoder set with {#link #setAudioEncoder(int)}. */
public int getAudioEncoder() {
return mAudioEncoder;
}
/** Returns the id of the {#link android.hardware.Camera} set with {#link #setCamera(int)}. */
public int getCamera() {
return mCamera;
}
/** Returns the video encoder set with {#link #setVideoEncoder(int)}. */
public int getVideoEncoder() {
return mVideoEncoder;
}
/** Returns the VideoQuality set with {#link #setVideoQuality(VideoQuality)}. */
public VideoQuality getVideoQuality() {
return mVideoQuality;
}
/** Returns the AudioQuality set with {#link #setAudioQuality(AudioQuality)}. */
public AudioQuality getAudioQuality() {
return mAudioQuality;
}
/** Returns the flash state set with {#link #setFlashEnabled(boolean)}. */
public boolean getFlashState() {
return mFlash;
}
/** Returns the SurfaceView set with {#link #setSurfaceView(SurfaceView)}. */
public SurfaceView getSurfaceView() {
return mSurfaceView;
}
/** Returns the time to live set with {#link #setTimeToLive(int)}. */
public int getTimeToLive() {
return mTimeToLive;
}
/** Returns a new {#link SessionBuilder} with the same configuration. */
public SessionBuilder clone() {
return new SessionBuilder()
.setDestination(mDestination)
.setOrigin(mOrigin)
.setSurfaceView(mSurfaceView)
.setPreviewOrientation(mOrientation)
.setVideoQuality(mVideoQuality)
.setVideoEncoder(mVideoEncoder)
.setFlashEnabled(mFlash)
.setCamera(mCamera)
.setTimeToLive(mTimeToLive)
.setAudioEncoder(mAudioEncoder)
.setAudioQuality(mAudioQuality)
.setContext(mContext)
.setCallback(mCallback);
}
}
Session Class:
public class Session {
public final static String TAG = "Session";
public final static int STREAM_VIDEO = 0x01;
public final static int STREAM_AUDIO = 0x00;
/** Some app is already using a camera (Camera.open() has failed). */
public final static int ERROR_CAMERA_ALREADY_IN_USE = 0x00;
/** The phone may not support some streaming parameters that you are trying to use (bit rate, frame rate, resolution...). */
public final static int ERROR_CONFIGURATION_NOT_SUPPORTED = 0x01;
/**
* The internal storage of the phone is not ready.
* libstreaming tried to store a test file on the sdcard but couldn't.
* See H264Stream and AACStream to find out why libstreaming would want to something like that.
*/
public final static int ERROR_STORAGE_NOT_READY = 0x02;
/** The phone has no flash. */
public final static int ERROR_CAMERA_HAS_NO_FLASH = 0x03;
/** The supplied SurfaceView is not a valid surface, or has not been created yet. */
public final static int ERROR_INVALID_SURFACE = 0x04;
/**
* The destination set with {#link Session#setDestination(String)} could not be resolved.
* May mean that the phone has no access to the internet, or that the DNS server could not
* resolved the host name.
*/
public final static int ERROR_UNKNOWN_HOST = 0x05;
/**
* Some other error occurred !
*/
public final static int ERROR_OTHER = 0x06;
private String mOrigin;
private String mDestination;
private int mTimeToLive = 64;
private long mTimestamp;
private AudioStream mAudioStream = null;
private VideoStream mVideoStream = null;
private Callback mCallback;
private Handler mMainHandler;
private Handler mHandler;
/**
* Creates a streaming session that can be customized by adding tracks.
*/
public Session() {
long uptime = System.currentTimeMillis();
HandlerThread thread = new HandlerThread("net.majorkernelpanic.streaming.Session");
thread.start();
mHandler = new Handler(thread.getLooper());
mMainHandler = new Handler(Looper.getMainLooper());
mTimestamp = (uptime/1000)<<32 & (((uptime-((uptime/1000)*1000))>>32)/1000); // NTP timestamp
mOrigin = "127.0.0.1";
}
/**
* The callback interface you need to implement to get some feedback
* Those will be called from the UI thread.
*/
public interface Callback {
/**
* Called periodically to inform you on the bandwidth
* consumption of the streams when streaming.
*/
public void onBitrateUpdate(long bitrate);
/** Called when some error occurs. */
public void onSessionError(int reason, int streamType, Exception e);
/**
* Called when the previw of the {#link VideoStream}
* has correctly been started.
* If an error occurs while starting the preview,
* {#link Callback#onSessionError(int, int, Exception)} will be
* called instead of {#link Callback#onPreviewStarted()}.
*/
public void onPreviewStarted();
/**
* Called when the session has correctly been configured
* after calling {#link Session#configure()}.
* If an error occurs while configuring the {#link Session},
* {#link Callback#onSessionError(int, int, Exception)} will be
* called instead of {#link Callback#onSessionConfigured()}.
*/
public void onSessionConfigured();
/**
* Called when the streams of the session have correctly been started.
* If an error occurs while starting the {#link Session},
* {#link Callback#onSessionError(int, int, Exception)} will be
* called instead of {#link Callback#onSessionStarted()}.
*/
public void onSessionStarted();
/** Called when the stream of the session have been stopped. */
public void onSessionStopped();
}
/** You probably don't need to use that directly, use the {#link SessionBuilder}. */
void addAudioTrack(AudioStream track) {
removeAudioTrack();
mAudioStream = track;
}
/** You probably don't need to use that directly, use the {#link SessionBuilder}. */
void addVideoTrack(VideoStream track) {
removeVideoTrack();
mVideoStream = track;
}
/** You probably don't need to use that directly, use the {#link SessionBuilder}. */
void removeAudioTrack() {
if (mAudioStream != null) {
mAudioStream.stop();
mAudioStream = null;
}
}
/** You probably don't need to use that directly, use the {#link SessionBuilder}. */
void removeVideoTrack() {
if (mVideoStream != null) {
mVideoStream.stopPreview();
mVideoStream = null;
}
}
/** Returns the underlying {#link AudioStream} used by the {#link Session}. */
public AudioStream getAudioTrack() {
return mAudioStream;
}
/** Returns the underlying {#link VideoStream} used by the {#link Session}. */
public VideoStream getVideoTrack() {
return mVideoStream;
}
/**
* Sets the callback interface that will be called by the {#link Session}.
* #param callback The implementation of the {#link Callback} interface
*/
public void setCallback(Callback callback) {
mCallback = callback;
}
/**
* The origin address of the session.
* It appears in the session description.
* #param origin The origin address
*/
public void setOrigin(String origin) {
mOrigin = origin;
}
/**
* The destination address for all the streams of the session. <br />
* Changes will be taken into account the next time you start the session.
* #param destination The destination address
*/
public void setDestination(String destination) {
mDestination = destination;
}
/**
* Set the TTL of all packets sent during the session. <br />
* Changes will be taken into account the next time you start the session.
* #param ttl The Time To Live
*/
public void setTimeToLive(int ttl) {
mTimeToLive = ttl;
}
/**
* Sets the configuration of the stream. <br />
* You can call this method at any time and changes will take
* effect next time you call {#link #configure()}.
* #param quality Quality of the stream
*/
public void setVideoQuality(VideoQuality quality) {
if (mVideoStream != null) {
mVideoStream.setVideoQuality(quality);
}
}
/**
* Sets a Surface to show a preview of recorded media (video). <br />
* You can call this method at any time and changes will take
* effect next time you call {#link #start()} or {#link #startPreview()}.
*/
public void setSurfaceView(final SurfaceView view) {
mHandler.post(new Runnable() {
#Override
public void run() {
if (mVideoStream != null) {
mVideoStream.setSurfaceView(view);
}
}
});
}
/**
* Sets the orientation of the preview. <br />
* You can call this method at any time and changes will take
* effect next time you call {#link #configure()}.
* #param orientation The orientation of the preview
*/
public void setPreviewOrientation(int orientation) {
if (mVideoStream != null) {
mVideoStream.setPreviewOrientation(orientation);
}
}
/**
* Sets the configuration of the stream. <br />
* You can call this method at any time and changes will take
* effect next time you call {#link #configure()}.
* #param quality Quality of the stream
*/
public void setAudioQuality(AudioQuality quality) {
if (mAudioStream != null) {
mAudioStream.setAudioQuality(quality);
}
}
/**
* Returns the {#link Callback} interface that was set with
* {#link #setCallback(Callback)} or null if none was set.
*/
public Callback getCallback() {
return mCallback;
}
/**
* Returns a Session Description that can be stored in a file or sent to a client with RTSP.
* #return The Session Description.
* #throws IllegalStateException Thrown when {#link #setDestination(String)} has never been called.
*/
public String getSessionDescription() {
StringBuilder sessionDescription = new StringBuilder();
if (mDestination==null) {
throw new IllegalStateException("setDestination() has not been called !");
}
sessionDescription.append("v=0\r\n");
// TODO: Add IPV6 support
sessionDescription.append("o=- "+mTimestamp+" "+mTimestamp+" IN IP4 "+mOrigin+"\r\n");
sessionDescription.append("s=Unnamed\r\n");
sessionDescription.append("i=N/A\r\n");
sessionDescription.append("c=IN IP4 "+mDestination+"\r\n");
// t=0 0 means the session is permanent (we don't know when it will stop)
sessionDescription.append("t=0 0\r\n");
sessionDescription.append("a=recvonly\r\n");
// Prevents two different sessions from using the same peripheral at the same time
if (mAudioStream != null) {
sessionDescription.append(mAudioStream.getSessionDescription());
sessionDescription.append("a=control:trackID="+0+"\r\n");
}
if (mVideoStream != null) {
sessionDescription.append(mVideoStream.getSessionDescription());
sessionDescription.append("a=control:trackID="+1+"\r\n");
}
return sessionDescription.toString();
}
/** Returns the destination set with {#link #setDestination(String)}. */
public String getDestination() {
return mDestination;
}
/** Returns an approximation of the bandwidth consumed by the session in bit per second. */
public long getBitrate() {
long sum = 0;
if (mAudioStream != null) sum += mAudioStream.getBitrate();
if (mVideoStream != null) sum += mVideoStream.getBitrate();
return sum;
}
/** Indicates if a track is currently running. */
public boolean isStreaming() {
if ( (mAudioStream!=null && mAudioStream.isStreaming()) || (mVideoStream!=null && mVideoStream.isStreaming()) )
return true;
else
return false;
}
/**
* Configures all streams of the session.
**/
public void configure() {
mHandler.post(new Runnable() {
#Override
public void run() {
try {
syncConfigure();
} catch (Exception e) {};
}
});
}
/**
* Does the same thing as {#link #configure()}, but in a synchronous manner. <br />
* Throws exceptions in addition to calling a callback
* {#link Callback#onSessionError(int, int, Exception)} when
* an error occurs.
**/
public void syncConfigure()
throws CameraInUseException,
StorageUnavailableException,
ConfNotSupportedException,
InvalidSurfaceException,
RuntimeException,
IOException {
for (int id=0;id<2;id++) {
Stream stream = id==0 ? mAudioStream : mVideoStream;
if (stream!=null && !stream.isStreaming()) {
try {
stream.configure();
} catch (CameraInUseException e) {
postError(ERROR_CAMERA_ALREADY_IN_USE , id, e);
throw e;
} catch (StorageUnavailableException e) {
postError(ERROR_STORAGE_NOT_READY , id, e);
throw e;
} catch (ConfNotSupportedException e) {
postError(ERROR_CONFIGURATION_NOT_SUPPORTED , id, e);
throw e;
} catch (InvalidSurfaceException e) {
postError(ERROR_INVALID_SURFACE , id, e);
throw e;
} catch (IOException e) {
postError(ERROR_OTHER, id, e);
throw e;
} catch (RuntimeException e) {
postError(ERROR_OTHER, id, e);
throw e;
}
}
}
postSessionConfigured();
}
/**
* Asynchronously starts all streams of the session.
**/
public void start() {
mHandler.post(new Runnable() {
#Override
public void run() {
try {
syncStart();
} catch (Exception e) {}
}
});
}
/**
* Starts a stream in a synchronous manner. <br />
* Throws exceptions in addition to calling a callback.
* #param id The id of the stream to start
**/
public void syncStart(int id)
throws CameraInUseException,
StorageUnavailableException,
ConfNotSupportedException,
InvalidSurfaceException,
UnknownHostException,
IOException {
Stream stream = id==0 ? mAudioStream : mVideoStream;
if (stream!=null && !stream.isStreaming()) {
try {
InetAddress destination = InetAddress.getByName(mDestination);
stream.setTimeToLive(mTimeToLive);
stream.setDestinationAddress(destination);
stream.start();
if (getTrack(1-id) == null || getTrack(1-id).isStreaming()) {
postSessionStarted();
}
if (getTrack(1-id) == null || !getTrack(1-id).isStreaming()) {
mHandler.post(mUpdateBitrate);
}
} catch (UnknownHostException e) {
postError(ERROR_UNKNOWN_HOST, id, e);
throw e;
} catch (CameraInUseException e) {
postError(ERROR_CAMERA_ALREADY_IN_USE , id, e);
throw e;
} catch (StorageUnavailableException e) {
postError(ERROR_STORAGE_NOT_READY , id, e);
throw e;
} catch (ConfNotSupportedException e) {
postError(ERROR_CONFIGURATION_NOT_SUPPORTED , id, e);
throw e;
} catch (InvalidSurfaceException e) {
postError(ERROR_INVALID_SURFACE , id, e);
throw e;
} catch (IOException e) {
postError(ERROR_OTHER, id, e);
throw e;
} catch (RuntimeException e) {
postError(ERROR_OTHER, id, e);
throw e;
}
}
}
/**
* Does the same thing as {#link #start()}, but in a synchronous manner. <br />
* Throws exceptions in addition to calling a callback.
**/
public void syncStart()
throws CameraInUseException,
StorageUnavailableException,
ConfNotSupportedException,
InvalidSurfaceException,
UnknownHostException,
IOException {
syncStart(1);
try {
syncStart(0);
} catch (RuntimeException e) {
syncStop(1);
throw e;
} catch (IOException e) {
syncStop(1);
throw e;
}
}
/** Stops all existing streams. */
public void stop() {
mHandler.post(new Runnable() {
#Override
public void run() {
syncStop();
}
});
}
/**
* Stops one stream in a synchronous manner.
* #param id The id of the stream to stop
**/
private void syncStop(final int id) {
Stream stream = id==0 ? mAudioStream : mVideoStream;
if (stream!=null) {
stream.stop();
}
}
/** Stops all existing streams in a synchronous manner. */
public void syncStop() {
syncStop(0);
syncStop(1);
postSessionStopped();
}
/**
* Asynchronously starts the camera preview. <br />
* You should of course pass a {#link SurfaceView} to {#link #setSurfaceView(SurfaceView)}
* before calling this method. Otherwise, the {#link Callback#onSessionError(int, int, Exception)}
* callback will be called with {#link #ERROR_INVALID_SURFACE}.
*/
public void startPreview() {
mHandler.post(new Runnable() {
#Override
public void run() {
if (mVideoStream != null) {
try {
mVideoStream.startPreview();
postPreviewStarted();
mVideoStream.configure();
} catch (CameraInUseException e) {
postError(ERROR_CAMERA_ALREADY_IN_USE , STREAM_VIDEO, e);
} catch (ConfNotSupportedException e) {
postError(ERROR_CONFIGURATION_NOT_SUPPORTED , STREAM_VIDEO, e);
} catch (InvalidSurfaceException e) {
postError(ERROR_INVALID_SURFACE , STREAM_VIDEO, e);
} catch (RuntimeException e) {
postError(ERROR_OTHER, STREAM_VIDEO, e);
} catch (StorageUnavailableException e) {
postError(ERROR_STORAGE_NOT_READY, STREAM_VIDEO, e);
} catch (IOException e) {
postError(ERROR_OTHER, STREAM_VIDEO, e);
}
}
}
});
}
/**
* Asynchronously stops the camera preview.
*/
public void stopPreview() {
mHandler.post(new Runnable() {
#Override
public void run() {
if (mVideoStream != null) {
mVideoStream.stopPreview();
}
}
});
}
/** Switch between the front facing and the back facing camera of the phone. <br />
* If {#link #startPreview()} has been called, the preview will be briefly interrupted. <br />
* If {#link #start()} has been called, the stream will be briefly interrupted.<br />
* To find out which camera is currently selected, use {#link #getCamera()}
**/
public void switchCamera() {
mHandler.post(new Runnable() {
#Override
public void run() {
if (mVideoStream != null) {
try {
mVideoStream.switchCamera();
postPreviewStarted();
} catch (CameraInUseException e) {
postError(ERROR_CAMERA_ALREADY_IN_USE , STREAM_VIDEO, e);
} catch (ConfNotSupportedException e) {
postError(ERROR_CONFIGURATION_NOT_SUPPORTED , STREAM_VIDEO, e);
} catch (InvalidSurfaceException e) {
postError(ERROR_INVALID_SURFACE , STREAM_VIDEO, e);
} catch (IOException e) {
postError(ERROR_OTHER, STREAM_VIDEO, e);
} catch (RuntimeException e) {
postError(ERROR_OTHER, STREAM_VIDEO, e);
}
}
}
});
}
/**
* Returns the id of the camera currently selected. <br />
* It can be either {#link CameraInfo#CAMERA_FACING_BACK} or
* {#link CameraInfo#CAMERA_FACING_FRONT}.
*/
public int getCamera() {
return mVideoStream != null ? mVideoStream.getCamera() : 0;
}
/**
* Toggles the LED of the phone if it has one.
* You can get the current state of the flash with
* {#link Session#getVideoTrack()} and {#link VideoStream#getFlashState()}.
**/
public void toggleFlash() {
mHandler.post(new Runnable() {
#Override
public void run() {
if (mVideoStream != null) {
try {
mVideoStream.toggleFlash();
} catch (RuntimeException e) {
postError(ERROR_CAMERA_HAS_NO_FLASH, STREAM_VIDEO, e);
}
}
}
});
}
/** Deletes all existing tracks & release associated resources. */
public void release() {
removeAudioTrack();
removeVideoTrack();
mHandler.getLooper().quit();
}
private void postPreviewStarted() {
mMainHandler.post(new Runnable() {
#Override
public void run() {
if (mCallback != null) {
mCallback.onPreviewStarted();
}
}
});
}
private void postSessionConfigured() {
mMainHandler.post(new Runnable() {
#Override
public void run() {
if (mCallback != null) {
mCallback.onSessionConfigured();
}
}
});
}
private void postSessionStarted() {
mMainHandler.post(new Runnable() {
#Override
public void run() {
if (mCallback != null) {
mCallback.onSessionStarted();
}
}
});
}
private void postSessionStopped() {
mMainHandler.post(new Runnable() {
#Override
public void run() {
if (mCallback != null) {
mCallback.onSessionStopped();
}
}
});
}
private void postError(final int reason, final int streamType,final Exception e) {
mMainHandler.post(new Runnable() {
#Override
public void run() {
if (mCallback != null) {
mCallback.onSessionError(reason, streamType, e);
}
}
});
}
private void postBitRate(final long bitrate) {
mMainHandler.post(new Runnable() {
#Override
public void run() {
if (mCallback != null) {
mCallback.onBitrateUpdate(bitrate);
}
}
});
}
private Runnable mUpdateBitrate = new Runnable() {
#Override
public void run() {
if (isStreaming()) {
postBitRate(getBitrate());
mHandler.postDelayed(mUpdateBitrate, 500);
} else {
postBitRate(0);
}
}
};
public boolean trackExists(int id) {
if (id==0)
return mAudioStream!=null;
else
return mVideoStream!=null;
}
public Stream getTrack(int id) {
if (id==0)
return mAudioStream;
else
return mVideoStream;
}
}

Getting response from Netty server in non-Netty client

This is my first question on StackOverflow and I hope I have adhered to the expected standards.
I have been taking over some code from someone else who isn't working here anymore and I'm pretty much stranded here. I searched and asked some colleagues (not too much Java experience unfortunately) but no-one seems to be able to help me. Searching didn't really help me either.
I'm sending Json requests to a Netty server from a client which intentionally is NOT implemented using Netty. For now it is just a simple Java socket, but the intention is to have a Flask client send requests to the Netty server. The requests arrive (both using Java Sockets and using Python Flask), and get properly processed in the pipeline, but I want to send a response to the client and although I suspect where in the code to send the response I'm clearly missing out on something as I don't get any response. Any suggestions?
The Java Socket client (note that the json1 and json2 strings have been omitted from the snippet here as they are rather long, but they are formatted properly). Posting requests using a Socket and the related output stream. The response part (with the input stream for the same socket) is just some test which I have my doubt about, but not sure how to do this otherwise (and that's why I kept it here). I've been seeing plenty of examples with clients implementing Netty interfaces and that seems to work fine, but as said I want a client not using Netty to be able to receive the responses as well (if that's possible at all).
String serverResponse;
for (int j = 0; j < 100; j++) {
for (int i = 0; i < 1000; i++) {
try {
Socket s = new Socket("localhost", 12000);
PrintWriter out = new PrintWriter(s.getOutputStream(), true);
out.write(json1 + i + json2);
out.flush();
// Testing only - trying to get the response back from the server
BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
while(true) {
if ((serverResponse = in.readLine()) != null) {
log.info("server says", serverResponse);
break;
}
}
out.close();
s.close();
Thread.sleep(1000);
} catch (Exception ex) {
ex.printStackTrace();
}
}
Thread.sleep(2000);
}
MCTcpServer.java
/**
* Abstract TCP Server class. this class should be implemented in the subclass to implement an actual server.
*
* #param <R> The data to be read from the socket.
* #param <W> data to be written (in case of duplex) from the socket.
*/
public abstract class MFTcpServer<R, W> {
protected final AtomicBoolean started;
protected MFTcpServer() {
this.started = new AtomicBoolean();
}
/**
* Start the server.
*
* #param initializer the channel initializers. they will be called when a new client connects to the server.
* #return instance of tcp server
*/
public final MFTcpServer<R, W> start(ChannelInitializer<Channel> initializer) {
if (!started.compareAndSet(false, true)) {
throw new IllegalStateException("Server already started");
}
doStart(initializer);
return this;
}
/**
* Start the server and wait for all the threads to be finished before shutdown.
* #param initializer the channel initializers. they will be called when a new client connects to the server.
*/
public final void startAndAwait(ChannelInitializer<Channel> initializer) {
start(initializer);
awaitShutdown();
}
/**
* Shutdown the server
* #return true if successfully shutdown.
*/
public final boolean shutdown() {
return !started.compareAndSet(true, false) || doShutdown();
}
/**
* Wait for all the threads to be finished before shutdown.
*/
public abstract void awaitShutdown();
/**
* Do the shutdown now.
* #return true if successfully shutdown
*/
public abstract boolean doShutdown();
/**
* start the server
* #param initializer the channel initializers. they will be called when a new client connetcs to the server.
* #return instance of tcp server
*/
public abstract MFTcpServer<R, W> doStart(ChannelInitializer<Channel> initializer);
/**
*
* #return the port where the server is running.
*/
public abstract int getPort();
MFNetty4TcpServer.java Actual server implementation
public class MFNetty4TcpServer<R, W> extends MFTcpServer<R, W> {
private static final Logger logger = LoggerFactory.getLogger(MFNetty4TcpServer.class);
private static final int BOSS_THREAD_POOL_SIZE = 2;
private int port;
private ServerBootstrap bootstrap;
private ChannelFuture bindFuture;
/**
* The constructor.
*
* #param port port where to listen
*/
protected MFNetty4TcpServer(int port) {
this.port = port;
final NioEventLoopGroup bossGroup = new NioEventLoopGroup(0, new DefaultEventExecutorGroup
(BOSS_THREAD_POOL_SIZE));
final NioEventLoopGroup workerGroup = new NioEventLoopGroup(0, new DefaultEventExecutorGroup
(JsonProducerConfig.THREAD_POOL_SIZE));
bootstrap = new ServerBootstrap()
.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class);
}
#Override
public MFNetty4TcpServer<R, W> doStart(ChannelInitializer<Channel> initializer) {
bootstrap.childHandler(new ChannelInitializer<Channel>() {
#Override
protected void initChannel(Channel ch) throws Exception {
if (initializer != null) {
ch.pipeline().addLast(initializer);
}
}
});
try {
bindFuture = bootstrap.bind(port).sync();
if (!bindFuture.isSuccess()) {
// Connection not successful
throw new RuntimeException(bindFuture.cause());
}
SocketAddress localAddress = bindFuture.channel().localAddress();
if (localAddress instanceof InetSocketAddress) {
port = ((InetSocketAddress) localAddress).getPort();
logger.info("Started server at port: " + port);
}
} catch (InterruptedException e) {
logger.error("Error waiting for binding server port: " + port, e);
}
return this;
}
#Override
public void awaitShutdown() {
try {
bindFuture.channel().closeFuture().await();
} catch (InterruptedException e) {
Thread.interrupted(); // Reset the interrupted status
logger.error("Interrupted while waiting for the server socket to close.", e);
}
}
#Override
public boolean doShutdown() {
try {
bindFuture.channel().close().sync();
return true;
} catch (InterruptedException e) {
logger.error("Failed to shutdown the server.", e);
return false;
}
}
#Override
public int getPort() {
return port;
}
/**
* Creates a tcp server at the defined port.
*
* #param port port to listen to
* #param <R> data to be read
* #param <W> data to be written back. Only in case of duplex connection.
* #return instance of tcp server.
*/
public static <R, W> MFTcpServer<R, W> create(int port) {
return new MFNetty4TcpServer<>(port);
}
}
JsonProducerConfig.java The pipeline is setup here.
/**
* Spring Configuration class of the application.
*/
#Configuration
#Import({DatabusConfig.class})
public class JsonProducerConfig {
private static final Logger log = LoggerFactory.getLogger(JsonProducerConfig.class);
public static final int THREAD_POOL_SIZE = Runtime.getRuntime().availableProcessors() * 2;
public static final String TCP_SERVER = "tcpServer";
public static final String CHANNEL_PIPELINE_INITIALIZER = "channel_initializer";
public static final String MF_KAFKA_PRODUCER = "mf_kafka_producer";
public static final String JSON_AVRO_CONVERTOR = "jsonAvroConvertor";
#Value("#{systemProperties['tcpserver.port']?:'12000'}")
private String tcpServerPort;
#Bean(name = TCP_SERVER)
#Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public MFTcpServer nettyTCPServer() {
return MFNetty4TcpServer.create(Integer.parseInt(tcpServerPort));
}
#Bean(name = MF_KAFKA_PRODUCER)
#Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public MFKafkaProducer pushToKafka() {
return new MFKafkaProducer();
}
#Bean(name = JSON_AVRO_CONVERTOR)
#Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public JsonAvroConvertor jsonAvroConvertor() {
return new JsonAvroConvertor();
}
/**
* This is where the pipeline is set for processing of events.
*
* #param jsonAvroConvertor converts json to avro
* #param kafkaProducer pushes to kafka
* #return chanenl initializers pipeline.
*/
#Bean(name = CHANNEL_PIPELINE_INITIALIZER)
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public ChannelInitializer<Channel> channelInitializers(JsonAvroConvertor jsonAvroConvertor,
MFKafkaProducer kafkaProducer) {
return new ChannelInitializer<Channel>() {
#Override
protected void initChannel(Channel channel) throws Exception {
if (log.isInfoEnabled())
log.info("initChannel - initing channel...");
channel.pipeline().addLast(new NioEventLoopGroup(0, new DefaultEventExecutorGroup(THREAD_POOL_SIZE)));
channel.pipeline().addLast(new JsonObjectDecoder(1048576));
channel.pipeline().addLast(jsonAvroConvertor);
channel.pipeline().addLast(kafkaProducer);
if (log.isInfoEnabled())
log.info("channel = " + channel.toString());
}
};
}
}
JsonProducer.java The main program
public class JsonProducer {
private static final Logger log = LoggerFactory.getLogger(JsonProducer.class);
private static MFTcpServer tcpServer;
/**
* Main startup method
*
* #param args not used
*/
public static void main(String[] args) {
System.setProperty("solschema", "false");
try {
// the shutdown hook.
Runtime.getRuntime().addShutdownHook(new Thread(
() -> {
if (tcpServer != null) {
tcpServer.shutdown();
}
}
));
AnnotationConfigApplicationContext context = new
AnnotationConfigApplicationContext(JsonProducerConfig.class);
tcpServer = (MFTcpServer) context.getBean(JsonProducerConfig.TCP_SERVER);
ChannelInitializer<Channel> channelInitializer = (ChannelInitializer<Channel>) context.
getBean(JsonProducerConfig.CHANNEL_PIPELINE_INITIALIZER);
tcpServer.startAndAwait(channelInitializer);
} catch (Exception t) {
log.error("Error while starting JsonProducer ", t);
System.exit(-1);
}
}
}
The MFKafkaProducer.java as the last channel in the pipeline. Note the ctx.writeAndFlush(msg) in the channelRead method which is where I understand the response should be initiated. But what after that. When running this channelFuture.isSuccess() evaluates to false. The response object was an attempt to a String response.
#ChannelHandler.Sharable
public class MFKafkaProducer extends ChannelInboundHandlerAdapter {
private static final Logger log = LoggerFactory.getLogger(MFKafkaProducer.class);
#Resource
ApplicationContext context;
#Resource(name = DatabusConfig.ADMIN)
Admin admin;
private Map<String, IProducer> streams = new HashMap<>();
#PreDestroy
public void stop() {
removeAllStreams(); // then stop writing to producers
}
/**
* #param clickRecord the record to be pushed to kafka
* #throws Exception
*/
public void handle(GenericRecord clickRecord) throws Exception {
Utf8 clientId = null;
try {
clientId = (Utf8) clickRecord.get(SchemaUtil.APP_ID);
stream(producer(clientId.toString()), clickRecord);
} catch (Exception e) {
String message = "Could not push click data for clientId:" + clientId;
log.warn("handle - " + message + "!!!", e);
assert clientId != null;
removeStream(clientId.toString());
}
}
/**
* removes all the streams
*/
private void removeAllStreams() {
Set<String> strings = streams.keySet();
for (String clientId : strings) {
removeStream(clientId);
}
}
/**
* removes a particular stream
*
* #param clientId the stream to be removed
*/
private void removeStream(String clientId) {
Assert.notEmpty(streams);
IProducer producer = streams.get(clientId);
producer.stopProducer();
streams.remove(clientId);
}
/**
* #param producer the producer where data needs to be written
* #param clickRecord teh record to be written
*/
private void stream(IProducer producer, GenericRecord clickRecord) {
producer.send(clickRecord);
}
/**
* This will create a producer in case it is not already created.
* If already created return the already present one
*
* #param clientId stream id
* #return the producer instance
*/
private IProducer producer(String clientId) {
if (streams.containsKey(clientId)) {
return streams.get(clientId);
} else {
IProducer producer = admin.createKeyTopicProducer(SchemaUtil.APP_ID, "test_" + clientId, new ICallback() {
#Override
public void onSuccess(long offset) {
if (log.isInfoEnabled())
log.info("onSuccess - Data at offset:" + offset + " send.");
}
#Override
public void onError(long offset, Exception ex) {
if (log.isInfoEnabled())
log.info("onError - Data at offset:" + offset + " failed. Exception: ", ex);
}
#Override
public void onStreamClosed() {
log.warn("onStreamClosed - Stream:" + clientId + " closed.");
removeStream(clientId);
}
});
producer.startProducer();
streams.put(clientId, producer);
return producer;
}
}
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
log.debug("KafkaProducer - channelRead() called with " + "ctx = [" + ctx + "], msg = [" + msg + "]");
if (msg instanceof GenericRecord) {
GenericRecord genericRecord = (GenericRecord) msg;
try {
handle(genericRecord);
log.debug("channelRead sending response");
Charset charset = Charset.defaultCharset();
ByteBuf response = Unpooled.copiedBuffer("Just a response", charset);
ChannelFuture future = ctx.writeAndFlush(msg);
future.addListener(new ChannelFutureListener() {
#Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
if (channelFuture.isSuccess())
log.info("channelRead - future.operationComplete - Response has been delivered to all channels");
else
log.info("channelRead - future.operationComplete - Response has NOT been delivered to all channels");
}
});
} catch (Exception ex) {
log.error("Something went wrong processing the generic record: " + msg + "\n ", ex);
}
} else {
log.debug("KafkaProducer - msg not of Type Generic Record !!! " + msg);
}
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
// Close the connection when an exception is raised.
log.error("Something went wrong writing to Kafka: \n", cause);
ctx.close();
}
}
Using ChannelFuture#cause() I noticed I was not serializing a ByteBuf object, but a GenericRecord instead. Using
ByteBuf response = Unpooled.copiedBuffer(genericRecord.toString(), charset);
ChannelFuture future = ctx.writeAndFlush(response);
the GenericRecord gets converted to a ButeBuf and sends a response using the writeAndFlush method.
The test client using a Socket implementation somehow never really received a response, but by using a SocketChannel this was resolved as well.

Threading for distance vector which does not drop packets

I am doing my assignment in Network architecture 1, where I have to implement a distance vector routing at each node.
At each node, I have a thread which listens for incoming DatagramPackets containing routing information from neighboring nodes only on a specific port. When a datagram arrives, the thread processes that datagram, and if there are updates in its internal routing tables, then it sends its routing information to all of its neighbors.
I am trying to do it in Java.
The problem I am facing is that when a datagram arrives, I need to process it. If during that time any other datagram arrives, it is dropped, as the thread is currently processing information. That means I have a loss of information.
Can any one help me with this?
I am using the usual way of reading from a socket in java.
DatagramSocket socket = new DatagramSocket(4445, InetAddress.getByName("127.0.0.1"));
while (true) {
try {
byte[] buf = new byte[2000];
// receive request
DatagramPacket recvRequest = new DatagramPacket(buf, buf.length);
socket.receive(recvRequest);
//Some process of data in datagram
} catch (IOException e) {
e.printStackTrace();
}
}
You can process the received datagram in a thread, so your thread with the socket listener can continue to receive new datagrams.
This the final project that i submitted.
It might be having some improper documentation and some bad usage of Java.
As this project runs on local system, instead of using different IP address and same port number, i am doing it other way.
NetworkBoot.java provides the initial neighbor details to each router.
Thanks
-Sunny Jain
enter code here
/*
* File Name : Router.java
* Public Class Name : Router
*
*/
//~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;
import javax.swing.SwingUtilities;
/**
*
* NA1 project 2 spring 2009 semester
* #author sunny jain
*
*
*/
public class Router extends Thread {
/**
* HashMap containing list of neighbors and cost to reach them.
*/
private HashMap<Integer, Integer> hmapDirectNeighbours = new HashMap<Integer, Integer>(61);
/**
* HashMap containing list of destination as key and routing info to them as value.
* Routing info contains RouteDetail object.
* #see RouteDetail
*/
private HashMap<Integer, RouteDetail> hmapRoutes = new HashMap<Integer, RouteDetail>();
/**
* DatagramSocket
*/
private DatagramSocket dSoc;
/**
* DatagramPacket
*/
private DatagramPacket dpackReceive, dpackSend;
/**
* Inetaddress of system on which runs this algorithm.
*/
private InetAddress localAddress;
/**
* port to listen at for incoming route info from neighbors.
*/
int port;
private LinkedBlockingQueue<DatagramPacket> lbq = new LinkedBlockingQueue<DatagramPacket>();
/**
* Made constructor private to force initialization by specifying port
* compulsory.
*/
private Router() {
}
/**
* Constuctor taking port number as parameter and creates a datagramSocket
* to listen for incoming DatagramPacket on that socket.
* #param port
*/
public Router(int port) {
try {
this.port = port;
localAddress = InetAddress.getByName("127.0.0.1");
dSoc = new DatagramSocket(port, localAddress);
} catch (Exception ex) {
System.out.println("Error while creating socket : " + ex.getMessage());
}
this.start();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
while (true) {
try {
received_Route_Info(lbq.take());
} catch (InterruptedException ex) {
System.out.println("Error while reading elements from datagram queue");
}}}});
}
public void setRouterBootInfo(String strNeighboursInfo) {
String[] strNeighbouringNodes = strNeighboursInfo.split(";");
for (int i = 0; i < strNeighbouringNodes.length; i++) {
String[] strNodeIpAndPort = strNeighbouringNodes[i].split(":");
hmapDirectNeighbours.put(Integer.valueOf(strNodeIpAndPort[0]), Integer.valueOf(strNodeIpAndPort[1]));
hmapRoutes.put(Integer.valueOf(strNodeIpAndPort[0]), new RouteDetail(null, Integer.valueOf(strNodeIpAndPort[1])));
}
propagateChanges();
// entry in Route table....No need for infinity as we creat entry when a node is reachable.
}
#Override
public void run() {
while (true) {
try {
byte[] buf = new byte[250];
// receive request
dpackReceive = new DatagramPacket(buf, buf.length);
dSoc.receive(dpackReceive);
lbq.put(dpackReceive);
} catch (InterruptedException ex) {
ex.printStackTrace();
dSoc.close();
} catch (IOException e) {
e.printStackTrace();
dSoc.close();
}
}
}
/**
* This method is called for each DatagramPacket received containing new
* routing information.
*
* This method checks whether this packet came from neighboring node
* (routers) only. If true it applies Distance vector algorithm on data
* present in datagram packet and due to this information if their is any
* change in local routing information that it displays current local
* updated routing information and also sends this updated information to
* other neighbours only.
*
* #param dataPckt
* #see #validate_Is_Packet_From_Neighbor(java.net.DatagramPacket)
* #see #apply_Routing_Algorithm(java.net.DatagramPacket, java.util.HashMap)
* #see #print_route_info()
* #see #send_Updates_To_Neighbors(routesInfo)
*/
private void received_Route_Info(DatagramPacket dataPckt) {
if (dataPckt.getPort() == 4000) {
setRouterBootInfo(getStringFromBytes(dataPckt));
} else if (validate_Is_Packet_From_Neighbor(dataPckt)) {
if (apply_Routing_Algorithm(dataPckt, create_HashMap_Routes(getStringFromBytes(dataPckt)))) {
// if their is change in routing information.
propagateChanges();
}
}
}
/**
* Validates whether the Datagram packet received is from the neighbors only.
* #param datagrampckt DatagramPacket comtaining routing information.
* #return true if datagrampckt is from neighbors only otherwise false.
*/
private boolean validate_Is_Packet_From_Neighbor(DatagramPacket datagrampckt) {
return hmapDirectNeighbours.containsKey(Integer.valueOf(datagrampckt.getPort()));
}
/**
* Returns byte representaion of data contained in DatagramPacket pkt.
* #param pkt DatagramPacket
* #return byte representation of data contained in pkt
*/
private String getStringFromBytes(DatagramPacket pkt) {
String strData = new String(pkt.getData());
return strData.substring(0, strData.lastIndexOf(';'));
}
/**
* Applies Distance Vector algorithm using newly received routing information
* and information presently with this node (Router).
* #param datagrampckt DatagramPacket containing routing information.
* #param newRoutes HashMap of routes new information received with
* destination as key and cost to that destination as value.
*/
private boolean apply_Routing_Algorithm(DatagramPacket dataPckt, HashMap<Integer, Integer> newRoutes) {
boolean updated = false;
Integer pktSourse = Integer.valueOf(dataPckt.getPort());
// Get a set of the routes
Set<Integer> set = newRoutes.keySet();
// Get an iterator
Iterator<Integer> iterator = set.iterator();
// Display elements.
while (iterator.hasNext()) {
Integer key = iterator.next();
Integer nextHopCost = hmapRoutes.get(pktSourse).getPathCost();
int optionalCost = newRoutes.get(key) + (nextHopCost == null ? 0 : nextHopCost);
if (hmapRoutes.containsKey(key)) {
RouteDetail routeDetail = hmapRoutes.get(key);
if (routeDetail.getPathCost().compareTo(optionalCost) > 0) {
routeDetail.setNextHop(pktSourse);
routeDetail.setPathCost(optionalCost);
hmapRoutes.put(key, routeDetail);
updated = true;
// try to verify above statement
}
} else {
if (!key.equals(port)) {
RouteDetail newRouteDetail = new RouteDetail(pktSourse, optionalCost);
hmapRoutes.put(key, newRouteDetail);
updated = true;
}
}
}
return updated;
}
/**
* When internal routing information is chaged, send this information to
* other neighbors.
* #param routesInfo byte representaion of routing information.
*/
private void send_Updates_To_Neighbors(byte[] routesInfo) {
// Get a set of the routes
Set<Integer> set = hmapDirectNeighbours.keySet();
// Get an iterator
Iterator<Integer> iterator = set.iterator();
// Display elements.
while (iterator.hasNext()) {
dpackSend = new DatagramPacket(routesInfo, routesInfo.length, localAddress, iterator.next().intValue());
try {
dSoc.send(dpackSend);
} catch (IOException ex) {
System.out.println("Error while sending route updates : " + ex.getMessage());
}
}
}
/**
* Parses routeInfo to creat an HashMap based on this informationin the
* format as HashMap of <<Integer:Destination>,<Integer: Cost to this destination>>
* #param routeInfo contains routing information as String in the syntax
* of {<Destination>:<Cost to destination>;}
* #return Hashmap<<Integer:Destination>,<Integer: Cost to this destination>>
*/
private HashMap<Integer, Integer> create_HashMap_Routes(String routeInfo) {
HashMap<Integer, Integer> routes = new HashMap<Integer, Integer>();
String[] straRoute = routeInfo.split(";");
for (int i = 0; i < straRoute.length; i++) {
String[] straDestAndCost = straRoute[i].split(":");
routes.put(Integer.parseInt(straDestAndCost[0]), Integer.parseInt(straDestAndCost[1]));
}
return routes;
}
/**
* Converts current routing information stored as HashMap to String
* presentation in format as {<Destination>:<Cost to destination>;}
*
* #return String representaion of routing information.
* #see #hmapRoutes.
*/
private String create_String_Of_Routes() {
StringBuilder strB = new StringBuilder();
// Get a set of the routes
Set<Integer> set = hmapRoutes.keySet();
// Get an iterator
Iterator<Integer> iterator = set.iterator();
// Display elements.
while (iterator.hasNext()) {
Integer destination = iterator.next();
strB.append(destination);
strB.append(":");
strB.append(hmapRoutes.get(destination).getPathCost());
strB.append(";");
}
return strB.toString();
}
/**
* Prints the current routing information stored in <code>hmapRoutes</code>
* to default output stream of this program.
* #see #hmapRoutes.
*/
public void print_route_info() {
RouteDetail route;
StringBuilder builder;
// PRINT THE CURRENT ROUTING INFO AT THIS NODE
System.out.println("");
System.out.println(" TABLE AT NODE WITH PORT : " + port);
System.out.println("--------------------------------------------------------------------------------");
System.out.println("\t\tTo \t|\t Via\t|\tCost\t\t");
System.out.println("--------------------------------------------------------------------------------");
// Get a set of the routes
Set<Integer> set = hmapRoutes.keySet();
// Get an iterator
Iterator<Integer> iterator = set.iterator();
// Display elements.
while (iterator.hasNext()) {
Integer key = iterator.next();
route = hmapRoutes.get(key);
builder = new StringBuilder();
builder.append("\t\t" + key.intValue());
builder.append("\t|\t" + (route.getNextHop() == null ? " -" : route.getNextHop()));
builder.append("\t|\t" + route.getPathCost() + "\t\t");
System.out.println(builder.toString());
}
}
/**
* This class provides details for each destination.
* It provides detail of cost that will be incurred to reach that
* destination and next router on that path.
*/
private class RouteDetail {
Integer nextHop;
Integer pathCost;
public RouteDetail(Integer nextHop, Integer pathCost) {
this.nextHop = nextHop;
this.pathCost = pathCost;
}
public Integer getNextHop() {
return nextHop;
}
public void setNextHop(Integer nextHop) {
this.nextHop = nextHop;
}
public Integer getPathCost() {
return pathCost;
}
public void setPathCost(Integer pathCost) {
this.pathCost = pathCost;
}
}
private void propagateChanges() {
print_route_info();
send_Updates_To_Neighbors(create_String_Of_Routes().getBytes());
}
public static void main(String[] args) {
new Router(Integer.parseInt(args[0]));
}
}
/*
* File Name : NetworkBoot.java
* Public Class Name : NetworkBoot
*
*/
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
*
* NA1 project 2 spring 2009 semester
* #author sunny jain
*
*
*/
public class NetworkBoot {
public static void main(String[] args) {
try {
DatagramSocket dSoc = new DatagramSocket(4000, InetAddress.getByName("127.0.0.1"));
String[] sendD = {"4006:3;4007:5;4009:2;", "4005:3;4007:3;4008:6;", "4005:5;4006:3;", "4009:2;4006:6;", "4008:2;4005:2;"};
for (int i = 0, port = 4005; i < 5; i++) {
dSoc.send(new DatagramPacket(sendD[i].getBytes(), sendD[i].length(), InetAddress.getByName("127.0.0.1"), port++));
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
DatagramSocket socket = new DatagramSocket(4445, InetAddress.getByName("127.0.0.1"));
while (true) {
try {
// note final ..
final byte[] buf = new byte[2000];
// receive request
DatagramPacket recvRequest = new DatagramPacket(buf, buf.length);
socket.receive(recvRequest);
//Some process of data in datagram
(new Thread(new Runnable() {
public void run () {
// do stuff with data in buf
...
}
})).start();
} catch (IOException e) {
e.printStackTrace();
}
}
I haven't done this in Java, but, you can (or should) pass more than one simultaneous datagram buffer to the socket (either with several threads each invoking the synchrnonous receive method, or preferably with one thread invoking an asynchrnonous receive method more than once).
The advantage of passing multiple simultaneous datagram buffers to the socket is obvious: i.e. the socket will still have a buffer (into which to receive the next datagram) even while it has already filled one buffer (with a previous datagram) and passed that buffer back to you.
You might ask, "in what sequence will the buffers be passed back to me?" and the answer to that is, "it shouldn't matter." If the sequence in which you process datagrams is important then the datagrams themselves should contain a sequence number (because datagrams might get out of sequence as they're routed over the network, whether or not you've passed multiple simultaneous to the local socket with a consequent possibility of "simultaneous" receives being delivered back to you out of sequence).
It is worth remembering UDP is lossy transport, while minimising packet loss is a good idea you should never assume you will get every packet, (or that the packets will arrive in the order you sent them)

Categories