Article by monkeyHi

This article is contributed by the developer of Agora. If you have any questions, please feel free to contact the author.

In today’s highly developed society, we cannot live without social contact and social network. In recent years, behind the steady and rapid development of the live broadcasting industry lies the fact that people need a more real-time Internet environment, just like the timely and effective face-to-face communication.

Try the Agora SignalingSDK this time.

Initial knowledge of Agora signaling SDK

Agora Signaling is a member of the Agora Family, designed to enable real-time point-to-point communications. Agora Signalling is designed to serve Agora buckets as a plug-in and can be used for real-time messaging.

Development of the document

The Agora website is already well documented.

For example, Agroa Signaling provides a description of client integration and a description of server integration. Common client implementations are described in the client section.

A siege lion with some development experience will soon be able to get started.

Of course, we also found a problem, the documentation only quick start, no further description of the use of the interface considerations. With this in mind, I quickly scanned the API reference section, and none of the interfaces provided specific demo codes and considerations. The basic access idea is as follows:

  1. Initialize the

  2. The login

  3. Point-to-point messaging

  4. News channel

  5. Call to invite

  6. The cancellation

The official Demo

Agroa’s official website provides various demo about Agora signaling. After a brief browse, it is easy to understand and there is no very strange writing method.

However, one problem with these demos is that they don’t have comments. This is not particularly friendly to newcomers to Agora products and may take a bit of effort to become familiar with the interfaces.

Some possible application scenarios

Through the Agora website and the API already published. Agora Signaling is one of the most common features of a chat room, message board and other interactive communication scenarios.

Live broadcast live live chat

Live chat and live chat are essentially a combination of message board and instant messaging. Agora signaling itself is designed for real-time communication and interaction, which can be achieved by adding a chat database to retain history.

Doctor-patient remote diagnosis

In real life, due to the influence of distance, time, psychology and many other factors, patients may not be able to arrive at the hospital in time, and doctors may not be able to arrive at the scene in time. At this time, instant communication network can provide a lot of convenience. Through an App, patients or their family members can transmit their feelings to doctors through video, voice and text, and communicate at any time. Just like on-site consultation, patients may also need a patient group or channel to share and communicate.

alerts

I believe you are familiar with mobile phone SMS, wechat message, QQ message, we can also achieve a simple version of the network SMS function with the help of Agora signaling.

Customer service function

Some products may require a customer service function, so that users can consult through the chat window at any time when they encounter problems, and there is no need to add additional customer service personnel on wechat. Communicate effectively while protecting each other’s privacy.

Communication between devices with high real-time performance

For example, I have A batch of mining machines in A province and need to know the condition of the machine room in time. Then I can set up A communication machine in the machine room and send the collected data back to the database through Agora signaling in time and record it in the database. While this scenario may not be the one Agora signaling was designed for, it is a good alternative.

Online Classroom interaction

In various online learning programs, including remote exams, classroom interaction is not limited to text, voice, and images, but is often combined.

Live shopping guide interaction

If there is such a live event, the screen is no different from that of TV shopping guide, but it can be more convenient to place orders, scan codes, communicate, fill in information, make payment, obtain order status, as well as on-site interaction on the terminal.

Scientific research

The need for remote collection and observation of all kinds of data. Experimental demonstration, etc. Real-time acquisition and processing of experimental data.

Almost any scenario you can think of that requires real-time communication, point-to-point communication, or channel-splitting communication.

Before actually making my own application, I first started to run the official demo to start the journey of stepping on the pit.

To prepare

Author experience environment:

  • windows10 x64
  • IntelliJ IDEA 2018.3.2 x64
  • SDK
  • jdk1.8

The SDK directory

Decompress the SDK and obtain the following directory structure. We will learn and understand the server SDK and API based on the samples: Agora-Signaling- Turmorale-Java.

├─ ├─ exercises, ├─ exercises, exercises, exercises, exercises, exercises, exercises, exercises, exercises ├─ ├─ garbage ├─ garbage ├─ garbage ├─ garbage ├─ garbage ├─ garbage ├─ garbage ├─ garbage ├─ garbage ├─ garbage ├─ garbage ├─ garbage ├─ garbage ├─ garbage ├─ garbage ├─ garbage ├─ garbage ├─ garbage ├─ garbage ├─ garbage ├─ garbage ├─ garbage ├─ SRC ├─main ├─ Java ├─ Mainclass ├─model ├─ toolCopy the code

Import as idea project

Earlier we briefly previewed the SDK directory, a Gradle project. Very easy to import idea. Here we use IDEA to build the Demo running environment.

1. Access Agora-Signaling-Tutorial-Java 2. Right-click –> Open Folder as InterlJ Idea Project 3. Wait for the import to complete, usually very quickly

configuration

1. Configure the SDK

Make sure that all jar packages in lib, libs-dep and SDK are in the project’s lib directory.

2. View and modify build.gradle, paying attention to line 14

dir: 'lib', include: ['* .jar']
Copy the code

Is amended as:

dir: 'lib', include: ['*.jar']
Copy the code

There is no space after the asterisk *. Build. gradle:

group 'com.agora'
version 1.0 the SNAPSHOT ' '
apply plugin: 'java'
sourceCompatibility = 1.5 repositories {jcenter()} dependencies {testCompile group: 'junit', name: 'junit', version: '4.11'
    compile fileTree (dir: 'lib', include: ['*.jar'])}Copy the code

When gradle configuration Changes, idea prompts you to import Changes. Click Import Changes.

Make sure gradle successfully introduces dependent JAR packages.

3. The configuration appid

Tip: Note that Agora has two authentication mechanisms. Use appID directly, or use token. For the convenience of demonstration, we directly use appID to complete the authentication, but at the same time, the author also moved to Java token algorithm. See step 4 introduction for details.

Switch to the Pancages perspective, find Tool /COnstant, note lines 8 to 11,

static {
 //app_ids.add("Your_appId");
 //app_ids.add("Your_appId");
}
Copy the code

Here we uncomment a line and replace Your_appId with the real appID.

static {
  //app_ids.add("Your_appId");
  app_ids.add("");
}
Copy the code

4. Calculate the token

Tips: Tokens are used only when app authentication is enabled. Here is a convenient demonstration, the author decided not to open app authentication for the time being. I just copy and post the code

Concrete implementation:

package tool;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class SignalingToken {

    public static String getToken(String appId, String certificate, String account, int expiredTsInSeconds) throws NoSuchAlgorithmException {

        StringBuilder digest_String = new StringBuilder().append(account).append(appId).append(certificate).append(expiredTsInSeconds);
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        md5.update(digest_String.toString().getBytes());
        byte[] output = md5.digest();
        String token = hexlify(output);
        String token_String = new StringBuilder().append("1").append(":").append(appId).append(":").append(expiredTsInSeconds).append(":").append(token).toString();
        return token_String;
    }

    public static String hexlify(byte[] data) {

        char[] DIGITS_LOWER = {'0'.'1'.'2'.'3'.'4'.'5'.'6'.'7'.'8'.'9'.'a'.'b'.'c'.'d'.'e'.'f'};
        char[] toDigits = DIGITS_LOWER;
        int l = data.length;
        char[] out = new char[l << 1];
        // two characters form the hex value.
        for (int i = 0, j = 0; i < l; i++) {
            out[j++] = toDigits[(0xF0 & data[i]) >>> 4];
            out[j++] = toDigits[0x0F & data[i]];
        }
        returnString.valueOf(out); }}Copy the code

For more details, please refer to the Java version of token algorithm implementation

See the authentication mechanism and algorithm for details

Run the demo

1. Before starting, it’s worth taking a look at the Mainclass directory.

There are two startup classes, one for starting the point-to-point communication server and the other for channel messages.

How do you think of it? It’s very simple, point-to-point communication, you can think of it as two people talking to each other. Channel communication is group chat (like wechat group).

└ ─ SRC └ ─ the main └ ─ Java ├ ─ mainclass │ MulteSignalObjectMain2. Java / / channel news start │ SingleSignalObjectMain. Java / / point-to-point communication │ start class Workerthread.java // Core business processesCopy the code

2. Try to communicate

A. start

Selected SingleSignalObjectMain. Java — > CTRL + shift + f10

The Run TAB already prompts you for Account, so we’ll just type Roman

You can then try to implement the user center yourself

C. Select a mode and send the MESSAGE

You’ll then see successD prompt

Here, let’s try peer-to-peer communication, type 2, press Enter

We type in the person we’re talking to, hello

Text each other

The demo may have omitted some functions, so the Java side can send point-to-point messages, but can’t receive them.

Try sending channel messages and find that the group chat channel mode is perfectly fine.

3. Summary

It’s easy to start the demo, but it takes some effort to learn how the business works.

code review (java)

The demo worked, but we didn’t quite understand what it did. If I were to write it myself, I’d still be confused. Therefore, I decided to review the code and learn the SDK usage.

Most of the dead and predefined parameter values in the file SRC \main\ Java \tool\ constant. Java are here

package tool; import java.util.ArrayList; public class Constant { public static int CURRENT_APPID = 0; public static ArrayList<String> app_ids = new ArrayList(); Public static String COMMAND_LOGOUT; // Declare some commands, usually constants. public static String COMMAND_LEAVE_CHART; public static String COMMAND_TYPE_SINGLE_POINT; public static String COMMAND_TYPE_CHANNEL; public static String RECORD_FILE_P2P; public static String RECORD_FILE_CHANEEL; public static int TIMEOUT; public static String COMMAND_CREATE_SIGNAL; public static String COMMAND_CREATE_ACCOUNT; public static String COMMAND_SINGLE_SIGNAL_OBJECT; public static String COMMAND_MULTI_SIGNAL_OBJECT; publicConstant() {} static {// app_ids is array format, meaning you can add multiple appids app_ids.add()"073e6cb4f3404d4ba9ad454c6760ec0b"); // Some command definitions // exit login COMMAND_LOGOUT ="logout"; // Leave the current chat drawing COMMAND_LEAVE_CHART ="leave"; Enter 2 COMMAND_TYPE_SINGLE_POINT = in private chat mode"2"; // Enter 3 COMMAND_TYPE_CHANNEL = for group chat mode"3"; // Cache file definition RECORD_FILE_P2P ="test_p2p.tmp";
    RECORD_FILE_CHANEEL = "test_channel.tmp"; // TIMEOUT = 20000; // Create a signal COMMAND_CREATE_SIGNAL ="0"; Create a new user COMMAND_CREATE_ACCOUNT ="1"; // Enter point-to-point mode COMMAND_SINGLE_SIGNAL_OBJECT ="0"; COMMAND_MULTI_SIGNAL_OBJECT = to enter the channel group chat mode"1"; }}Copy the code

Start the class

Take the peer-to-peer example:

// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package mainclass; import tool.Constant; Public class SingleSignalObjectMain {// Constructor publicSingleSignalObjectMainPublic static void main(String[] args) {// New workerThread, WorkerThread workerThread = new workerThread (Constant.COMMAND_SINGLE_SIGNAL_OBJECT); // Start the workerThread thread. (new Thread(workerThread)).start(); }}Copy the code

The Model directory defines some data classes and class methods that are easy to understand.

/ main/Java/mainclass WorkerThread. Java file defines a thread class, inheritance Runable.

Limited to space, here pick part of the code to interpret.

First, the WorkerThread class defines:

private boolean mainThreadStatus = false; // The main thread status is defaultfalse
private String token = "_no_need_token"; // Token authentication is disabled by default, but appID private String currentUser is used directly. // Current session user private Boolean timeOutFlag; Private DialogueStatus currentStatus; Private HashMap<String, User> users; Private HashMap<String, List<DialogueRecord>> accountDialogueRecords = null; Private HashMap<String, List<DialogueRecord>> channelDialogueRecords = null; / / channel session records List < DialogueRecord > currentAccountDialogueRecords = null; / / current account session records List < DialogueRecord > currentChannelDialogueRecords = null; // Record the current channel sessionCopy the code

Focus on the constructor

public WorkerThread(String mode) { currentMode = mode; // pass mode init(); // Initialization String appId = constant.app_ids.get (0); // Initialization String appId = constant.app_ids.get (0); // If the mode value passed in is equal to COMMAND_SINGLE_SIGNAL_OBJECT (point-to-point), update the session status to login status with appID new. // Otherwise, check whether it is channel mode and update the status. Here, you can modify the logic to suit your needs. // Why does one branch need a new Signal and the other does not?if (currentMode.equals(Constant.COMMAND_SINGLE_SIGNAL_OBJECT)) {
        sig = new Signal(appid);
        currentStatus = DialogueStatus.UNLOGIN;
    } else {
        if(currentMode.equals(Constant.COMMAND_MULTI_SIGNAL_OBJECT)) { currentStatus = DialogueStatus.SIGNALINSTANCE; }}}Copy the code

Init () function initializes data that requires interactive input to initialize

Run () function calls different business functions based on the value of currentStatus

A key step in makeSignal()

Signal signal = new Signal(appId); // Instantiate signaling with idCopy the code

JoinChannel (String channelName) uses the LoginSession class and Channel class

public void joinChannel(String channelName) { final CountDownLatch channelJoindLatch = new CountDownLatch(1); // Instantiate Channel class, Channel Channel = users.get(currentUser).getSession(). ChannelJoin (channelName, new Signal).ChannelCallback() {@override public void onChannelJoined(signal.loginSession Session, Signal.LoginSession.Channel channel) { channelJoindLatch.countDown(); } @override public void onChannelUserList(signal.loginSession session, Signal.LoginSession.Channel channel, List<String> users, List<Integer> uIDS) {Override public void onMessageChannelReceive(signal.loginSession session, Signal.LoginSession.Channel channel, String account, int uid, String msg) {if(currentChannelDialogueRecords ! = null && currentStatus == DialogueStatus.CHANNEL) { PrintToScreen.printToScreenLine(account +":"+ msg); DialogueRecord dialogueRecord = new DialogueRecord(account, msg, new Date()); currentChannelDialogueRecords.add(dialogueRecord); }} @override public void onChannelUserJoined(signal. LoginSession session, Signal.LoginSession.Channel channel, String account, int uid) {if (currentStatus == DialogueStatus.CHANNEL) {
            PrintToScreen.printToScreenLine("..." + account + " joined channel... ");
        }
    }

    @Override
    public void onChannelUserLeaved(Signal.LoginSession session, Signal.LoginSession.Channel channel, String account, int uid) {
        if (currentStatus == DialogueStatus.CHANNEL) {
            PrintToScreen.printToScreenLine("..." + account + " leave channel... ");
        }
    }

    @Override
    public void onChannelLeaved(Signal.LoginSession session, Signal.LoginSession.Channel channel, int ecode) {
        if(currentStatus == DialogueStatus.CHANNEL) { currentStatus = DialogueStatus.LOGINED; }}}); timeOutFlag =false;
  wait_time(channelJoindLatch, Constant.TIMEOUT, channelName);
  if (timeOutFlag == falseUsers.get (currentUser).setChannel(channel); }}Copy the code

There’s not enough space here to post the entire code. You can look at the API documentation to see how to authenticate, log in, and send and receive messages.

Later, I will upload the annotations to Github.

Possible problems and solutions

1. There is an extra space in build.gradle of demo

*.jar –> *.jar

2. Failed to instantiate signal

Solution: Check whether the APPID is correct and whether token authentication is enabled

If token authentication is enabled, you need to add the token calculation algorithm. For details, see this document.

3. The author found that although the default startup command value of the two startup classes is different, the startup effect is the same, and they can choose to switch to P2P or channel mode.