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)
Related
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
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.
I'm having an issue with a multi-threaded server I'm building as an academic exercise, more specifically with getting a connection to close down gracefully.
Each connection is managed by a Session class. This class maintains 2 threads for the connection, a DownstreamThread and an UpstreamThread.
The UpstreamThread blocks on the client socket and encodes all incoming strings into messages to be passed up to another layer to deal with. The DownstreamThread blocks on a BlockingQueue into which messages for the client are inserted. When there's a message on the queue, the Downstream thread takes the message off the queue, turns it into a string and sends it to the client. In the final system, an application layer will act on incoming messages and push outgoing messages down to the server to send to the appropriate client, but for now I just have a simple application that sleeps for a second on an incoming message before echoing it back as an outgoing message with a timestamp appended.
The problem I'm having is getting the whole thing to shut down gracefully when the client disconnects. The first issue I'm contending with is a normal disconnect, where the client lets the server know that it's ending the connection with a QUIT command. The basic pseudocode is:
while (!quitting) {
inputString = socket.readLine () // blocks
if (inputString != "QUIT") {
// forward the message upstream
server.acceptMessage (inputString);
} else {
// Do cleanup
quitting = true;
socket.close ();
}
}
The upstream thread's main loop looks at the input string. If it's QUIT the thread sets a flag to say that the client has ended communications and exits the loop. This leads to the upstream thread shutting down nicely.
The downstream thread's main loop waits for messages in the BlockingQueue for as long as the connection closing flag isn't set. When it is, the downstream thread is also supposed to terminate. However, it doesn't, it just sits there waiting. Its psuedocode looks like this:
while (!quitting) {
outputMessage = messageQueue.take (); // blocks
sendMessageToClient (outputMessage);
}
When I tested this, I noticed that when the client quit, the upstream thread shut down, but the downstream thread didn't.
After a bit of head scratching, I realised that the downstream thread is still blocking on the BlockingQueue waiting for an incoming message that will never come. The upstream thread doesn't forward the QUIT message any further up the chain.
How can I make the downstream thread shut down gracefully? The first idea that sprang to mind was setting a timeout on the take() call. I'm not too keen on this idea though, because whatever value you select, it's bound to be not entirely satisfactory. Either it's too long and a zombie thread sits there for a long time before shutting down, or it's too short and connections that have idled for a few minutes but are still valid will be killed. I did think of sending the QUIT message up the chain, but that requires it to make a full round trip to the server, then the application, then back down to the server again and finally to the session. This doesn't seem like an elegant solution either.
I did look at the documentation for Thread.stop() but that's apparently deprecated because it never worked properly anyway, so that looks like it's not really an option either. Another idea I had was to force an exception to be triggered in the downstream thread somehow and let it clean up in its finally block, but this strikes me as a horrible and kludgey idea.
I feel that both threads should be able to gracefully shutdown on their own, but I also suspect that if one thread ends it must also signal the other thread to end in a more proactive way than simply setting a flag for the other thread to check. As I'm still not very experienced with Java, I'm rather out of ideas at this point. If anyone has any advice, it would be greatly appreciated.
For the sake of completeness, I've included the real code for the Session class below, though I believe the pseudocode snippets above cover the relevant parts of the problem. The full class is about 250 lines.
import java.io.*;
import java.net.*;
import java.util.concurrent.*;
import java.util.logging.*;
/**
* Session class
*
* A session manages the individual connection between a client and the server.
* It accepts input from the client and sends output to the client over the
* provided socket.
*
*/
public class Session {
private Socket clientSocket = null;
private Server server = null;
private Integer sessionId = 0;
private DownstreamThread downstream = null;
private UpstreamThread upstream = null;
private boolean sessionEnding = false;
/**
* This thread handles waiting for messages from the server and sending
* them to the client
*/
private class DownstreamThread implements Runnable {
private BlockingQueue<DownstreamMessage> incomingMessages = null;
private OutputStreamWriter streamWriter = null;
private Session outer = null;
#Override
public void run () {
DownstreamMessage message;
Thread.currentThread ().setName ("DownstreamThread_" + outer.getId ());
try {
// Send connect message
this.sendMessageToClient ("Hello, you are client " + outer.getId ());
while (!outer.sessionEnding) {
message = this.incomingMessages.take ();
this.sendMessageToClient (message.getPayload ());
}
// Send disconnect message
this.sendMessageToClient ("Goodbye, client " + getId ());
} catch (InterruptedException | IOException ex) {
Logger.getLogger (DownstreamThread.class.getName ()).log (Level.SEVERE, ex.getMessage (), ex);
} finally {
this.terminate ();
}
}
/**
* Add a message to the downstream queue
*
* #param message
* #return
* #throws InterruptedException
*/
public DownstreamThread acceptMessage (DownstreamMessage message) throws InterruptedException {
if (!outer.sessionEnding) {
this.incomingMessages.put (message);
}
return this;
}
/**
* Send the given message to the client
*
* #param message
* #throws IOException
*/
private DownstreamThread sendMessageToClient (CharSequence message) throws IOException {
OutputStreamWriter osw;
// Output to client
if (null != (osw = this.getStreamWriter ())) {
osw.write ((String) message);
osw.write ("\r\n");
osw.flush ();
}
return this;
}
/**
* Perform session cleanup
*
* #return
*/
private DownstreamThread terminate () {
try {
this.streamWriter.close ();
} catch (IOException ex) {
Logger.getLogger (DownstreamThread.class.getName ()).log (Level.SEVERE, ex.getMessage (), ex);
}
this.streamWriter = null;
return this;
}
/**
* Get an output stream writer, initialize it if it's not active
*
* #return A configured OutputStreamWriter object
* #throws IOException
*/
private OutputStreamWriter getStreamWriter () throws IOException {
if ((null == this.streamWriter)
&& (!outer.sessionEnding)) {
BufferedOutputStream os = new BufferedOutputStream (outer.clientSocket.getOutputStream ());
this.streamWriter = new OutputStreamWriter (os, "UTF8");
}
return this.streamWriter;
}
/**
*
* #param outer
*/
public DownstreamThread (Session outer) {
this.outer = outer;
this.incomingMessages = new LinkedBlockingQueue ();
System.out.println ("Class " + this.getClass () + " created");
}
}
/**
* This thread handles waiting for client input and sending it upstream
*/
private class UpstreamThread implements Runnable {
private Session outer = null;
#Override
public void run () {
StringBuffer inputBuffer = new StringBuffer ();
BufferedReader inReader;
Thread.currentThread ().setName ("UpstreamThread_" + outer.getId ());
try {
inReader = new BufferedReader (new InputStreamReader (outer.clientSocket.getInputStream (), "UTF8"));
while (!outer.sessionEnding) {
// Read whatever was in the input buffer
inputBuffer.delete (0, inputBuffer.length ());
inputBuffer.append (inReader.readLine ());
System.out.println ("Input message was: " + inputBuffer);
if (!inputBuffer.toString ().equals ("QUIT")) {
// Forward the message up the chain to the Server
outer.server.acceptMessage (new UpstreamMessage (sessionId, inputBuffer.toString ()));
} else {
// End the session
outer.sessionEnding = true;
}
}
} catch (IOException | InterruptedException e) {
Logger.getLogger (Session.class.getName ()).log (Level.SEVERE, e.getMessage (), e);
} finally {
outer.terminate ();
outer.server.deleteSession (outer.getId ());
}
}
/**
* Class constructor
*
* The Core Java volume 1 book said that a constructor such as this
* should be implicitly created, but that doesn't seem to be the case!
*
* #param outer
*/
public UpstreamThread (Session outer) {
this.outer = outer;
System.out.println ("Class " + this.getClass () + " created");
}
}
/**
* Start the session threads
*/
public void run () //throws InterruptedException
{
Thread upThread = new Thread (this.upstream);
Thread downThread = new Thread (this.downstream);
upThread.start ();
downThread.start ();
}
/**
* Accept a message to send to the client
*
* #param message
* #return
* #throws InterruptedException
*/
public Session acceptMessage (DownstreamMessage message) throws InterruptedException {
this.downstream.acceptMessage (message);
return this;
}
/**
* Accept a message to send to the client
*
* #param message
* #return
* #throws InterruptedException
*/
public Session acceptMessage (String message) throws InterruptedException {
return this.acceptMessage (new DownstreamMessage (this.getId (), message));
}
/**
* Terminate the client connection
*/
private void terminate () {
try {
this.clientSocket.close ();
} catch (IOException e) {
Logger.getLogger (Session.class.getName ()).log (Level.SEVERE, e.getMessage (), e);
}
}
/**
* Get this Session's ID
*
* #return The ID of this session
*/
public Integer getId () {
return this.sessionId;
}
/**
* Session constructor
*
* #param owner The Server object that owns this session
* #param sessionId The unique ID this session will be given
* #throws IOException
*/
public Session (Server owner, Socket clientSocket, Integer sessionId) throws IOException {
this.server = owner;
this.clientSocket = clientSocket;
this.sessionId = sessionId;
this.upstream = new UpstreamThread (this);
this.downstream = new DownstreamThread (this);
System.out.println ("Class " + this.getClass () + " created");
System.out.println ("Session ID is " + this.sessionId);
}
}
Instead of calling Thread.stop use Thread.interrupt. That will cause the take method to throw an InterruptedException which you can use to know that you should shut down.
Can you just create "fake" quit message instead of setting outer.sessionEnding to true when "QUIT" appears. Putting this fake message in queue will wake the DownstreamThread and you can end it. In that case you can even eliminate this sessionEnding variable.
In pseudo code this could look like this:
while (true) {
outputMessage = messageQueue.take (); // blocks
if (QUIT == outputMessage)
break
sendMessageToClient (outputMessage);
}
i have network client / server application that using java zeromq
framework for the communications .
i have the main server and N clients that polls the server . when the server gets online.
the clients connect him and there some short massaging going on between them
until now i done with single client and it worked fine .
but when adding another client ( that's 2 )
i getting in the request null as returned massage :
request = socket.recv (0);
based on the this example :
http://zguide.zeromq.org/java:mtserver
my code (part of it its very long )
all context and the ZeroMq Settings are set and not null
and i allways get this exception :
Exception in thread "Thread-1" org.zeromq.ZMQException: Operation cannot be accomplished in current state(0x9523dfb)
at org.zeromq.ZMQ$Socket.recv(Native Method)
at com.controller.core.Daemon$1.run(Daemon.java:127)
for(int thread_nbr = 0; thread_nbr < m_iThreadPoolCount; thread_nbr++) {
Thread worker_routine = new Thread() {
#Override
public void run() {
//synchronized(OBJ_LOCK) {
ZMQ.Socket socket = m_pNetworkManager.getContext().socket(ZMQ.REP);//context.socket(ZMQ.REP);
socket.connect ("inproc://workers");
while (true) {
/** Wait for next request from client (C string) */
byte[] request = null;
try{
if(m_pNetworkManager.getContext()!=null) // its never null
{
request = socket.recv (0);
}
}catch (Exception e)
{
// it allays gets null exception
}
boolean bFoundInList = false;
if(request!=null)
{
// multi frame sending
socket.send(m_UT.getbyteArray(
m_UT.getReplayStructure(aStateMap_replay)
),ZMQ.SNDMORE);
socket.send(new byte[0], ZMQ.SNDMORE);
byte[] byteFileStruct = null;
byteFileStruct = m_UT.serialize(stateFilesStruct);
boolean send = socket.send(byteFileStruct,0);
} // socket.recv end
}
// }// synchronized block
}
}; //Thread worker_routine
worker_routine.start();
}
// Connect work threads to client threads via a queue
ZMQQueue zMQQueue = new ZMQQueue( m_pNetworkManager.getContext(),
m_pNetworkManager.getClients(),
m_pNetworkManager.getWorkers());
zMQQueue.run();
// We never get here but clean up anyhow
m_pNetworkManager.getClients().close();
m_pNetworkManager.getWorkers().close();
m_pNetworkManager.getContext().term();
}
allso added the NetworkManager class
public class NetworkManager {
/** ZeroMQ context */
private ZMQ.Context m_context = null;
/** ZeroMQ socket */
private ZMQ.Socket m_socket = null;
/** representation of the clients */
ZMQ.Socket m_clients = null;
/** representation of the workers threads */
ZMQ.Socket m_workers = null;
/**
* NetworkManager constructor.
*/
public NetworkManager()
{
;
}
/**
* Setup the network ZeroMQ network layer
* #param sControllerDomain the Controller domain name and port
*/
public void Init(String sControllerDomain)
{
/** Prepare our context and socket */
m_context = ZMQ.context(1);
m_clients = m_context.socket(ZMQ.ROUTER);
// m_clients = m_context.socket(ZMQ.REP);
m_clients.bind (sControllerDomain);
m_workers = m_context.socket(ZMQ.DEALER);
m_workers.bind ("inproc://workers");
}
/**
* Get ZeroMQ context
* #return ZMQ.Context
*/
public ZMQ.Context getContext() {
return m_context;
}
/**
* get ZeroMQ Socket
* #return ZMQ.Socket
*/
public ZMQ.Socket getSocket() {
return m_socket;
}
/**
* get the workers as ZMQ.Socket
* #return ZMQ.Socket
*/
public ZMQ.Socket getWorkers() {
return m_workers;
}
/**
* get the Clients as ZMQ.Socket
* #return ZMQ.Socket
*/
public ZMQ.Socket getClients() {
return m_clients;
}
}
What your OS system? If you are using Windows the operations using: m_workers.bind ("inproc://workers") is not supported. IIRC.
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