I'm writing a main class that will create a few clients and test them subscribing and publishing. I'd like to display information of the clients connection, like the data & time connected, clientId, clientIP used to connect, whether they connected gracefully or not. I'm new to using tools like Logger so I'm not sure how I would do this. I left a link to the HiveMQ community edition (broker) and the client. I'd like to display this information in my main class in the HiveMQ client project but there a log file in the community edition called event.log which contains exactly the kind of information I want to display. I left an image below.
HiveMQ:
https://github.com/hivemq/hivemq-community-edition
https://github.com/hivemq/hivemq-mqtt-client
There is an event.log file in hivemq-community-edition that has the kind of information I'd like to display. It was generated when I build the project as a Gradle project so it won't be found unless you imported into Eclipse and built in with Gradle so I left a screenshot of what it looks like.
Code in my Main class in HiveMQ Client:
package com.main;
import java.util.UUID;
import com.hivemq.client.mqtt.MqttGlobalPublishFilter;
import com.hivemq.client.mqtt.datatypes.MqttQos;
import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient;
import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient.Mqtt5Publishes;
import com.hivemq.client.mqtt.mqtt5.Mqtt5Client;
import com.hivemq.client.mqtt.mqtt5.message.publish.Mqtt5Publish;
import java.util.logging.Logger;
import java.util.NoSuchElementException;
import java.util.logging.Level;
import java.util.concurrent.TimeUnit;
public class Main {
private static final Logger LOGGER = Logger.getLogger(Main.class.getName()); // Creates a logger instance
public static void main(String[] args) {
Mqtt5BlockingClient client1 = Mqtt5Client.builder()
.identifier(UUID.randomUUID().toString()) // the unique identifier of the MQTT client. The ID is randomly generated between
.serverHost("localhost") // the host name or IP address of the MQTT server. Kept it 0.0.0.0 for testing. localhost is default if not specified.
.serverPort(1883) // specifies the port of the server
.buildBlocking(); // creates the client builder
client1.connect(); // connects the client
System.out.println("Client1 Connected");
System.out.println(client1.toString());
String testmessage = "How is it going!";
byte[] messagebytesend = testmessage.getBytes(); // stores a message as a byte array to be used in the payload
try {
Mqtt5Publishes publishes = client1.publishes(MqttGlobalPublishFilter.ALL); // creates a "publishes" instance thats used to queue incoming messages
client1.subscribeWith() // creates a subscription
.topicFilter("test1/#") // filters to receive messages only on this topic (# = Multilevel wild card, + = single level wild card)
.qos(MqttQos.AT_LEAST_ONCE) // Sets the QoS to 2 (At least once)
.send();
System.out.println("The client1 has subscribed");
client1.publishWith() // publishes the message to the subscribed topic
.topic("test/pancakes/topic") // publishes to the specified topic
.qos(MqttQos.AT_LEAST_ONCE)
.payload(messagebytesend) // the contents of the message
.send();
System.out.println("The client1 has published");
Mqtt5Publish receivedMessage = publishes.receive(5,TimeUnit.SECONDS).get(); // receives the message using the "publishes" instance waiting up to 5 seconds // .get() returns the object if available or throws a NoSuchElementException
byte[] tempdata = receivedMessage.getPayloadAsBytes(); // converts the "Optional" type message to a byte array
System.out.println();
String getdata = new String(tempdata); // converts the byte array to a String
System.out.println(getdata);
}
catch (InterruptedException e) { // Catches interruptions in the thread
LOGGER.log(Level.SEVERE, "The thread was interrupted while waiting for a message to be received", e);
}
catch (NoSuchElementException e){
System.out.println("There are no received messages"); // Handles when a publish instance has no messages
}
client1.disconnect();
System.out.println("Client1 Disconnected");
}
}
You can obtain information about the client with the method getConfig e.g.
Mqtt5ClientConfig config = client.getConfig();
config.getClientIdentifier();
To get the information of the current connection use getConnectionConfig e.g.
Optional<Mqtt5ClientConnectionConfig> connectionConfig = config.getConnectionConfig();
if (connectionConfig.isPresent()) {
MqttClientTransportConfig transportConfig = connectionConfig.get().getTransportConfig();
}
You can also use listeners which are notified when the client is connected or disconnected e.g.
Mqtt5Client.builder()
.addConnectedListener(context -> System.out.println("connected"))
.addDisconnectedListener(context -> System.out.println("disconnected"))
...
Related
Our web app acts as an integration layer which allows the users to run Matlab code (Matlab is a scientific programming language) which was compiled to Java, packaged up as jar files via browser (selected ones as in above image, except for remote_proxy-1.0.0.jar which is not, it is used for RMI).
The problem is that, Matlab Java runtime, contained inside the javabuilder-1.0.0.jar file, has a process-wide blocking mechanism which means if the first user sends an HTTP request to execute the cdf_read-1.0.0.jar or any Matlab-compiled-to-Java jars at all, then subsequent requests will block until the first one finishes and it will take no less than 5 seconds since JNI is used to invoke the native Matlab code and because the app server just spawns new threads to serve each request, but once again, due to the process-wide locking mechanism of Matlab Java runtime, these newly spawned threads will just block waiting for the first request to be fulfilled, thus our app can technically serve one user at a time.
So to work around this problem, for each such request, we start a new JVM process, send the request to this new process to run the job using RMI, then return the result back to the app server's process, then destroy the spawned process. So we've solved the blocking issue, but this is not very good at all in terms of memory used, this is a niche app, so number of users is in the range of thoudsands. Below is the code used to start a new process to run the BootStrap class which starts a new RMI registry, and binds a remote object to run the job.
package rmi;
import java.io.*;
import java.nio.file.*;
import static java.util.stream.Collectors.joining;
import java.util.stream.Stream;
import javax.enterprise.concurrent.ManagedExecutorService;
import org.slf4j.LoggerFactory;
//TODO: Remove sout
public class ProcessInit {
public static Process startRMIServer(ManagedExecutorService pool, String WEBINF, int port, String jar) {
ProcessBuilder pb = new ProcessBuilder();
Path wd = Paths.get(WEBINF);
pb.directory(wd.resolve("classes").toFile());
Path lib = wd.resolve("lib");
String cp = Stream.of("javabuilder-1.0.0.jar", "remote_proxy-1.0.0.jar", jar)
.map(e -> lib.resolve(e).toString())
.collect(joining(File.pathSeparator));
pb.command("java", "-cp", "." + File.pathSeparator + cp, "rmi.BootStrap", String.valueOf(port));
while (true) {
try {
Process p = pb.start();
pool.execute(() -> flushIStream(p.getInputStream()));
pool.execute(() -> flushIStream(p.getErrorStream()));
return p;
} catch (Exception ex) {
ex.printStackTrace();
System.out.println("Retrying....");
}
}
}
private static void flushIStream(InputStream is) {
try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
br.lines().forEach(System.out::println);
} catch (IOException ex) {
LoggerFactory.getLogger(ProcessInit.class.getName()).error(ex.getMessage());
}
}
}
This class is used to start a new RMI registry so each HTTP request to execute Matlab code can be run in a separate process, the reason we do this is because each RMI registry is bound to a process, so we need a separate registry for each JVM process.
package rmi;
import java.rmi.RemoteException;
import java.rmi.registry.*;
import java.rmi.server.UnicastRemoteObject;
import java.util.logging.*;
import remote_proxy.*;
//TODO: Remove sout
public class BootStrap {
public static void main(String[] args) {
int port = Integer.parseInt(args[0]);
System.out.println("Instantiating a task runner implementation on port: " + port );
try {
System.setProperty("java.rmi.server.hostname", "localhost");
TaskRunner runner = new TaskRunnerRemoteObject();
TaskRunner stub = (TaskRunner)UnicastRemoteObject.exportObject(runner, 0);
Registry reg = LocateRegistry.createRegistry(port);
reg.rebind("runner" + port, stub);
} catch (RemoteException ex) {
Logger.getLogger(BootStrap.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
This class allows to submit the request to execute the Matlab code, returns the result, and kill the newly spawned process.
package rmi.tasks;
import java.rmi.*;
import java.rmi.registry.*;
import java.util.Random;
import java.util.concurrent.*;
import java.util.logging.*;
import javax.enterprise.concurrent.ManagedExecutorService;
import remote_proxy.*;
import rmi.ProcessInit;
public final class Tasks {
/**
* #param pool This instance should be injected using #Resource(name = "java:comp/DefaultManagedExecutorService")
* #param task This is an implementation of the Task interface, this
* implementation should extend from MATLAB class and accept any necessary
* arguments, e.g Class1 and it must implement Serializable interface
* #param WEBINF WEB-INF directory
* #param jar Name of the jar that contains this MATLAB function
* #throws RemoteException
* #throws NotBoundException
*/
public static final <T> T runTask(ManagedExecutorService pool, Task<T> task, String WEBINF, String jar) throws RemoteException, NotBoundException {
int port = new Random().nextInt(1000) + 2000;
Future<Process> process = pool.submit(() -> ProcessInit.startRMIServer(pool, WEBINF, port, jar));
Registry reg = LocateRegistry.getRegistry(port);
TaskRunner generator = (TaskRunner) reg.lookup("runner" + port);
T result = generator.runTask(task);
destroyProcess(process);
return result;
}
private static void destroyProcess(Future<Process> process) {
try {
System.out.println("KILLING THIS PROCESS");
process.get().destroy();
System.out.println("DONE KILLING THIS PROCESS");
} catch (InterruptedException | ExecutionException ex) {
Logger.getLogger(Tasks.class.getName()).log(Level.SEVERE, null, ex);
System.out.println("DONE KILLING THIS PROCESS");
}
}
}
The questions:
Do I have to start a new separate RMI registry and bind a remote to it for each new process?
Is there a better way to achieve the same result?
You don't want JVM startup time to be part of the perceived transaction time. I would start a large number of RMI JVMs ahead of time, dependending on the expected number of concurrent requests, which could be in the hundreds or even thousands.
You only need one Registry: rmiregistry.exe. Start it on its default port and with an appropriate CLASSPATH so it can find all your stubs and application classes they depend on.
Bind each remote object into that Registry with sequentially-increasing names of the general form runner%d.
Have your RMI client pick a 'runner' at random from the known range 1-N where N is the number of runners. You may need a more sophisticated mechanism than mere randomness to ensure that the runner is free at the time.
You don't need multiple Registry ports or even multiple Registries.
This is the error I am getting from the code bellow.
Error:
Usage: org.jitsi.examples.AVReceive2 <args>
Valid args:
--local-port-base= The port on which media is to be received. The specified value will be used as the port to receive the audio RTP on, the next port after it will be used to receive the audio RTCP on. Respectively, the subsequent ports will be used to receive the video RTP and RTCP on.
--remote-host= The name of the host from which the media is to be received.
--remote-port-base= The port from which media is to be received. The specified vaue will be used as the port to receive the audio RTP from, the next port after it will be used to receive the audio RTCP from. Respectively, the subsequent ports will be used to receive the video RTP and RTCP from.
All the code I have:
package com.hexicle.DiscordBot;
import me.itsghost.jdiscord.DiscordAPI;
import me.itsghost.jdiscord.DiscordBuilder;
import me.itsghost.jdiscord.exception.BadUsernamePasswordException;
import me.itsghost.jdiscord.exception.DiscordFailedToConnectException;
public class MyBot {
private DiscordAPI api;
public MyBot()
{
api = new DiscordBuilder().build();
}
public void connect()
{
try {
api.login("", "");
} catch (BadUsernamePasswordException | DiscordFailedToConnectException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
MyBot bot = new MyBot();
bot.connect();
}
}
I am not very experienced with Java so this might be super easy! Thanks!
I have a java socket client application that send data through TCP over Socket. The data it send is byte array and can send inconsistent data within inconsistent time interval. Although data send at a particular time will be complete in itself and will not have any marker for end of message. Code for client cannot be modified. I had to create a running thread for server so that it will read data whenever it is available on socket.Server application work as - Another class create server socket and start thread for incoming message. Incoming message thread read for message on socket passed while initializing the thread.
The code has following issues.1) It reads only the first data sent from client and thread exits on attempting to read second run2) Cannot use thread to wait for some time period as data can be sent from client in any time interval3) Cannot try opening and closing socket as another thread on server application will send data to client at any time interval.4) Do not want code that will block execution of complete application as another thread of this application will be sending data to client through same socket.
Please help and let me know for any further information. Code as below :
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
public class IncomingMsg extends Thread {
InputStream in =null;
private Socket clientSocket =null;
public IncomingMsg(Socket clientSoc){
this.start();
clientSocket = clientSoc;
}
public void run(){
try {
in = clientSocket.getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
while (true) {
try {
byte[] data = new byte[100];
int count = in.read(data); //this is where thread exits on second run as new message may not have been received by the time execution reaches this point.
String message = "";
if(count>0)
{
for(int num=0;num<count;num++)
{message = message + " "+ data[num];}
System.out.println("Received Message from IP:"+clientSocket.getInetAddress()+" Message Byte:" +message);
}
data=null;
} catch (Exception e) {
logger.error(this.getClass().getName()+ ": Exception in receiving message "+ e);
}}} }
There was no issue with the given code. Problem was client application changes local port if it does not receive any message. Since server application is not sending any message, local port of client increment and thus server cannot read data on the second run. Will update answer more if any further clue is received.
I am new to MQ programmimg. As per my requirement I am trying to put a sample XML message in a queue and expecting a response back from the response queue.
I can see that the associated channel is opening for a short duration, for a few seconds and then getting closed. Please find below the code I am using to put the message in queue. Request your valuable inputs in getting this issue resolved.
Error:
Process(12908.13579) User(abc) Program(amqrmppa)
Host(hostname)
AMQ9208: Error on receive from host 10 (10.0.0.1).
EXPLANATION:
An error occurred receiving data from 10 (10.0.0.1) over TCP/IP. This may
be due to a communications failure.
Code Used:
package com.company.mq;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import com.ibm.mq.MQC;
import com.ibm.mq.MQEnvironment;
import com.ibm.mq.MQException;
import com.ibm.mq.MQGetMessageOptions;
import com.ibm.mq.MQMessage;
import com.ibm.mq.MQPutMessageOptions;
import com.ibm.mq.MQQueue;
import com.ibm.mq.MQQueueManager;
public class MQConnection {
private static final String CORR_ID = "CORRELID";
String qMgrStr = "";
String hostName = "hostname";
String password ="xxxx";
String userName ="username";
String putqueueName = "putqueuename";
String getqueuename = "getqueuename ";
String channel = "channel";
String replyToQueue = "replyToQueue";
String replyToQueueManager = "";
static String content = "";
int port =10000;
MQQueue readQueue = null;
MQQueue writeQueue = null;
MQQueueManager qManager;
#SuppressWarnings("unchecked")
public void init(){
MQEnvironment.hostname =hostName;
MQEnvironment.channel = channel;
MQEnvironment.port = port;
MQEnvironment.userID = userName;
MQEnvironment.password = password;
MQEnvironment.properties.put(MQC.TRANSPORT_PROPERTY, MQC.TRANSPORT_MQSERIES_CLIENT);
try {
qManager = new MQQueueManager("");
System.out.println("qManager====>"+qManager);
}catch(Exception e){
e.printStackTrace();
}
try {
System.out.println("qManager==> hhh"+qManager);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public String putAndGetMessage() throws InterruptedException, IOException{
int openOptions = MQC.MQOO_OUTPUT | MQC.MQPMO_SET_ALL_CONTEXT | MQC.MQOO_FAIL_IF_QUIESCING;
String msgString = content.toString();
System.out.println("msgString=="+msgString);
int expiryTime =60000;
MQMessage getmessage = null;
int waitInterval =4000;
try {
System.out.println("qManager Desc==>"+qManager.getDescription());
writeQueue =openWriteQueue(qManager,putqueueName);
MQMessage message = myPut(writeQueue,msgString,expiryTime,getqueuename);
// qManager.accessQueue(putqueueName, openOptions,null,null,null);
readQueue =openReadQueue(qManager,getqueuename);
getmessage =mqGet(readQueue,waitInterval,message.messageId);
/*MQMessage msg = new MQMessage();
msg.messageType = MQC.MQMT_REQUEST;
msg.format = "MQSTR";
// msg.characterSet = 500;
msg.persistence = MQC.MQPER_NOT_PERSISTENT;
msg.correlationId = CORR_ID.getBytes();
// msg.messageId = CORR_ID.getBytes();
msg.expiry= 10000;*/
/*System.out.println("before");
Thread.sleep(10000);
System.out.println("after");*/
/*MQGetMessageOptions gmo = new MQGetMessageOptions();
int openOptions1 = MQC.MQGMO_WAIT| MQC.MQGMO_CONVERT| MQC.MQGMO_FAIL_IF_QUIESCING;
System.out.println("in getqManager==>"+qManager);
readQueue = qManager.accessQueue(getqueuename, openOptions1);
System.out.println("deafaultQueue======>"+readQueue);
readQueue.get(getmessage,gmo);
System.out.println(getmessage.readInt());
String retriveMsg = getmessage.readUTF();
System.out.println("read===>"+retriveMsg);*/
} catch(MQException e){
e.printStackTrace();
}
return getmessage.readString(getmessage.getMessageLength());
}
private MQMessage mqGet(MQQueue readQueue2, int waitInterval,
byte[] corrID) throws MQException {
MQMessage responseMessage = new MQMessage();
responseMessage.correlationId =corrID;
MQGetMessageOptions gmo = new MQGetMessageOptions();
gmo.options =MQC.MQGMO_WAIT| MQC.MQGMO_CONVERT| MQC.MQGMO_FAIL_IF_QUIESCING;
gmo.matchOptions = MQC.MQMO_MATCH_CORREL_ID;
gmo.waitInterval = waitInterval;
// TODO Auto-generated method stub
readQueue2.get(responseMessage,gmo);
return responseMessage;
}
private MQQueue openReadQueue(MQQueueManager manager, String getqueuename2) throws MQException {
// TODO Auto-generated method stub
return openQueue(manager,getqueuename2,MQC.MQOO_INPUT_SHARED | MQC.MQOO_INQUIRE |MQC.MQOO_FAIL_IF_QUIESCING);
}
private MQMessage myPut(MQQueue writeQueue2, String msgString,
int expiryTime, String getqueuename2) {
// TODO Auto-generated method stub
MQPutMessageOptions mpo =new MQPutMessageOptions();
mpo.options = MQC.MQPMO_NEW_MSG_ID | MQC.MQMO_MATCH_CORREL_ID;
MQMessage putmessage = new MQMessage();
putmessage.format = MQC.MQFMT_STRING;
putmessage.messageFlags = MQC.MQMT_REQUEST;
putmessage.replyToQueueName =replyToQueue;
putmessage.replyToQueueManagerName = qMgrStr;
putmessage.userId="userId";
putmessage.expiry =expiryTime;
try {
putmessage.write(msgString.getBytes());
try {
writeQueue2.put(putmessage,mpo);
} catch (MQException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return putmessage;
}
private MQQueue openWriteQueue(MQQueueManager manager, String queueName) throws MQException{
// TODO Auto-generated method stub
return openQueue(manager,queueName,MQC.MQOO_OUTPUT | MQC.MQPMO_SET_ALL_CONTEXT | MQC.MQOO_FAIL_IF_QUIESCING);
}
private MQQueue openQueue(MQQueueManager manager, String queueName, int options) throws MQException{
// TODO Auto-generated method stub
return manager.accessQueue(queueName, options,null,null,null);
}
/**
* #param args
* #throws IOException
* #throws InterruptedException
*/
public static void main(String[] args) throws IOException, InterruptedException {
// TODO Auto-generated method stub
MQConnection conn = new MQConnection();
DataInputStream dis = new DataInputStream ( new FileInputStream ("c://request//Request.xml"));
byte[] datainBytes = new byte[dis.available()];
dis.readFully(datainBytes);
dis.close();
content = new String(datainBytes, 0, datainBytes.length);
//System.out.println("content===>"+content);
conn.init();
System.out.println("connected");
conn.putAndGetMessage();
}
}
The application appears to be a class which encapsulates the MQ I/O. On instantiation, the init routine constructs the class and attempts to connect to the queue manager. There are a few problems with the code that I can see. Also, and I suspect I know why the code fails but there is no diagnostic information provided in the question so I can only guess. I'll describe the problems first, then which diagnostic info I'd normally expect.
Code issues
The code provides a user ID and password. Unless this is a v8.0 client talking to a v8.0 queue manager, or a client that uses an exit to talk to MQ over a TLS channel, the password is not being checked and not needed. Even worse, if the channel is not TLS and encrypted, the ID and password are being transmitted in the clear over the network. Take the password out. Use the ID only if the ID presented by the app needs to be overridden in the connection request.
The code is not printing the linked exception. A JMS exception is a multi-level data structure in which the top level is the exception as JMS understands it and the lower level exception is the one the transport provider understands. Many transport providers, especially those written in pure Java, use only the top level of the data structure and do not supply a linked exception. However, as a developer you must not assume that this will never be the case.
There is no valid reason for a JMS developer to ever fail to print the linked exception.
When I was managing the MQ Admin team at a large bank, my policy was that nothing went to Production if it did not follow these rules:
When encountering a JMS exception, the code must print the linked exception.
If there is no exception linked to a JMS Exception, the code must print the string "No linked exception found" so that we know that it has at least looked for one.
The code opens the queue with the MQC.MQPMO_SET_ALL_CONTEXT option. This level of privilege is not normally granted to non-admin users. The code explicitly specifies an ID which suggests it is not running with admin privileges. For these reasons, I suspect that you are getting a 2035 error, which of course you do not see because the code fails to print the linked exception.
Missing diagnostics
MQ behaves differently from version to version. Over the years the Java/JMS classes have been repackaged, refactored, and upgraded to JMS 1.1, then to JMS 2.0. Diagnostic questions, whether posted here or sent to your MQ Admin tean for a Production outage, should indicate the versions of MQ client and MQ server in use. The output of dspmqver -a on the client node and the server node are always helpful.
As mentioned previously, the linked exception is not being printed. Without it the stack trace is probably worthless, so for once I'm not that disappointed no stack trace was provided. However, update the code to print the linked exception and include a stack trace in the future.
Both the client and the server produce error logs. In addition to the queue manager's error log, there are global error logs on the server for things that happen before the queue manager has been identified or for commands which operate on the global MQ configuration. No error log information from any of these sources was provided in this case. Why not?
The server also produces event messages. These are normally consumed by a monitoring agent but may be enabled by administrators and reviewed to determine the problem. In the absence of error log messages, event messages can be very helpful. I would not expect them in this case but mention it here for completeness.
FDC files are produced by the queue manager and stored in the same directory as the global error logs. These provide much more detailed information than the error log entries but are not always produced. Usually a problem diagnosis request would mention either that there were no FDC files produced, or else that they were and provide the header block from them.
Resources
Please see:
The MQ Knowledge Center, if you have not already found it.
In particular, the section on troubleshooting,
and the section on linked exceptions. (Although these are not specific to MQ, IBM has at least provided some sample code you can copy and paste. Be sure to paste as plain text and correct any curly quotes.)
The MQ SupportPacs page,
...and in particular MS0T is the latest MQ Explorer (which works with back-level QMgrs) and MS0P is a set of plug-ins that allow Explorer to parse the event messages, among many other things.
IBM's MQDev community. Though this is great for finding articles and reference materials, the forums are mediocre.
For a community, I much prefer the Vienna MQ list server or the forum at MQSeries.net. Both are thriving communities of developers, admins and IBMers.
Keep posting here in the websphere-mq tag, of course. Neither of the communities above has a well-developed FAQ for MQ and Stack Overflow provides that function. There are several MQ old-timers and IBMers curating the Stack Overflow MQ tag.
If you can manage the funding and travel, attend the MQ Tech Conference. IBM used to have the Transaction and Messaging conference but combined those with the rest of WebSphere to form IMPACT. Last year it combined several conferences, including IMPACT, into Interconnect. The MQ Tech conference is all MQ, all week, and insanely inexpensive. Since IBM outsourced their MQ curriculum and training, classroom training is a less viable option, and the conferences cover material not available in the classroom training.
Stick with it and good luck!
Full disclosure: I am one of the founders, and my MQ consulting firm is a sponsor, of the MQ Tech conference. However, it had almost as many MQ sessions on it's first day last year as Interconnect had all week so I do not see a conflict of interest in recommending it.
I am developing an application on Android where I am searching for all the peers in the range and afterwards connect with all of them, The device who initiated the the discovery become the group owner and all others become client, I have done all the connection thing but now I want to the group owner to send the message to all the connecting peers, How to achieve this and also please tell me what is the methodology in peer-to-peer communication , Does p2p in Android also use IP to send and receive data?
Thankyou
Regards Talib.
Wi-Fi Direct/P2P can be considered as normal Wi-Fi but where the group owner (GO) acts as a software access point (dhcp server, provisioning, etc). So to answer your last question, yes Wi-Fi Direct also uses IP to send and receive data.
You want to send data to all members in the group? There are two solutions for this:
Broadcast the message once using multicast.
Send the message to each individuel client in the group.
The most efficient method would be solution 1, to broadcast the data using multicast, as you would only need to send the data once. Unfortunately Wi-Fi multicast support is very fragmented in Android, as a lot of devices seem to block non-unicast traffic. See this article for more in depth information if you want to go down this route.
Solution 2 is the best method if you want to guarantee support on all devices and only transmit a small amount of data. The GO need the IP addresses of the clients in the group, but because of the way Wi-Fi Direct is implemented in Android, only the GO IP is known to all devices. One solution is to let the clients connect to a socket on the GO, to get their IP address:
Client code
private static final int SERVER_PORT = 1030;
... // on group join:
wifiP2pManager.requestConnectionInfo(channel, new ConnectionInfoListener() {
#Override
public void onConnectionInfoAvailable(WifiP2pInfo p2pInfo) {
if (!p2pInfo.isGroupOwner) {
// Joined group as client - connect to GO
Socket socket = new Socket();
socket.connect(new InetSocketAddress(p2pInfo.groupOwnerAddress, SERVER_PORT));
}
}
});
Group owner code:
private static final int SERVER_PORT = 1030;
private ArrayList<InetAddress> clients = new ArrayList<InetAddress>();
public void startServer() {
clients.clear();
ServerSocket serverSocket = new ServerSocket(SERVER_PORT);
// Collect client ip's
while(true) {
Socket clientSocket = serverSocket.accept();
clients.add(clientSocket.getInetAddress());
clientSocket.close();
}
}
Now all you need to do is start a serversocket on each client, and make to GO iterate through the client list creating a socket connection to each and sending the message you want to broadcast.
Now we have https://github.com/libp2p/go-libp2p-pubsub - for handling multicast messages (and it's also could shard network into topics)
and we also have some pretty peer discovery protocol like that: https://github.com/libp2p/go-libp2p-examples/blob/master/chat-with-mdns/mdns.go
So, you can very easily to interact with topic messages multicast in local network, just using libp2p
I've just test https://github.com/MoonSHRD/p2chat-android which wrap solution you need into single library, which can be used from android.
This far we could interact with high level of messages instead of interaction with sockets or streams at low levels. Hope this will help someone.
p.s. However, I should notice, that I didn't test mDNS discovery in wi-fi direct networks yet
There are several options to start with but you can choose according to your requirements. It's fairly easy to broadcast and discover the service using jmdns/jmdns. Here is the example from docs,
Service Registration
import java.io.IOException;
import java.net.InetAddress;
import javax.jmdns.JmDNS;
import javax.jmdns.ServiceInfo;
public class ExampleServiceRegistration {
public static void main(String[] args) throws InterruptedException {
try {
// Create a JmDNS instance
JmDNS jmdns = JmDNS.create(InetAddress.getLocalHost());
// Register a service
ServiceInfo serviceInfo = ServiceInfo.create("_http._tcp.local.", "example", 1234, "path=index.html");
jmdns.registerService(serviceInfo);
// Wait a bit
Thread.sleep(25000);
// Unregister all services
jmdns.unregisterAllServices();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
Service Discovery
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import javax.jmdns.JmDNS;
import javax.jmdns.ServiceEvent;
import javax.jmdns.ServiceListener;
public class ExampleServiceDiscovery {
private static class SampleListener implements ServiceListener {
#Override
public void serviceAdded(ServiceEvent event) {
System.out.println("Service added: " + event.getInfo());
}
#Override
public void serviceRemoved(ServiceEvent event) {
System.out.println("Service removed: " + event.getInfo());
}
#Override
public void serviceResolved(ServiceEvent event) {
System.out.println("Service resolved: " + event.getInfo());
}
}
public static void main(String[] args) throws InterruptedException {
try {
// Create a JmDNS instance
JmDNS jmdns = JmDNS.create(InetAddress.getLocalHost());
// Add a service listener
jmdns.addServiceListener("_http._tcp.local.", new SampleListener());
// Wait a bit
Thread.sleep(30000);
} catch (UnknownHostException e) {
System.out.println(e.getMessage());
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
if you are developing desktop application in java, your goal should be to find the best cross-platform DNS-SD (Zeroconf, Bonjour, DNS self discovery) library exists out there.
There are other pure Java DNS-SD implementations, but it's unclear if any of them offer a library that is as easy to use or fully tested as DNS-SD. Head over to the
Waiter once. However, I prefer going through the jmdns, It works well. As the peer to peer connection is supposed to use IP(it have to), you can send/receive data easily once the connection is established.