1
votes

I am implementing a chat application using Nodejs and socket programming. If I send a message from my Android application, it should show connected in my terminal of WebStorm, however nothing is happening.

index.js

var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
app.get('/',function(req,res){
    res.sendFile(__dirname+'/index.html');
})
io.on('connection',function(socket){
    console.log('one user connected '+socket.id);
    socket.on('message',function(data){
        var sockets = io.sockets.sockets;
        // sockets.forEach(function(sock){
        //  if(sock.id != socket.id)
        //  {
        //  sock.emit('message',data);
        //  }
        //  })
        socket.broadcast.emit('message', data);
        console.log('message '+data);
    })
    socket.on('disconnect',function(){
        console.log('one user disconnected '+socket.id);
    })
})

http.listen(3000,function(){
    console.log('server listening on port 3000');
})

index.html

 <!doctype html>
<html>
<head>
    <title>Socket.IO chat</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body { font: 13px Helvetica, Arial; }
        form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }
        form input { border: 0; padding: 10px; width: 90%; margin-right: .5%; }
        form button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; }
        #messages { list-style-type: none; margin: 0; padding: 0; }
        #messages li { padding: 5px 10px; }
        #messages li:nth-child(odd) { background: #eee; }
    </style>
</head>
<body>
<ul id="messages"></ul>
<form action="">
    <input id="m" autocomplete="off" /><button>Send</button>
</form>
<script src="/socket.io/socket.io.js"></script>
<script>
    var socket = io();
</script>
</body>
<h1>Hello Sockets</h1>
</html>

ChatFragment.java

package com.example.admin.chatapp;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Base64;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.ImageButton;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import org.json.JSONException;
import org.json.JSONObject;

import io.socket.client.IO;
import io.socket.client.Socket;
import io.socket.emitter.Emitter;


/**
 * A simple {@link Fragment} subclass.
 * Activities that contain this fragment must implement the
 * {@link ChatFragment.OnFragmentInteractionListener} interface
 * to handle interaction events.
 * Use the {@link ChatFragment#newInstance} factory method to
 * create an instance of this fragment.
 */
public class ChatFragment extends Fragment {
    // TODO: Rename parameter arguments, choose names that match
    // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
    private static final String ARG_PARAM1 = "param1";
    private static final String ARG_PARAM2 = "param2";

    // TODO: Rename and change types of parameters
    private String mParam1;
    private String mParam2;
    private EditText mInputMessageView;
    private RecyclerView mMessagesView;
    private OnFragmentInteractionListener mListener;
    private List<Message> mMessages = new ArrayList<Message>();
    private RecyclerView.Adapter mAdapter;

    private Socket socket;
    {
        try{
            socket = IO.socket("http://192.168.x.x:3000");
        }catch(URISyntaxException e){
            throw new RuntimeException(e);
        }
    }
    /**
     * Use this factory method to create a new instance of
     * this fragment using the provided parameters.
     *
     * @param param1 Parameter 1.
     * @param param2 Parameter 2.
     * @return A new instance of fragment ChatFragment.
     */
    // TODO: Rename and change types and number of parameters
    public static ChatFragment newInstance(String param1, String param2) {
        ChatFragment fragment = new ChatFragment();
        Bundle args = new Bundle();
        args.putString(ARG_PARAM1, param1);
        args.putString(ARG_PARAM2, param2);
        fragment.setArguments(args);
        return fragment;
    }

    public ChatFragment() {
        // Required empty public constructor
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
        socket.connect();
        socket.on("message", handleIncomingMessages);
    }



    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_chat, container, false);
    }

    // TODO: Rename method, update argument and hook method into UI event
    public void onButtonPressed(Uri uri) {
        if (mListener != null) {
            mListener.onFragmentInteraction(uri);
        }
    }

    @Override
    public void onAttach(Context activity) {
        super.onAttach(activity);
        mAdapter = new MessageAdapter(mMessages);
        /*try {
            mListener = (OnFragmentInteractionListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString()
                    + " must implement OnFragmentInteractionListener");
        }*/

    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        mMessagesView = (RecyclerView) view.findViewById(R.id.messages);
        mMessagesView.setLayoutManager(new LinearLayoutManager(getActivity()));
        mMessagesView.setAdapter(mAdapter);

        ImageButton sendButton = (ImageButton) view.findViewById(R.id.send_button);
        mInputMessageView = (EditText) view.findViewById(R.id.message_input);

        sendButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                sendMessage();
            }
        });


    }

    private void sendMessage(){
        String message = mInputMessageView.getText().toString().trim();
        mInputMessageView.setText("");
        addMessage(message);
        JSONObject sendText = new JSONObject();
        try{
            sendText.put("text",message);
            socket.emit("message", sendText);
        }catch(JSONException e){

        }

    }

    public void sendImage(String path)
    {
        JSONObject sendData = new JSONObject();
        try{
            sendData.put("image", encodeImage(path));
            Bitmap bmp = decodeImage(sendData.getString("image"));
            addImage(bmp);
            socket.emit("message",sendData);
        }catch(JSONException e){

        }
    }

    private void addMessage(String message) {

        mMessages.add(new Message.Builder(Message.TYPE_MESSAGE)
                .message(message).build());
        // mAdapter = new MessageAdapter(mMessages);
        mAdapter = new MessageAdapter( mMessages);
        mAdapter.notifyItemInserted(0);
        scrollToBottom();
    }

    private void addImage(Bitmap bmp){
        mMessages.add(new Message.Builder(Message.TYPE_MESSAGE)
                .image(bmp).build());
        mAdapter = new MessageAdapter(mMessages);
        mAdapter.notifyItemInserted(0);
        scrollToBottom();
    }
    private void scrollToBottom() {
        mMessagesView.scrollToPosition(mAdapter.getItemCount() - 1);
    }

    private String encodeImage(String path)
    {
        File imagefile = new File(path);
        FileInputStream fis = null;
        try{
            fis = new FileInputStream(imagefile);
        }catch(FileNotFoundException e){
            e.printStackTrace();
        }
        Bitmap bm = BitmapFactory.decodeStream(fis);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        bm.compress(Bitmap.CompressFormat.JPEG,100,baos);
        byte[] b = baos.toByteArray();
        String encImage = Base64.encodeToString(b, Base64.DEFAULT);
        //Base64.de
        return encImage;

    }

    private Bitmap decodeImage(String data)
    {
        byte[] b = Base64.decode(data,Base64.DEFAULT);
        Bitmap bmp = BitmapFactory.decodeByteArray(b,0,b.length);
        return bmp;
    }
    private Emitter.Listener handleIncomingMessages = new Emitter.Listener(){
        @Override
        public void call(final Object... args){
            getActivity().runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Log.d("cdcdc", String.valueOf(args[0]));
                    JSONObject data = (JSONObject) args[0];
                    Log.d("cdcdc", String.valueOf(data));
                    String message;
                    String imageText;
                    try {
                        message = data.getString("text").toString();
                        addMessage(message);

                    } catch (JSONException e) {
                        // return;
                    }
                    try {
                        imageText = data.getString("image");
                        addImage(decodeImage(imageText));
                    } catch (JSONException e) {
                        //retur
                    }

                }
            });
        }
    };

    @Override
    public void onDetach() {
        super.onDetach();
        mListener = null;
    }

    /**
     * This interface must be implemented by activities that contain this
     * fragment to allow an interaction in this fragment to be communicated
     * to the activity and potentially other fragments contained in that
     * activity.
     * <p/>
     * See the Android Training lesson <a href=
     * "http://developer.android.com/training/basics/fragments/communicating.html"
     * >Communicating with Other Fragments</a> for more information.
     */
    public interface OnFragmentInteractionListener {
        // TODO: Update argument type and name
        public void onFragmentInteraction(Uri uri);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        socket.disconnect();
    }

}

To run the index.js file in WebStorm, I type the command

node index.js

which gives me the output as

server listening on port 3000

and when I send a message from my app, nothing happens. The terminal stays as it is. What should I do, and what is causing this.

Update I changed my android code to

socket = IO.socket("http://01e42de0.ngrok.io");

Using ngrok gives me an error

06-05 20:27:52.578 23204-23204/com.example.admin.chatapp E/AndroidRuntime: FATAL EXCEPTION: main

Process: com.example.admin.chatapp, PID: 23204 java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.admin.chatapp/com.example.admin.chatapp.SocketActivity}: android.view.InflateException: Binary XML file line #9: Binary XML file line #9: Error inflating class fragment at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2728) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2814) at android.app.ActivityThread.-wrap12(ActivityThread.java) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1527) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:154) at android.app.ActivityThread.main(ActivityThread.java:6290) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776) Caused by: android.view.InflateException: Binary XML file line #9: Binary XML file line #9: Error inflating class fragment Caused by: android.view.InflateException: Binary XML file line #9: Error inflating class fragment Caused by: java.lang.RuntimeException: java.net.URISyntaxException: Illegal character in scheme name at index 0: http://01e42de0.ngrok.io at com.example.admin.chatapp.ChatFragment.(ChatFragment.java:64) at java.lang.Class.newInstance(Native Method) at android.app.Fragment.instantiate(Fragment.java:622) at android.app.Fragment.instantiate(Fragment.java:593) at android.app.FragmentManagerImpl.onCreateView(FragmentManager.java:2302) at android.app.FragmentController.onCreateView(FragmentController.java:98) at android.app.Activity.onCreateView(Activity.java:5982) at android.support.v4.app.BaseFragmentActivityHoneycomb.onCreateView(BaseFragmentActivityHoneycomb.java:36) at android.support.v4.app.FragmentActivity.onCreateView(FragmentActivity.java:79) at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:777) at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:727) at android.view.LayoutInflater.rInflate(LayoutInflater.java:858) at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:821) at android.view.LayoutInflater.inflate(LayoutInflater.java:518) at android.view.LayoutInflater.inflate(LayoutInflater.java:426) at android.view.LayoutInflater.inflate(LayoutInflater.java:377) at android.support.v7.app.AppCompatDelegateImplV9.setContentView(AppCompatDelegateImplV9.java:292) at android.support.v7.app.AppCompatActivity.setContentView(AppCompatActivity.java:140) at com.example.admin.chatapp.SocketActivity.onCreate(SocketActivity.java:22) at android.app.Activity.performCreate(Activity.java:6760) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1134) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2681) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2814) at android.app.ActivityThread.-wrap12(ActivityThread.java) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1527) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:154) at android.app.ActivityThread.main(ActivityThread.java:6290) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776) Caused by: java.net.URISyntaxException: Illegal character in scheme name at index 0: http://01e42de0.ngrok.io at java.net.URI$Parser.fail(URI.java:2856) at java.net.URI$Parser.checkChars(URI.java:3029) at java.net.URI$Parser.checkChar(URI.java:3039) at java.net.URI$Parser.parse(URI.java:3055) at java.net.URI.(URI.java:590) at io.socket.client.IO.socket(IO.java:41) at io.socket.client.IO.socket(IO.java:37) at com.example.admin.chatapp.ChatFragment.(ChatFragment.java:61) at java.lang.Class.newInstance(Native Method)  at android.app.Fragment.instantiate(Fragment.java:622)  at android.app.Fragment.instantiate(Fragment.java:593)  at android.app.FragmentManagerImpl.onCreateView(FragmentManager.java:2302)  at android.app.FragmentController.onCreateView(FragmentController.java:98)  at android.app.Activity.onCreateView(Activity.java:5982)  at android.support.v4.app.BaseFragmentActivityHoneycomb.onCreateView(BaseFragmentActivityHoneycomb.java:36)  at android.support.v4.app.FragmentActivity.onCreateView(FragmentActivity.java:79)  at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:777)  at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:727)  at android.view.LayoutInflater.rInflate(LayoutInflater.java:858)  at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:821)  at android.view.LayoutInflater.inflate(LayoutInflater.java:518)  at android.view.LayoutInflater.inflate(LayoutInflater.java:426)  at android.view.LayoutInflater.inflate(LayoutInflater.java:377)  at android.support.v7.app.AppCompatDelegateImplV9.setContentView(AppCompatDelegateImplV9.java:292)  at android.support.v7.app.AppCompatActivity.setContentView(AppCompatActivity.java:140)  at com.example.admin.chatapp.SocketActivity.onCreate(SocketActivity.java:22)  at android.app.Activity.performCreate(Activity.java:6760)  at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1134)  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2681)  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2814)  at android.app.ActivityThread.-wrap12(ActivityThread.java)  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1527)  at android.os.Handler.dispatchMessage(Handler.java:102)  at android.os.Looper.loop(Looper.java:154)  at android.app.ActivityThread.main(ActivityThread.java:6290)  at java.lang.reflect.Method.invoke(Native Method)  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)

1
Is the URL http://192.168.1.9:3000 visible by your Android device? - Marcos Placona
@MarcosPlacona how do I do that ? - rut_0_1
Go into the network / wifi settings of your android device and see what your IP address is - OneCricketeer
@cricket_007 my IP address is 192.168.1.23 - rut_0_1

1 Answers

0
votes

by asking you questions on your own question, I think I know what your problem is. Seems like your android device may not be on the same network as your application, and because you're running this on 192.168.x.x, I'm pretty sure this is what your problem is.

Your Android device just can't see your socket application as it's just running in localhost.

To make it accessible to your Android device, you can use a tunnelling application like ngrok. This will make your application available. Get it installed on your computer and run it in a new terminal window as follows:

ngrok http 3000

On that terminal window you will then be given a UR: that is accessible from outside. Copy that URL.

Sample here as image uploads seem broken right now.

Change this line on your code:

socket = IO.socket("http://192.168.1.9:3000");

to your ngrok URL. I.e.

socket = IO.socket("http://fa9910Hf.ngrok.io");

Try again and hopefully you should see the messages coming in.