android 使用websocket 实现简单的聊天。

websocket:可谓是web端的TCP协议,具体详细介绍,参见知乎: https://www.zhihu.com/question/20215561

websocket是一种全新的协议,不属于http无状态协议,协议名为"ws",这意味着一个websocket连接地址会是这样的写法:ws://**,eg: ws://192.168.8.132:2444 . IP +端口 访问.

Look! demo如下: 在MyEclipse 使用Tomcat7 + JDK 简单构建服务端。

android 使用websocket 实现简单的聊天。

不考虑前端 ,没有JSP代码,没有其他无关web.xml配置.


在MyEclipse 里面启动运行TomCat 服务器,部署项目。

eg:WebSocket2右键Run As JavaApplication 运行 websocket 工具类。

android 使用websocket 实现简单的聊天。

OK!启动运行成功。

打开android studio运行demo。

android 使用websocket 实现简单的聊天。


java文件代码:

package com.mysocket;


import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.Collection;


import org.java_websocket.WebSocket;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.server.WebSocketServer;


public class SocketServer extends WebSocketServer {



private static final int PORT = 2444;


    public static void main(String[] args) {
        SocketServer server = new SocketServer(PORT);
        server.start();


        try {
            String ip = InetAddress.getLocalHost().getHostAddress();
            System.out.println("获取的IP----》"+ip);
            int port = server.getPort();
            print(String.format("服务已启动: %s:%d", ip, port));
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }


        InputStreamReader in = new InputStreamReader(System.in);
        BufferedReader reader = new BufferedReader(in);


        while (true) {
            try {
                String msg = reader.readLine();
                server.broadcastMessage(msg);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    public SocketServer(int port) {
        super(new InetSocketAddress(port));
    }


    public SocketServer(InetSocketAddress address) {
        super(address);
    }


    @Override
    public void onOpen(WebSocket webSocket, ClientHandshake clientHandshake) {
        String address = webSocket.getRemoteSocketAddress().getAddress().getHostAddress();
        String message = String.format("(%s) <加入>", address);
        broadcastMessage(message);
        print(message);
    }


    @Override
    public void onClose(WebSocket webSocket, int code, String reason, boolean remote) {
        String address = webSocket.getRemoteSocketAddress().getAddress().getHostAddress();
        String message = String.format("(%s) <离开>", address);
        broadcastMessage(message);
        print(message);
    }


    @Override
    public void onMessage(WebSocket webSocket, String msg) {
        String address = webSocket.getRemoteSocketAddress().getAddress().getHostAddress();
        String message = String.format("(%s) %s", address, msg);
        broadcastMessage(message);
        print(message);
    }


    @Override
    public void onError(WebSocket webSocket, Exception e) {
        if (null != webSocket) {
            if (!webSocket.isClosed()) {
                webSocket.close(0);
            }
        }
        e.printStackTrace();
    }


    /**
     * 广播收到消息
     *
     * @param msg
     */
    private void broadcastMessage(String msg) {
        Collection<WebSocket> connections = connections();
        synchronized (connections) {
            for (WebSocket client : connections) {
                client.send(msg);
            }
        }
    }


    private static void print(String msg) {
        System.out.println(String.format("[%d] %s", System.currentTimeMillis(), msg));
    }




}


Android 客户端Demo如下:


package com.websocket2;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RadioGroup;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;

import org.java_websocket.WebSocket;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft;
import org.java_websocket.drafts.Draft_10;
import org.java_websocket.drafts.Draft_17;
import org.java_websocket.drafts.Draft_75;
import org.java_websocket.drafts.Draft_76;
import org.java_websocket.exceptions.InvalidDataException;
import org.java_websocket.framing.Framedata;
import org.java_websocket.framing.FramedataImpl1;
import org.java_websocket.handshake.ServerHandshake;

import java.net.URI;
import java.net.URISyntaxException;
import java.nio.ByteBuffer;
import java.nio.channels.NotYetConnectedException;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;

public class MainActivity extends AppCompatActivity {

    private static final int STATUS_CLOSE = 0;
    private static final int STATUS_CONNECT = 1;
    private static final int STATUS_MESSAGE = 2;

    @BindView(R.id.etIP)
    EditText etIP;
    @BindView(R.id.etPort)
    EditText etPort;
    @BindView(R.id.tvStatus)
    TextView tvStatus;
    @BindView(R.id.tvMsg)
    TextView tvMsg;
    @BindView(R.id.rgVersion)
    RadioGroup rgVersion;
    @BindView(R.id.etMessage)
    EditText etMessage;
    @BindView(R.id.svContent)
    ScrollView svContent;
    @BindView(R.id.viewMain)
    View viewMain;

    @BindView(R.id.btConnect)
    Button btConnect;
    @BindView(R.id.btDisconnect)
    Button btDisconnect;
    @BindView(R.id.btSend)
    Button btSend;

    @OnClick({R.id.btConnect, R.id.btDisconnect, R.id.btSend, R.id.btPing})
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btConnect:
                connectToServer();
                break;
            case R.id.btDisconnect:
                if (null != mClient) {
                    mClient.close();
                }
                break;
            case R.id.btSend:
                if (null != mClient) {
                    String msg = etMessage.getText().toString();
                    if (!TextUtils.isEmpty(msg)) {
                        try {
                            mClient.send(msg);
                        } catch (NotYetConnectedException e) {
                            e.printStackTrace();
                            return;
                        }
                        // 发送完成之后 清除输入框里面的内容
                        etMessage.setText("");
                    }
                }
                break;
            case R.id.btPing:
                ByteBuffer buffer = ByteBuffer.wrap("Hello".getBytes());
                FramedataImpl1 resp = new FramedataImpl1(Framedata.Opcode.PING);
                resp.setFin(true);
                try {
                    resp.setPayload(buffer);
                } catch (InvalidDataException e) {
                    e.printStackTrace();
                }
                mClient.getConnection().sendFrame(resp);
                break;

            default:
                break;
        }
    }

    private Client mClient;
    private Handler mHandle = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            String message = String.format("[%d] %s\n", System.currentTimeMillis(), msg.obj.toString());
            tvMsg.append(message);

            switch (msg.what) {
                case STATUS_CONNECT:
                    btConnect.setEnabled(false);
                    btDisconnect.setEnabled(true);
                    btSend.setEnabled(true);
                    break;
                case STATUS_CLOSE:
                    btConnect.setEnabled(true);
                    btDisconnect.setEnabled(false);
                    btSend.setEnabled(false);
                    break;
                case STATUS_MESSAGE:
                    // TODO: 16/8/24
                    break;
                default:
                    break;
            }
            svContent.postDelayed(new Runnable() {
                @Override
                public void run() {
                    svContent.fullScroll(View.FOCUS_DOWN);
                }
            }, 100);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);


        System.setProperty("java.net.preferIPv6Addresses", "false");
        System.setProperty("java.net.preferIPv4Stack", "true");
    }

    private void connectToServer() {
        String ip = etIP.getText().toString();
        String port = etPort.getText().toString();
        if (TextUtils.isEmpty(ip) || TextUtils.isEmpty(port)) {
            Toast.makeText(viewMain.getContext(),"IP and Port 不能为空",Toast.LENGTH_LONG).show();
            return;
        }
        String address = String.format("ws://%s:%s", ip, port);// 格式化 ws://192.168.8.132:2444 拼接socket 访问路径。
        Draft draft = null;
        switch (rgVersion.getCheckedRadioButtonId()) {
            case R.id.rbDraft10:
                draft = new Draft_10();
                break;
            case R.id.rbDraft17:
                draft = new Draft_17();
                break;
            case R.id.rbDraft75:
                draft = new Draft_75();
                break;
            case R.id.rbDraft76:
                draft = new Draft_76();
                break;

            default:
                draft = new Draft_17();
                break;
        }
        try {
            URI uri = new URI(address);
            mClient = new Client(uri, draft);
            mClient.connect();
        } catch (URISyntaxException e) {
            e.printStackTrace();
            return;
        }

        tvStatus.setText(address);
    }

    private class Client extends WebSocketClient {

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

        public Client(URI serverUri, Draft draft) {
            super(serverUri, draft);
        }

        @Override
        public void onOpen(ServerHandshake handShakeData) {
            Message msg = new Message();
            msg.what = STATUS_CONNECT;
            msg.obj = String.format("[Welcome:%s]", getURI());
            mHandle.sendMessage(msg);
        }

        @Override
        public void onMessage(String message) {
            Message msg = new Message();
            msg.what = STATUS_MESSAGE;
            msg.obj = message;
            mHandle.sendMessage(msg);
        }

        @Override
        public void onClose(int code, String reason, boolean remote) {
            Message msg = new Message();
            msg.what = STATUS_CLOSE;
            msg.obj = String.format("[Bye:%s]", getURI());
            mHandle.sendMessage(msg);
        }

        @Override
        public void onWebsocketPong(WebSocket conn, Framedata f) {
            super.onWebsocketPong(conn, f);

            String value = parseFramedata(f);

            Message msg = new Message();
            msg.what = STATUS_MESSAGE;
            msg.obj = "pong:" + value;
            mHandle.sendMessage(msg);
        }

        @Override
        public void onWebsocketPing(WebSocket conn, Framedata f) {
            super.onWebsocketPing(conn, f);

            String value = parseFramedata(f);

            Message msg = new Message();
            msg.what = STATUS_MESSAGE;
            msg.obj = "ping:" + value;
            mHandle.sendMessage(msg);
        }

        @Override
        public void onError(Exception ex) {
            ex.printStackTrace();
        }

        public String parseFramedata(Framedata framedata){
            String result = "null";
            ByteBuffer buffer = framedata.getPayloadData();
            if(null == buffer){
                return result;
            }
            byte[] data = buffer.array();
            if(null != data && data.length > 0){
                return new String(data);
            }
            return result;
        }
    }
}


layout xml文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/viewMain"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingLeft="20dp"
    android:paddingRight="20dp">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="IP:" />

        <EditText
            android:id="@+id/etIP"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:maxLength="15"
            android:maxLines="1"
            android:text="192.168.8.132" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20dp"
            android:text="Port:" />

        <EditText
            android:id="@+id/etPort"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:maxLength="5"
            android:maxLines="1"
            android:text="2444" />
    </LinearLayout>


    <RadioGroup
        android:id="@+id/rgVersion"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:orientation="horizontal">

        <RadioButton
            android:id="@+id/rbDraft10"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Draft_10" />

        <RadioButton
            android:id="@+id/rbDraft17"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:checked="true"
            android:text="Draft_17" />

        <RadioButton
            android:id="@+id/rbDraft75"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Draft_75" />

        <RadioButton
            android:id="@+id/rbDraft76"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Draft_76" />
    </RadioGroup>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="right"
        android:orientation="horizontal">

        <Button
            android:id="@+id/btConnect"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:enabled="true"
            android:text="连接" />

        <Button
            android:id="@+id/btDisconnect"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:enabled="false"
            android:text="断开" />

        <Button
            android:id="@+id/btPing"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Ping" />
    </LinearLayout>

    <TextView
        android:id="@+id/tvStatus"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="right"
        tools:text="ws://192.168.1.40:2333 Draft_17" />

    <ScrollView
        android:id="@+id/svContent"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">

        <TextView
            android:id="@+id/tvMsg"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </ScrollView>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="right"
        android:orientation="horizontal">

        <EditText
            android:id="@+id/etMessage"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1" />

        <Button
            android:id="@+id/btSend"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:enabled="false"
            android:text="Send" />
    </LinearLayout>
</LinearLayout>


mainfest.xml:

简单的添加两个先关权限即可:

  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

简易的websocket 搭建成功,如果还需web页面聊天室,在去编写前端JSP页面。也是相同的建立websocket连接

,发送消息。