I am listening to UDP data packets on a heavy traffic network. The wireshark capture shows the UDP data packets ip address as 125.6.6.5 but when the packet is listened by my code "NetworkListener.java", I see it prints out the right information, but I do pass in this data and construct a new object "Packet" and then put into a LinkedBlockingQueue and read it in another class "Worker.java".
When I print the same packet in Worker.java class I see that the ip address of the packet has being changed.
Since it being a heavy traffic network where I am getting more than 40 data packets in a second on that port and I am converting it to a diff object and storing in a linkedBlockingQueue, does the packets are colliding in the Queue and giving me an incorrect ip???
The java classes are:
public class NetworkListener implements Runnable
{
private LinkedBlockingQueue lbq;
private volatile boolean running;
public NetworkListener()
{
lbq = PacketQueue.getInstance();
running = true;
}
public void run()
{
try
{
byte[] rwhat = new byte[1024];
DatagramPacket packet = new DatagramPacket(rwhat, 1024);
// Setup listener on port 5000
InetSocketAddress isock = new InetSocketAddress(5000);
DatagramSocket datagramSocket = new DatagramSocket(null);
datagramSocket.setReuseAddress(true);
datagramSocket.bind(isock);
datagramSocket.setSoTimeout(15000); // 15 seconds timeouts
// loop reading packets
while (running)
{
try {
datagramSocket.receive(packet);
}
catch (SocketTimeoutException te) {
continue;
}
String ipAddr = new String (packet.getAddress().getHostAddress());
String payLoad = new String (packet.getData());
String strRecv = new String( "IP Address from Network Listener:[" + ipAddr + "], Payload :[" + payLoad + "]");
System.out.println(strRecv);
synchronized (lbq)
{
lbq.put(new Packet(packet.getData(), packet.getAddress().getHostAddress()));
lbq.notifyAll();
}
}
}
catch (Exception e)
{
}
}
/**
* Shutdown current thread
*/
public void shutdown()
{
running = false;
if (lbq != null)
{
synchronized (lbq)
{
lbq.notifyAll();
lbq = null;
}
}
}
}
public class Packet
{
private byte[] bytes;
private String ipAddress;
public Packet(final byte[] bytes, final String ipAddress)
{
this.bytes = bytes;
this.ipAddress = ipAddress;
}
public byte[] getBytes()
{
return bytes;
}
public String getipAddress()
{
return ipAddress;
}
}
public class PacketQueue extends LinkedBlockingQueue
{
private static volatile PacketQueue INSTANCE = null;
public static synchronized final PacketQueue getInstance()
{
if (INSTANCE == null)
INSTANCE = new PacketQueue();
return INSTANCE;
}
}
public class Worker implements Runnable
{
private LinkedBlockingQueue lbq;
private volatile boolean running;
public Worker()
{
lbq = PacketQueue.getInstance();
running = true;
}
public void run()
{
System.out.println("Thread Starting Up . . .");
while (running)
{
synchronized (lbq)
{
while (lbq.empty())
{
try
{
lbq.wait();
if (lbq == null)
return;
}
catch (InterruptedException e)
{
return;
}
}
}
Packet packet = (Packet)lbq.poll();
if (packet != null)
{
String ipAddr = new String (packet.getipAddress());
String payLoad = new String (packet.getBytes());
String strRecv = new String( "IP Address from Worker:[" + ipAddr + "], Payload :[" + payLoad + "]");
System.out.println(strRecv);
}
}
}
/**
* Shutdown current thread
*/
public void shutdown()
{
running = false;
if (lbq != null)
{
synchronized (lbq)
{
lbq.notifyAll();
lbq = null;
}
}
}
}
So if I am comparing both the print statements in NetworkListener.java and Worker.java, I would expect them to be the same ip address for a given payload but in my case sometimes its giving me correct ip address sometimes its giving an incorrect ip address. At every sec, I am seeing around 35 more data packets to be processed in the queue.
What am I doing wrong???
Is my code not able to handle heavy traffic??
Should I be using any other data structure apart from LinkedBlockingQueue??
Or should I not convert from DatagramPacket to Packet object??
Please advise and thanks for looking.
Your problem could be a race condition. Maybe other problems could be but I cannot say.
Just by looking at your code. Shouldn't this
Packet packet = (Packet)lbq.poll();
be part of the critical section and have synchronized access? This is the only access to lbq that is not protected. Is seems it should be within the synchronized section after the while loop.
Related
There are two classes Client and ChatWindow, client has DatagramSocket, InetAddress and port fields along with methods for sending, receiving, and closing the socket. To close a socket I use an anonymous thread "socketCLOSE"
Client class
public class Client {
private static final long serialVersionUID = 1L;
private DatagramSocket socket;
private String name, address;
private int port;
private InetAddress ip;
private Thread send;
private int ID = -1;
private boolean flag = false;
public Client(String name, String address, int port) {
this.name = name;
this.address = address;
this.port = port;
}
public String receive() {
byte[] data = new byte[1024];
DatagramPacket packet = new DatagramPacket(data, data.length);
try {
socket.receive(packet);
} catch (IOException e) {
e.printStackTrace();
}
String message = new String(packet.getData());
return message;
}
public void send(final byte[] data) {
send = new Thread("Send") {
public void run() {
DatagramPacket packet = new DatagramPacket(data, data.length, ip, port);
try {
socket.send(packet);
} catch (IOException e) {
e.printStackTrace();
}
}
};
send.start();
}
public int close() {
System.err.println("close function called");
new Thread("socketClOSE") {
public void run() {
synchronized (socket) {
socket.close();
System.err.println("is socket closed "+socket.isClosed());
}
}
}.start();
return 0;
}
ChatWindow class is sort of a GUI which extends JPanel and implements Runnable, There are two threads inside the class - run and Listen.
public class ClientWindow extends JFrame implements Runnable {
private static final long serialVersionUID = 1L;
private Thread run, listen;
private Client client;
private boolean running = false;
public ClientWindow(String name, String address, int port) {
client = new Client(name, address, port);
createWindow();
console("Attempting a connection to " + address + ":" + port + ", user: " + name);
String connection = "/c/" + name + "/e/";
client.send(connection.getBytes());
running = true;
run = new Thread(this, "Running");
run.start();
}
private void createWindow() {
{
//Jcomponents and Layouts here
}
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
String disconnect = "/d/" + client.getID() + "/e/";
send(disconnect, false);
running = false;
client.close();
dispose();
}
});
setVisible(true);
txtMessage.requestFocusInWindow();
}
public void run() {
listen();
}
private void send(String message, boolean text) {
if (message.equals("")) return;
if (text) {
message = client.getName() + ": " + message;
message = "/m/" + message + "/e/";
txtMessage.setText("");
}
client.send(message.getBytes());
}
public void listen() {
listen = new Thread("Listen") {
public void run() {
while (running) {
String message = client.receive();
if (message.startsWith("/c/")) {
client.setID(Integer.parseInt(message.split("/c/|/e/")[1]));
console("Successfully connected to server! ID: " + client.getID());
} else if (message.startsWith("/m/")) {
String text = message.substring(3);
text = text.split("/e/")[0];
console(text);
} else if (message.startsWith("/i/")) {
String text = "/i/" + client.getID() + "/e/";
send(text, false);
} else if (message.startsWith("/u/")) {
String[] u = message.split("/u/|/n/|/e/");
users.update(Arrays.copyOfRange(u, 1, u.length - 1));
}
}
}
};
listen.start();
}
public void console(String message) {
}
}
Whenever the client is closed, the client.close() is called which spawns socketCLOSE thread, but the thread does nothing, it enters the blocked state, as revealed by stack trace -
Name: socketClOSE
State: BLOCKED on java.net.DatagramSocket#1de1602 owned by: Listen
Total blocked: 1 Total waited: 0
Stack trace:
app//com.server.Client$2.run(Client.java:90)
Name: Listen
State: RUNNABLE
Total blocked: 0 Total waited: 0
Stack trace:
java.base#14.0.1/java.net.DualStackPlainDatagramSocketImpl.socketReceiveOrPeekData(Native Method)
java.base#14.0.1/java.net.DualStackPlainDatagramSocketImpl.receive0(DualStackPlainDatagramSocketImpl.java:130)
locked java.net.DualStackPlainDatagramSocketImpl#3dd26cc7
java.base#14.0.1/java.net.AbstractPlainDatagramSocketImpl.receive(AbstractPlainDatagramSocketImpl.java:181)
locked java.net.DualStackPlainDatagramSocketImpl#3dd26cc7
java.base#14.0.1/java.net.DatagramSocket.receive(DatagramSocket.java:864)
locked java.net.DatagramPacket#6d21ecb
locked java.net.DatagramSocket#1de1602
app//com.thecherno.chernochat.Client.receive(Client.java:59)
app//com.thecherno.chernochat.ClientWindow$5.run(ClientWindow.java:183)
This doesn’t let SocketCLOSE thread close the socket inside the synchronized block as lock on socket is held by Listen thread. How can I make the listen thread release its lock, the program terminates without closing the socket and debugger shows Listen thread as still runnable. Is the implementation flawed itself or can this be solved ?
I can reproduce the problem with JDK 14, but not with JDK 15 or newer.
This seems plausible, as JDK-8235674, JEP 373: Reimplement the Legacy DatagramSocket API indicates that the implementation has been rewritten for JDK 15. The report even says “The implementation also has several concurrency issues (e.g., with asynchronous close) that require an overhaul to address properly.”
However, you can get rid of the problem with JDK 14 too; just remove the synchronized. Nothing in the documentation says that synchronization was required to call close() and when I removed it, my testcase worked as intended.
When you want to coordinate the multithreaded access to the socket of your application, you should use a different lock object than the socket instance itself.
So I'm working on a server that handles a few commands and one small problem is trying to remove the list of active clients when a user decides to log out. Each client is handled in a thread and once the command is done this active client gets removed however its not removing.
Below is an example of removing an active client, this thread is
public class serverHandlerThread implements Runnable
{
private Socket socket;
//private BufferedWriter clientOut;
private ObjectOutputStream toClient;
private MainServer server;
private Users user;
//Constructor
serverHandlerThread(MainServer server, Socket socket)
{
this.server = server;
this.socket = socket;
}
private ObjectOutputStream getWriter()
{
return toClient;
}
private void deleteClient(serverHandlerThread obj)
{
synchronized (server.clients)
{
server.clients.remove(obj);
}
}
#Override
public void run ()
{
try
{
//Setup I/O
toClient = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream fromClient = new ObjectInputStream(socket.getInputStream());
while(!socket.isClosed())
{
//If server has received a message
if(fromClient.available() > 0)
{
//Reads message and objects from client
String input = fromClient.readUTF();
Object obj = fromClient.readObject();
//logger(input);
switch (input)
{
//Logout the user
case ".logout":
//Set the user to being logged out and print the log
user = (Users) obj;
deleteClient(this);
for (int i = 0; i < server.usersList.size(); i++)
{
if (user.getUserName().equals(server.usersList.get(i).getUserName()))
{
server.usersList.get(i).setLoggedIn(false);
logger(user.getUserName() + " has logged out");
}
}
break;
//Push message received to other clients
default:
logger("Sending message to clients");
user = (Users) obj;
deleteClient(this);
logger("clients size is: " + String.valueOf(server.clients.size()));
for (serverHandlerThread thatClient : server.getClients())
{
ObjectOutputStream thatClientOut = thatClient.getWriter();
if (thatClientOut != null)
{
thatClientOut.writeUTF(user.getUserName() + ": " + input + "\r\n");
thatClientOut.flush();
}
}
break;
}
}
}
}
catch (IOException | ClassNotFoundException e)
{
e.printStackTrace();
}
}
}
server is of type MainServer which contains the list of clients and is written as List<ServerHandlerThread> clients. MainServer calls serverHandlerThread when a new client is accepted ie. making the server multithread.
The problem is when the clients requests to logout it should delete the user from the active client list. It doesn't and so when the server tries to push messages to all clients it also tried to write a message to the client who's socket has been closed(user who logged out) and so the server spits out a broken pipe error. Any ideas?
*Edit
More information on the mainServer class, omitted a few things but this should be enough information
public class MainServer
{
//Static variables
private static final int portNumber = 4444;
//Variables
private int serverPort;
private List<serverHandlerThread> clients;
/**
* Very basic logger that prints out
* the current time and date
* #param msg used when printing the log
*/
private void logger(String msg)
{
System.out.println(LocalDate.now()+ " " +LocalTime.now() + " - " +msg);
}
private List<serverHandlerThread> getClients()
{
return clients;
}
//Starts the server and begins accepting clients
private void startServer()
{
clients = new ArrayList<>();
ServerSocket serverSocket;
try
{
serverSocket = new ServerSocket(serverPort);
acceptClients(serverSocket);
}
catch (IOException e)
{
logger("Could not listen on port: " + serverPort);
System.exit(1);
}
}
//Continuously accept clients
private void acceptClients(ServerSocket serverSocket)
{
logger("Server starts port = " + serverSocket.getLocalSocketAddress());
while (true)
{
try
{
Socket socket = serverSocket.accept();
//logger("Accepts: " + socket.getRemoteSocketAddress());
serverHandlerThread client = new serverHandlerThread(this, socket);
Thread thread = new Thread(client);
thread.setDaemon(true);
thread.start();
synchronized(clients)
{
clients.add(client);
}
}
catch (IOException e)
{
System.err.println("Accept failed on:" + serverPort);
}
}
}
public MainServer(int portNumber)
{
this.serverPort = portNumber;
}
public static void main(String[] args)
{
MainServer server = new MainServer(portNumber);
server.startServer();
}
}
*Edit 2
So I've made a little method that synchronizes the client list accross all threads and edited the mainServer to do this as-well but the problem persists
private void deleteClient(serverHandlerThread obj)
{
synchronized (server.clients)
{
server.clients.remove(obj);
}
}
You should probably refactor your code using a client manager pattern to avoid the problem you currently have:
You are managing your clients from N threads, one of which being the server.
You have access to one list with various form of synchronization which may be lead to synchronization issues because the code is all over.
Given this pattern, here is an example (I used synchronized, but other form of synchronization may work):
class ClientManager {
private final List<Client> clients;
public ClientManager() {
this.clients = new ArrayList<>();
}
public synchronized void add(Client client) {
this.clients.add(client);
}
public synchronized void remove(Client client) {
this.clients.remove(client);
}
public synchronized List<Client> list() {
return new ArrayList<>(this.clients);
}
}
Both Client (ServerHandlerThread) and Server (MainServer) will play the ClientManager: my point is that this class is doing all the synchronization work and not the Server/Client.
I use a copy of the list to minimize the lock time (otherwise, client would wait for other thread calling list()). This means that a Client logout may occurs here when you send your message: you'll need to use a flag (alive, etc) indicating if the Client is still there.
You may also check it in the sendMessage and return a status indicating if the message was sent or not.
class Server {
private final ClientManager manager = new ClientManager();
// register new client
Client newClient() {
Client client = new Client(manager);
manager.add(client);
return client;
}
void sendMessageToAll(String msg) {
for (Client client : manager.list()) {
// isAlive returns true except if the client was logged out.
// It should probably be synchronized too.
if (client.isAlive()) {
client.sendMessage(msg);
}
}
}
}
class Client {
private final ClientManager manager;
public Client(ClientManager manager) {
this.manager = manager;
}
public void logoff() {
manager.remove(this);
}
}
Edit: to answer your comment, I added an example of the Client and how server and client use the manager.
Just to clarify you discuss the client having a play in the client
manager which confuses me. My server accepts a client (socket =
serversocket.accept() ) and sends this socket to a new thread, this
thread handles all communication with the client (messaging and
commands). After the thread is started the thread is added to the
client list. The problem is in the thread when the client sends a
command the thread should run the command then delete itself from the
client list (clientlist.remove(this)). Will your solution still work
here? Cause you discussion about Client confused me
Briefly, yes.
Simple: you are using a List, which is by default not synchronized (you could use a Vector or Collections::synchronizedList for that). Because you do that in several thread, there are synchronization issues (aka "random effect" :)).
Instead of using directly the list, you should rather use a class dedicated to this usage: that's what the ClientManager is for. An object which will manage a list of Client.
This has also another advantages: instead of having several synchronized blocks across your code, all code are in one place.
I'm trying to intercept packets and be able to block them from incoming/outgoing, for a specific domain
In order to do that i made my (java) program adds the domain to the hosts file with a redirection to my own public ipv4 adress (this doesnt matter it just can't be the real IP and i must be able to intercept it, redirecting to my own IP makes sure nobody else in the world receives it). Secondly, i make the program listen to that signal and resend it on a different source port to the real server. (Checksum changes have been taken care of) Now the plan is to receive the response and do the exact same thing, but now by editting the source ip (my own public IP in this case) and the destination port
This should create a program where i'm a kind of middle men between a connection
But it doesnt work as expected, the moment im getting a response of the server (flags SYN/ACK), it's automatically sending them back a RST flag (IPv4/TCP) from the random chosen port by me which isnt the same as the port of the real client
I don't know if there are better ways to do this (there probably are) and how to prevent the problem I'm having, I couldn't really find similiar things to this on the internet. Any kind of help/hints would be appreciated
Keep in mind that I'm using jnetpscape at this moment and it would be nice to continue at what i'm doing right now
EDIT (code):
this is the "HConnection" class (not fully showed but all essential things):
public class HConnection {
private volatile int state = -1; // current state of the program
private volatile boolean HostFileEdited = false;
private volatile String domain = null;
private volatile boolean waitingConnection = false;
private volatile String ipOfDomain = null; // string of the server adress
private volatile byte[] ipofdomb; //4 bytes of the server adress
private volatile String myIpAdr = null; //my IP adress
private volatile byte[] myIpb; //my public IP in 4 bytes
private volatile byte[] port = null; //port of proxy
private volatile byte[] falseport = null; //port of client
private volatile ServerSocket server;
public HConnection() {
try {
server = new ServerSocket(0);
byte[] tempPortb = ByteBuffer.allocate(4).putInt(server.getLocalPort()).array();
System.out.println(server.getLocalPort());
port = new byte[]{tempPortb[2], tempPortb[3]};
(new Thread() {
public void run() {
try {
server.accept();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}).start();
state = 0;
} catch (UnknownHostException e) {System.out.println("fail");} catch (IOException e) {System.out.println("fail");}
}
public String getPublicIP () {
try{
myIpAdr = new BufferedReader(new InputStreamReader(new URL("http://checkip.amazonaws.com/").openStream())).readLine();
System.out.println(myIpAdr);
InetAddress ip = InetAddress.getByName(myIpAdr);
myIpb = ip.getAddress();
return myIpAdr;
}
catch (Exception e){}
return null;
}
public void setUrl(String domain) {
this.domain = domain;
}
public int getState() {
return state;
}
public void prepare() {
try{
URL urlofsite = new URL("https://"+domain);
InetAddress address = InetAddress.getByName(urlofsite.getHost());
ipOfDomain = address.getHostAddress();
System.out.println(ipOfDomain);
ipofdomb = address.getAddress();
addToHostsFile(getPublicIP() + "\t" + domain);
state = 1;
}
catch(Exception e){}
}
public void abort() {
removeFromHostsFile(domain);
HostFileEdited = false;
state = -1;
try {
server.close();
} catch (IOException e) { }
waitingConnection = false;
}
public void awaitConnection() {
if (state == 1) {
waitingConnection = true;
System.out.println("stap1");
StringBuilder errbuf = new StringBuilder(); // For any error msgs
int snaplen = 64 * 1024; // Capture all packets, no truncation
int flags = Pcap.MODE_PROMISCUOUS; // capture all packets
int timeout = 0; // 10 seconds in millis
Pcap pcap = Pcap.openLive("wlp4s0", snaplen, flags, timeout, errbuf);
if (pcap == null) {
System.err.printf("Error while opening device for capture: "
+ errbuf.toString());
return;
}
PcapHeader hdr = new PcapHeader(JMemory.POINTER);
JBuffer buf = new JBuffer(JMemory.POINTER);
int id = JRegistry.mapDLTToId(pcap.datalink());
while (HostFileEdited && waitingConnection && state == 1 && pcap.nextEx(hdr, buf) == Pcap.NEXT_EX_OK) {
PcapPacket packet = new PcapPacket(hdr, buf);
try {
packet.scan(id);
TcpPacket pkt = new TcpPacket(packet);
if (pkt.isTcp()) {
if (pkt.destinationIPequals(myIpAdr) && pkt.getDestinationPort() == 443 && (falseport == null || Arrays.equals(pkt.getSourcePortb(), falseport))) {
if (falseport == null) {
falseport = pkt.getSourcePortb();
}
pkt.changeDestinationIP(ipofdomb);
pkt.changeSourcePort(port);
pkt.iPchecksumFix();
pkt.tcPchecksumFix();
ByteBuffer b = ByteBuffer.wrap(pkt.getPacketInBytes());
System.out.println("10");
System.out.println("OUT"+ (pcap.sendPacket(b)));
}
else if (pkt.sourceIPequals(ipOfDomain) && pkt.getSourcePort() == 443 && falseport != null && Arrays.equals(pkt.getDestinationPortb(),port) ) {
pkt.changeSourceIP(myIpb);
pkt.changeDestinationPort(falseport);
pkt.iPchecksumFix();
pkt.tcPchecksumFix();
ByteBuffer b = ByteBuffer.wrap(pkt.getPacketInBytes());
System.out.println("IN"+ pcap.sendPacket(b));
}
}
}
catch (Exception e) {}
}
System.out.println("stap2");
if (state == 1 && waitingConnection == true) state = 2;
waitingConnection = false;
}
}
}
The "awaitConnection()" method is were currently most things are happening. But this will only be the beginning of my program
HConnection is called from the main class (SWT Designer):
private Button btnNewButton_1;
private HConnection connectie;
private void btnConnect_clicked(SelectionEvent e) throws InterruptedException {
if (btnNewButton_1.getText().equals("Connect")) {
String Url = combo.getText();
connectie = new HConnection();
connectie.setUrl(Url);
connectie.prepare();
lblNewLabel_2.setText("Waiting -> client");
new Thread(new Runnable() {
public void run() {
connectie.awaitConnection();
Display.getDefault().asyncExec(new Runnable() {
public void run() {
if (connectie.getState() == 2) {
lblNewLabel_2.setText("Replacing URL");
}
else {
lblNewLabel_2.setText("Failed");
connectie.abort();
btnNewButton_1.setText("Connect");
}
}
});
if (connectie.getState() == 2) {
// go on with the rest of the program
}
}
}).start();
btnNewButton_1.setText("Abort");
}
else if(btnNewButton_1.getText().equals("Abort")) {
connectie.abort();
lblNewLabel_2.setText("Aborted");
btnNewButton_1.setText("Connect");
}
}
The following code accepts a connection, but doesn't maintain a reference to the resulting Socket instance. This Socket is eligible for garbage collection, and when that happens, it is automatically closed. A client sending data to that socket will then receive an RST.
public void run() {
try {
server.accept();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
I have to listen 3 different ports for UDP in my Java project. I implemented my system as follows:
I have a UDPMessageListener class which implements Runnable. I want to create 3 threads running as this object.
I have an interface called "UDPPacketProcessor" which has a single onPacketReceived method.
Every UDPMessageListener has a UDPPacketProcessor instance, and directs the UDP packet to the object registered as its UDPPacketProcessor.
And I have a DatabaseProc Class which needs to serve to the messages coming from 3 different UDP ports. DatabaseProc implements UDPPacketProcessor
to register itself to those 3 UDPMessageListener classes.
In theory, according my Java knowledge there cannot be any problem, 3 Runnable threads listens to 3 ports, and on packet received, calls a method of my main singleton object's method. However, when I run the program, only one of the ports is listened. I can only the HBMessageListener works correctly, the others do not respond. When I bypass the HBMessageListener (comment out the line I run it) I can now see that only AlarmMessageListener works. Where am I wrong?
UDPMessageListener.java
public class UDPMessageListener implements Runnable {
int port;
byte[] receiveData;
DatagramSocket udpListeningSocket;
UDPPacketProcessor processor;
public UDPMessageListener(UDPPacketProcessor listener,int localPort){
port = localPort;
this.processor = listener;
receiveData = new byte[SRPDefinitions.BYTE_BUFFER_MAX_LENGTH];
try {
udpListeningSocket = new DatagramSocket(port);
} catch (SocketException e) {
System.out.println("Socket bind error in port: " + port);
e.printStackTrace();
}
}
#Override
public void run() {
while(true){
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
try {
udpListeningSocket.receive(receivePacket);
System.out.println("Received UDP Packet from Port:" + port);
processor.onPacketReceived(receivePacket, port);
} catch (IOException e) {
System.out.println("UDP Listener end up with an exception:");
e.printStackTrace();
}
}
}
}
UDPPacketProcessor.java
public interface UDPPacketProcessor {
public void onPacketReceived(DatagramPacket receivedPacket,int localPort);
}
DatabaseProc.java
public class DatabaseProc implements UDPPacketProcessor{
private static DatabaseProc instance = null; // for singleton.
byte[] receiveData;
byte[] sendData;
ByteBuffer systemMessageByteBuffer;
UDPMessageListener HBMessageListener;
UDPMessageListener AlarmMessageListener;
UDPMessageListener TrackMessageListener;
private DatabaseProc(){
receiveData = new byte[SRPDefinitions.BYTE_BUFFER_MAX_LENGTH];
sendData = new byte[SRPDefinitions.BYTE_BUFFER_MAX_LENGTH];
HBMessageListener = new UDPMessageListener(this,SRPDefinitions.HB_PORT);
AlarmMessageListener = new UDPMessageListener(this,SRPDefinitions.ALARM_PORT);
TrackMessageListener = new UDPMessageListener(this,SRPDefinitions.TRACK_PORT);
}
public void run(){
runListeners();
}
private void runListeners(){
HBMessageListener.run();
AlarmMessageListener.run();
TrackMessageListener.run();
}
public static DatabaseProc getInstance(){
if(instance == null){
instance = new DatabaseProc();
}
return instance;
}
#Override
public void onPacketReceived(DatagramPacket receivedPacket, int localPort) {
String strIPAddress =receivedPacket.getAddress().toString();
ByteBuffer buffer = ByteBuffer.allocate(receivedPacket.getLength());
System.out.println("Received Packet Length: " + receivedPacket.getLength() + "/" + receivedPacket.getData().length);
buffer.order(ByteOrder.LITTLE_ENDIAN);
buffer.put(receivedPacket.getData(),0,receivedPacket.getLength());
buffer.position(0);
if(localPort == SRPDefinitions.HB_PORT){
System.out.println("HB Message Received from " + strIPAddress + "!");
SRPHeartBeatMessage message = new SRPHeartBeatMessage(buffer);
//message.print();
}
if(localPort == SRPDefinitions.ALARM_PORT){
System.out.println("ALARM Message Received from " + strIPAddress + "!");
SRPAlarmMessage message = new SRPAlarmMessage(buffer);
message.print();
}
if(localPort == SRPDefinitions.TRACK_PORT){
System.out.println("TRACK Message Received from " + strIPAddress + "!");
}
}
The issue is in DatabaseProc class. Change runListeners to as following.
private void runListeners(){
new Thread(HBMessageListener).start();
new Thread(AlarmMessageListener).start();
new Thread(TrackMessageListener).start();
}
EDIT
Explanation is when UDPMessageListener.run() is directly called, It is not creating a new Thread and executing asynchronously. With your code as it is the execution will never come out of HBMessageListener.run() method as it is in a infinite loop. You need to parallelize the Listeners so that all can listen.
Im facing one problem in streaming data capture for reading the broadcast data during multithreading, pls help or suggest,
Actually there is one class which is reading data from one of the udp socket. Another class accepts the tcp connection from every client request, creates a thread for every client and request the same udp class for data. The thing is working with 1st thread which gets created. But when i request with another client from another pc/ip the packets get losted to the 2nd client/thread
I have made a workaround by creating a list where im storing the Threads outputstream object
and looping it to send the data to all the client. But this is just temporary as it ll delay the packets if clients/connections gets increased.
code for reading UDP Data
public class EventNotifier
{
private InterestingEvent ie;
public DatagramSocket clientSocket;
public String[] split_str;
byte[] receiveData;
HashMap<String, String> secMap = new HashMap<String, String>();
public EventNotifier(InterestingEvent event)
{
ie = event;
clientSocket = new DatagramSocket(9050);
receiveData = new byte[500];
}
public String getDataFeed(String client_id)
{
try
{
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
clientSocket.receive(receivePacket);
String s = new String(receivePacket.getData());
String split_str = s.split(",");
if(secMap.containsValue(split_str[0]))
return s;
else
return "";
} catch(Exception e3) {}
}
}// end of eventNotifier class
code for multithreading handling client requests
public class multiServer
{
static protected List<PrintWriter> writers = new ArrayList<PrintWriter>();
static String client_id = "";
public static void main(String[] args)
{
try
{
ServerSocket servsock = new ServerSocket(8858);
Socket incoming;
while(true)
{
incoming = servsock.accept();
multiServerThread connection = new multiServerThread(incoming);
Thread t1 = new Thread(connection);
t1.start();
}
}
catch(IOException e)
{
System.out.println("couldnt make socket");
}
}
}
class multiServerThread extends Thread implements InterestingEvent
{
Socket incoming;
PrintWriter out=null;
PrintWriter broad=null;
BufferedReader in = null;
String cliString=null;
private EventNotifier en;
int id;
public static String udp_data;
public void interestingEvent(String str1)
{
this.udp_data = str1;
}
public String getUdpData()
{
String _udp_data = this.udp_data;
return _udp_data;
}
multiServerThread(Socket incoming)
{
this.incoming=incoming;
en = new EventNotifier(this);
}
public void run()
{
try
{
out = new PrintWriter(incoming.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(incoming.getInputStream()));
cliString = in.readLine();
multiServer.writers.add(out);
while(true)
{
try
{
udp_data = en.getDataFeed(cliString);
if(udp_data!=null && udp_data.length()>0)
{
//workaround for serving the data to all cleints who are connected
for (int i=0; i<multiServer.writers.size();i++)
{
broad=multiServer.writers.get(i);
broad.println(udp_data.trim());
}
//else will directly write to the outputstream object for every thread which is connected
// out.println(udp_data.trim());
}
}
catch (Exception e)
{
System.out.println("exception "+e);
}
Thread.sleep(1);
}
} catch(IOException e)
{
System.out.print("IO Exception :: "+ e);
}
catch(InterruptedException e)
{
System.out.print("exception "+ e);
}
}
}
You need mutual exclusion (or a different design).
For example, what will happen if two threads call multiServer.writers.add(out); concurrently?
From the ArrayList Javadocs
Note that this implementation is not synchronized. If multiple threads access an ArrayList instance concurrently, and at least one of the threads modifies the list structurally, it must be synchronized externally. (A structural modification is any operation that adds or deletes one or more elements, or [...])
Another problem is two calling udp_data = en.getDataFeed(cliString); concurrently. The second thread might overwrite the result of the first. You'll loose data!
What happens if one thread calls for (int i=0; i<multiServer.writers.size();i++) while another thread is busy doing multiServer.writers.add(out);? The size may have increased, before out has actually been added to the list!
public class multiServer
{
private List<PrintWriter> writers = new ArrayList<PrintWriter>();
public synchronized void addWriter(PrintWrite out) {
writers.add(out);
}
public synchronized void serveAllWriters(String data) {
for (int i=0; i<multiServer.writers.size();i++)
{
broad=multiServer.writers.get(i);
broad.println(data);
}
}
}
Now when a thread tries to add a writer, the synchronizeds will make sure no other thread is adding or printing. So multiServerThread should be fixed to use the new methods:
class multiServerThread extends Thread implements InterestingEvent
{
//...
private String udp_data;
//...
myMultiServer.addWriter(out);
//...
udp_data = en.getDataFeed(cliString);
if(udp_data!=null && udp_data.length()>0)
myMultiServer.serveAllWriters(udp_data.trim());
//...
}
There might be more problems, not sure I don't fully understand your code. The question you must ask yourself is, can another thread read and/or write the same data or object? Yes? Then you'll need proper synchronization.