Search code examples
androidwebsocketnetwork-programmingjava-websocket

What is a simple way to implement a websocket client in Android? Is the following example correct?


I was trying to use a class that implements a WebSocket client in Android. But I get the following error:

12-07 11:22:46.286 31579-31579/com.domain.wsocketchat W/System: ClassLoader referenced unknown path: /data/app/com.domain.wsocketchat-2/lib/arm
12-07 11:22:46.392 31579-31579/com.domain.wsocketchat W/System.err: an error occurred:java.net.SocketException: socket failed: EACCES (Permission denied)

The class that I'm using follows:

import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft;
import org.java_websocket.drafts.Draft_10;
import org.java_websocket.handshake.ServerHandshake;

import java.net.URI;

public class EmptyClient extends WebSocketClient {
    public EmptyClient(URI serverUri, Draft draft) {
        super(serverUri, draft);
    }

    public EmptyClient(URI serverURI) {
        super(serverURI);
    }

    @Override
    public void onOpen(ServerHandshake handshakedata) {
        System.out.println("new connection opened");
    }

    @Override
    public void onClose(int code, String reason, boolean remote) {
        System.out.println("closed with exit code " + code + " additional info: " + reason);
    }

    @Override
    public void onMessage(String message) {
        System.out.println("received message: " + message);
    }

    @Override
    public void onError(Exception ex) {
        System.err.println("an error occurred:" + ex);
    }
}

The class is called within the onCreate():

WebSocketClient client = null;
try {
    client = new EmptyClient(new URI("ws://192.168.1.135:9000/server.php"), new Draft_10());
} catch (URISyntaxException e) {
    e.printStackTrace();
}
client.connect();

AndroidManifest:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

Sdk: minSdkVersion 16

Anyone have any idea, what could be the problem? It seems everything allright! Thank you in advance.


Solution

  • I already resolved the problem by using another class, that handles the websocket client:

        import android.app.Activity;
        import android.app.Fragment;
        import android.os.Build;
        import android.os.Bundle;
        import android.util.Log;
        import android.view.LayoutInflater;
        import android.view.Menu;
        import android.view.MenuItem;
        import android.view.View;
        import android.view.ViewGroup;
        import android.widget.EditText;
        import android.widget.TextView;
    
        import org.java_websocket.client.WebSocketClient;
        import org.java_websocket.handshake.ServerHandshake;
    
        import java.net.URI;
        import java.net.URISyntaxException;
    
        public class MainActivity extends Activity {
            private WebSocketClient mWebSocketClient;
    
            @Override
            protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_main);
    
                connectWebSocket();
    
                if (savedInstanceState == null) {
                    getFragmentManager().beginTransaction()
                            .add(R.id.container, new PlaceholderFragment())
                            .commit();
                }
            }
    
    
            @Override
            public boolean onCreateOptionsMenu(Menu menu) {
    
                // Inflate the menu; this adds items to the action bar if it is present.
                getMenuInflater().inflate(R.menu.main, menu);
                return true;
            }
    
            @Override
            public boolean onOptionsItemSelected(MenuItem item) {
                // Handle action bar item clicks here. The action bar will
                // automatically handle clicks on the Home/Up button, so long
                // as you specify a parent activity in AndroidManifest.xml.
                switch (item.getItemId()) {
                    case R.id.action_settings:
                        return true;
                }
                return super.onOptionsItemSelected(item);
            }
    
            /**
             * A placeholder fragment containing a simple view.
             */
            public static class PlaceholderFragment extends Fragment {
    
                public PlaceholderFragment() {
                }
    
                @Override
                public View onCreateView(LayoutInflater inflater, ViewGroup container,
                        Bundle savedInstanceState) {
                    View rootView = inflater.inflate(R.layout.fragment_main, container, false);
                    return rootView;
                }
            }
    
            private void connectWebSocket() {
                URI uri;
                try {
                    uri = new URI("ws://192.168.1.135:9000/");
                } catch (URISyntaxException e) {
                    e.printStackTrace();
                    return;
                }
    
                mWebSocketClient = new WebSocketClient(uri) {
                    @Override
                    public void onOpen(ServerHandshake serverHandshake) {
                        Log.i("Websocket", "Opened");
                        mWebSocketClient.send("Hello from " + Build.MANUFACTURER + " " + Build.MODEL);
                    }
    
                    @Override
                    public void onMessage(String s) {
                        final String message = s;
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                TextView textView = (TextView)findViewById(R.id.messages);
                                textView.setText(textView.getText() + "\n" + message);
                            }
                        });
                    }
    
                    @Override
                    public void onClose(int i, String s, boolean b) {
                        Log.i("Websocket", "Closed " + s);
                    }
    
                    @Override
                    public void onError(Exception e) {
                        Log.i("Websocket", "Error " + e.getMessage());
                    }
                };
                mWebSocketClient.connect();
            }
    
            public void sendMessage(View view) {
                EditText editText = (EditText)findViewById(R.id.message);
                mWebSocketClient.send(editText.getText().toString());
                editText.setText("");
            }
        }
    

    For anyone that is interested, I've used a Websocket-ChatServer written in PHP:

    <?php
    $r = $_SERVER['SERVER_ADDR'];
    if($r=="::1") $r="localhost";
    
    $host = $r; //'localhost'; //host
    $port = '9000'; //port
    $null = NULL; //null var
    
    //Create TCP/IP sream socket
    $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
    //reuseable port
    socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);
    
    //bind socket to specified host
    socket_bind($socket, 0, $port);
    
    //listen to port
    socket_listen($socket);
    
    //create & add listning socket to the list
    $clients = array($socket);
    
    //start endless loop, so that our script doesn't stop
    while (true) {
        //manage multipal connections
        $changed = $clients;
        //returns the socket resources in $changed array
        socket_select($changed, $null, $null, 0, 10);
    
        //check for new socket
        if (in_array($socket, $changed)) {
            $socket_new = socket_accept($socket); //accpet new socket
            $clients[] = $socket_new; //add socket to client array
    
            $header = socket_read($socket_new, 1024); //read data sent by the socket
            perform_handshaking($header, $socket_new, $host, $port); //perform websocket handshake
    
            socket_getpeername($socket_new, $ip); //get ip address of connected socket
            $response = mask(json_encode(array('type'=>'system', 'message'=>$ip.' connected'))); //prepare json data
            send_message($response); //notify all users about new connection
    
            //make room for new socket
            $found_socket = array_search($socket, $changed);
            unset($changed[$found_socket]);
        }
    
        //loop through all connected sockets
        foreach ($changed as $changed_socket) { 
    
            //check for any incomming data
            while(socket_recv($changed_socket, $buf, 1024, 0) >= 1)
            {
                $received_text = unmask($buf); //unmask data
                $tst_msg = json_decode($received_text); //json decode 
                $user_name = $tst_msg->name; //sender name
                $user_message = $tst_msg->message; //message text
                $user_color = $tst_msg->color; //color
    
                //prepare data to be sent to client
                $response_text = mask(json_encode(array('type'=>'usermsg', 'name'=>$user_name, 'message'=>$user_message, 'color'=>$user_color)));
                send_message($response_text); //send data
                break 2; //exist this loop
            }
    
            $buf = @socket_read($changed_socket, 1024, PHP_NORMAL_READ);
            if ($buf === false) { // check disconnected client
                // remove client for $clients array
                $found_socket = array_search($changed_socket, $clients);
                socket_getpeername($changed_socket, $ip);
                unset($clients[$found_socket]);
    
                //notify all users about disconnected connection
                $response = mask(json_encode(array('type'=>'system', 'message'=>$ip.' disconnected')));
                send_message($response);
            }
        }
    }
    // close the listening socket
    socket_close($sock);
    
    function send_message($msg)
    {
        global $clients;
        foreach($clients as $changed_socket)
        {
            @socket_write($changed_socket,$msg,strlen($msg));
        }
        return true;
    }
    
    
    //Unmask incoming framed message
    function unmask($text) {
        $length = ord($text[1]) & 127;
        if($length == 126) {
            $masks = substr($text, 4, 4);
            $data = substr($text, 8);
        }
        elseif($length == 127) {
            $masks = substr($text, 10, 4);
            $data = substr($text, 14);
        }
        else {
            $masks = substr($text, 2, 4);
            $data = substr($text, 6);
        }
        $text = "";
        for ($i = 0; $i < strlen($data); ++$i) {
            $text .= $data[$i] ^ $masks[$i%4];
        }
        return $text;
    }
    
    //Encode message for transfer to client.
    function mask($text)
    {
        $b1 = 0x80 | (0x1 & 0x0f);
        $length = strlen($text);
    
        if($length <= 125)
            $header = pack('CC', $b1, $length);
        elseif($length > 125 && $length < 65536)
            $header = pack('CCn', $b1, 126, $length);
        elseif($length >= 65536)
            $header = pack('CCNN', $b1, 127, $length);
        return $header.$text;
    }
    
    //handshake new client.
    function perform_handshaking($receved_header,$client_conn, $host, $port)
    {
        $headers = array();
        $lines = preg_split("/\r\n/", $receved_header);
        foreach($lines as $line)
        {
            $line = chop($line);
            if(preg_match('/\A(\S+): (.*)\z/', $line, $matches))
            {
                $headers[$matches[1]] = $matches[2];
            }
        }
    
        $secKey = $headers['Sec-WebSocket-Key'];
        $secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
        //hand shaking header
        $upgrade  = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
        "Upgrade: websocket\r\n" .
        "Connection: Upgrade\r\n" .
        "WebSocket-Origin: $host\r\n" .
        "WebSocket-Location: ws://$host:$port/demo/shout.php\r\n".
        "Sec-WebSocket-Accept:$secAccept\r\n\r\n";
        socket_write($client_conn,$upgrade,strlen($upgrade));
    }
    

    In order to start the websocket server it's needed to run a console command like this: php -q myserver.php