IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 安卓Java Web Socket简单使用 -> 正文阅读

[网络协议]安卓Java Web Socket简单使用

安卓Java Web Socket简单使用

android使用javaWebSocket实现跨设备设备通讯的方式之一。

demo地址:https://gitee.com/sixu_Java/java-web-socket

只要关注onOpenonMessageonCloseonError这四个方法,然后server端启动,client端通过server端ip与端口去连接。即可完成简单的连接demo。

而server端与client端的通讯关注onMessage

注意:操作界面需要在UI线程,如将接收到的message更新到页面上,可借助Handler。如果直接在onMessage直接更新页面,则不会走onMessage,会走onError

断线重连必定会走onClose方法,所以根据client的onClose方法断线的原因,调用reconnect或者reconnectBlocking即可完成重连动作。

准备

  1. 首先需要创建两个项目,作为通讯的serve端与client端。

  2. 引入Java Web Socket依赖

    implementation "org.java-websocket:Java-WebSocket:1.4.0"
    

    Java-WebSocket的github地址https://github.com/TooTallNate/Java-WebSocket

  3. 给client端和serve端在AndroidManifest.xml添加联网权限

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

Client端

继承WebSocketClient

新建类继承WebSocketClient,并实现4个方法。我们所有关于通讯的操作基本上都围绕着这个类。

    @Override
    public void onOpen(ServerHandshake handshakedata) {
    }

    @Override
    public void onMessage(String message) {
    }

    @Override
    public void onMessage(ByteBuffer bytes) {
    }

    @Override
    public void onClose(int code, String reason, boolean remote) {
    }

    @Override
    public void onError(Exception ex) {
    }
  1. onOpen:当连接上serve端时调用。

  2. onMessage:接收到消息时调用。

  3. onClose:连接断开时调用。code表示断开的原因,remote表示断开是否是由于serve的原因。

    code可在org.java_websocket.framing.CloseFrame中查看类型,根据code可以决定是否需要进行重连。

    如code=1,则表示client与server从来没有连接过,则不需要重连。

    code = 1000(Normal),表示是正常断开,一般情况下已经完成了要做的事情,用户手动断开,无需重连。

    code= 1001 (GOING_AWAY),服务器可能由于自身原因导致断开,可重连。

  4. onError:连接出错时调用,之后会调用onClose。

代码:

public class JWSClient extends WebSocketClient {
    private static final String TAG = "JWSClient";
    private JWSCallBack mCallBack;

    public JWSClient(URI serverUri, JWSCallBack callBack) {
        this(serverUri);
        mCallBack = callBack;
    }

    private JWSClient(URI serverUri) {
        super(serverUri);
    }

    @Override
    public void onOpen(ServerHandshake handshakedata) {
        Log.d(TAG, "onOpen: ");
        mCallBack.onOpen(handshakedata);
    }

    @Override
    public void onMessage(String message) {
        Log.d(TAG, "onMessage: ");
        mCallBack.onMessage(message);
    }

    @Override
    public void onMessage(ByteBuffer bytes) {
        super.onMessage(bytes);
        Log.d(TAG, "onMessage: ");
        mCallBack.onMessage(bytes);
    }

    @Override
    public void onClose(int code, String reason, boolean remote) {
        Log.d(TAG, "onClose: code = " + code + ", reason = " + reason + ", remote = " + remote);
        if (code==-1){
            return;
        }
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    reconnectBlocking();
                } catch (InterruptedException exception) {
                    exception.printStackTrace();
                }
            }
        }).start();
    }

    @Override
    public void onError(Exception ex) {
        Log.d(TAG, "onError: " + ex.getMessage());
    }

    interface JWSCallBack {
        void onOpen(ServerHandshake handshakedata);

        void onMessage(String message);

        void onMessage(ByteBuffer bytes);
    }

}

连接

ws://ip地址:端口生成一个WebSocketClient对象,然后调用connect方法,即尝试连接serve。

ip地址表示serve的ip地址,端口需要与serve约定,保持一致。

以ws开头表示未加密,wws开头表示加密。

connect方法不会阻塞,connectBlocking会阻塞当前线程。

连接成功后,调用WebSocketClient的send方法,即可向serve发送数据。

send的参数是byte[]类型,则serve的onMessage收到的也是byte[]。

send的参数是string类型,则serve的onMessage收到的也是string。

代码

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";

    private TextView mTextViewReceive;
    private EditText mEditTextIP;
    private EditText mEditTextSend;
    private Button mButtonConnect;
    private Button mButtonSend;

    private JWSClient mJWSClient;
    private JWSClient.JWSCallBack mJWSCallBack = new JWSClient.JWSCallBack() {
        @Override
        public void onOpen(ServerHandshake handshakedata) {

        }

        @Override
        public void onMessage(String message) {
            Message msg = Message.obtain();
            msg.what = 0;
            msg.obj = message;
            mUIHandler.sendMessage(msg);
        }

        @Override
        public void onMessage(ByteBuffer bytes) {
            Message msg = Message.obtain();
            msg.what = 0;
            msg.obj = getString(bytes);
            mUIHandler.sendMessage(msg);
        }
    };

    private Handler mUIHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 0:
                    mTextViewReceive.setText((String) msg.obj);
                    break;
                default:
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
        mEditTextIP = findViewById(R.id.edit_ip);
        mEditTextSend = findViewById(R.id.edit_send);
        mButtonConnect = findViewById(R.id.btn_connect);
        mButtonSend = findViewById(R.id.btn_send);
        mTextViewReceive = findViewById(R.id.text_receive);
    }

    public void connectServe(View view) {
        String ip = mEditTextIP.getText().toString();
        if (ip != null && ip.startsWith("192.168.")) {
            try {
                mJWSClient = new JWSClient(new URI("ws://" + ip + ":13333"), mJWSCallBack);
                mJWSClient.setConnectionLostTimeout(5);
                mJWSClient.connectBlocking(3, TimeUnit.SECONDS);
            } catch (URISyntaxException | InterruptedException exception) {
                exception.printStackTrace();
                Toast.makeText(MainActivity.this, "连接失败", Toast.LENGTH_SHORT).show();
            }
        } else {
            Toast.makeText(MainActivity.this, "请输入正确IP", Toast.LENGTH_SHORT).show();
        }
    }

    public void send(View view) {
        String msg = mEditTextSend.getText().toString();
        if (msg != null && !"".equals(msg)) {
            if (mJWSClient != null && mJWSClient.isOpen()) {
                Log.d(TAG, "send: ");
                mJWSClient.send(msg);
            }
        } else {
            Toast.makeText(MainActivity.this, "发送不能为空", Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * 将ByteBuffer转换为String
     *
     * @param buffer
     * @return
     */
    public static String getString(ByteBuffer buffer) {
        Charset charset = null;
        CharsetDecoder decoder = null;
        CharBuffer charBuffer = null;
        try {
            charset = Charset.forName("UTF-8");
            decoder = charset.newDecoder();
            // charBuffer = decoder.decode(buffer);//用这个的话,只能输出来一次结果,第二次显示为空
            charBuffer = decoder.decode(buffer.asReadOnlyBuffer());
            return charBuffer.toString();
        } catch (Exception ex) {
            ex.printStackTrace();
            return "";
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mJWSClient != null) {
            mJWSClient.close();
        }
    }
}

serve端

继承WebSocketServer

与client差不多,只不过会多一个WebSocket参数,每当有一个client去连接serve端时,就会生成一个WebSocket对象,可以获取client的ip地址。

onClose时,无法通过WebSocket获取地址。

service端启动

传入端口创建WebSocketServer对象,调用start方法即可。

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";

    private TextView mTextViewReceive;
    private TextView mTextConnectClient;
    private JWSServe mJWSServe;
    private List<String> mClientIpList = new ArrayList<>();

    private JWSServe.JWSCallBack mJWSCallBack = new JWSServe.JWSCallBack() {
        @Override
        public void onOpen(ClientHandshake handshake) {

        }

        @Override
        public void onMessage(String message) {
            Message msg = Message.obtain();
            msg.what = 0;
            msg.obj = message;
            mUIHandler.sendMessage(msg);
        }

        @Override
        public void onMessage(ByteBuffer bytes) {
            Message msg = Message.obtain();
            msg.what = 0;
            msg.obj = getString(bytes);
            mUIHandler.sendMessage(msg);
        }

        @Override
        public void updateSocketClient(HashMap<WebSocket, String> socketHashMap) {
            mClientIpList = new ArrayList<>(socketHashMap.values());
            mUIHandler.sendEmptyMessage(1);
        }
    };

    private Handler mUIHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 0:
                    mTextViewReceive.setText((String) msg.obj);
                    break;
                case 1:
                    StringBuffer stringBuffer = new StringBuffer();
                    if (mClientIpList.size() < 1) {
                        stringBuffer.append("当前无设备连接\n");
                    } else {
                        for (String ip : mClientIpList) {
                            stringBuffer.append(ip + ":已连接\n");
                        }
                    }
                    mTextConnectClient.setText(stringBuffer.toString());
                    break;
                default:
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextViewReceive = findViewById(R.id.text_receive);
        mTextConnectClient = findViewById(R.id.text_connect_client);
    }

    @Override
    protected void onResume() {
        super.onResume();
        mJWSServe = new JWSServe(13333, mJWSCallBack);
        mJWSServe.setConnectionLostTimeout(5);
        mJWSServe.start();
    }

    public void send(View view) {
        String msg = ((EditText) findViewById(R.id.edit_send)).getText().toString();
        if (msg != null && !"".equals(msg)) {
            mJWSServe.send(mClientIpList, msg);
        } else {
            Toast.makeText(MainActivity.this, "发送不能为空", Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * 将ByteBuffer转换为String
     *
     * @param buffer
     * @return
     */
    public static String getString(ByteBuffer buffer) {
        Charset charset = null;
        CharsetDecoder decoder = null;
        CharBuffer charBuffer = null;
        try {
            charset = Charset.forName("UTF-8");
            decoder = charset.newDecoder();
            // charBuffer = decoder.decode(buffer);//用这个的话,只能输出来一次结果,第二次显示为空
            charBuffer = decoder.decode(buffer.asReadOnlyBuffer());
            return charBuffer.toString();
        } catch (Exception ex) {
            ex.printStackTrace();
            return "";
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        try {
            mJWSServe.stop();
        } catch (IOException exception) {
            exception.printStackTrace();
        } catch (InterruptedException exception) {
            exception.printStackTrace();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }
}

JavaWebSocket的心跳机制

client和server的重连是依靠PingPong

如client调用ping方法,如果server收到消息会,会立刻给client回复一个Pong。则client的Pong方法会立刻被调用。

实际上实现使用JavaWebSocket并不需要如此复杂,在server端启动或client端连接前,通过setConnectionLostTimeout设置一个连接超时的时间(未设置则默认时间一分钟,设置为0或负数表示关闭超时检测)。

serve会根据设置时间检测client列表有哪些已超时,则会走onClose。

client设置时间后,serve会每隔固定式见向client发送ping,如果client超时没有收到pong,则走onError和onClose。

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-03-12 17:57:52  更:2022-03-12 17:58:43 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 6:54:57-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码