Android 使用WebSocket实现手机连手机聊天室

实现效果:
在这里插入图片描述
1.开发环境Android studio 2.3
2.添加依赖【可以只使用.java-websocket 就能完成】
注:这里使用了eventbus来做数据传输

//WebSocket 客户端
compile 'com.neovisionaries:nv-websocket-client:2.2'
//WebSocket 服务端
compile "org.java-websocket:Java-WebSocket:1.4.0"
//eventbus
compile 'org.greenrobot:eventbus:3.0.0'

3.结构如下【服务端的开启和客户端的socket接入都是在同一个界面完成的】
在这里插入图片描述
4.具体代码:

import org.greenrobot.eventbus.EventBus;
import org.java_websocket.WebSocket;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.server.WebSocketServer;

import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import mwteck.ettda.util.L;

/**
 * CreateTime 2019/6/6 10:04
 * Author LiuShiHua
 * Description:
 */

public class MyServerSocket extends WebSocketServer {
    private EventBus bus = null;
    private Map<String, WebSocket> userMap = new HashMap<String, WebSocket>();//存储客户端信息

    public MyServerSocket(int port) {//默认端口号是80,这里自定义了自己的端口号
        super(new InetSocketAddress(port));
        bus = EventBus.getDefault();
    }

    //发送消息给所有用户
    public void sendMessageToUser(String message) {
        message = "服务端说:" + message;
        bus.post(message);
        if (userMap == null || userMap.isEmpty()) return;
        Set<String> ketSet = userMap.keySet();
        for (String key : ketSet) {
            WebSocket clientSocket = userMap.get(key);
            if (clientSocket != null) {
                clientSocket.send(message);
            }
        }
    }

    @Override
    public void onOpen(WebSocket conn, ClientHandshake handshake) {
        String userHost = conn.getRemoteSocketAddress().getHostString();
        if (!userMap.containsKey(userHost)) {//判断
            userMap.put(userHost, conn);
        }
        String openStr = "onOpen客户端 " + getClientInfo(conn) + " 上线";
        bus.post(openStr);
        L.d(openStr);
    }

    //客户端离开或者服务端关闭时调用
    @Override
    public void onClose(WebSocket conn, int code, String reason, boolean remote) {
        String userHost = conn.getRemoteSocketAddress().getHostString();
        if (userMap.containsKey(userHost)) {
            userMap.remove(userHost);
        }
        String closeStr = "";
        if (remote) {//远程关闭连接
            closeStr = "onClose客户端关闭:code = " + code + ":reason = " + reason + ":remote = " + remote;

        } else {//服务端关闭连接
            closeStr = "onClose本地关闭:code = " + code + ":reason = " + reason + ":remote = " + remote;
        }
        bus.post(closeStr);
        L.d(closeStr);
    }

    //收到消息时调用
    @Override
    public void onMessage(WebSocket conn, String message) {
        String msg = getClientInfo(conn) + "说:" + message;
        bus.post(msg);
        L.d("onMessage " + msg);
    }

    @Override
    public void onError(WebSocket conn, Exception ex) {
        String err = "onError " + getClientInfo(conn) + ":" + ex.getMessage();
        bus.post(err);
        L.d(err);
    }

    //用户连接进来时先调用此方法在调用onOpen
    @Override
    protected boolean onConnect(SelectionKey key) {
        bus.post("onConnect");
        L.d("onConnect:SelectionKey" + key.channel().toString());
        return super.onConnect(key);
    }

    @Override
    public void onStart() {
        String con = "onStart:port = " + this.getAddress().getPort();
        bus.post(con);
        L.d(con);
    }

    //获取客户端host:port
    private String getClientInfo(WebSocket conn) {
        return conn.getRemoteSocketAddress().getHostString() + ":" + conn.getRemoteSocketAddress().getPort();
    }
}


/**
 * CreateTime 2019/6/5 13:55
 * Author LiuShiHua
 * Description:
 */

import android.util.Log;

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

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import mwteck.ettda.util.L;

/**
 * Created by Roc on 2018/10/9.
 */
public class ServerSocketManager {
    private MyServerSocket serverSocket = null;
    private final int SOCKET_PORT = 8201;
    private Map<WebSocket, String> userMap = new HashMap<WebSocket, String>();

    public ServerSocketManager() {
        this.serverSocket =  new MyServerSocket(SOCKET_PORT);
    }

    //将消息发送给所有用户
    public void sendMessageToAll(String message) {
        serverSocket.sendMessageToUser(message);
    }

    //开启服务
    public boolean start() {
        try {
            serverSocket.start();
            L.d("serverSocket is start");
            return true;
        } catch (Exception e) {
            L.d("serverSocket start onError:" + e.getMessage());
            e.printStackTrace();
            return false;
        }
    }

    public boolean stop() {
        try {
            serverSocket.stop();
            return true;
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException ie) {
            ie.printStackTrace();
        }
        return false;

    }


}


import com.neovisionaries.ws.client.WebSocket;
import com.neovisionaries.ws.client.WebSocketAdapter;
import com.neovisionaries.ws.client.WebSocketException;
import com.neovisionaries.ws.client.WebSocketFactory;
import com.neovisionaries.ws.client.WebSocketFrame;

import org.greenrobot.eventbus.EventBus;

import java.io.IOException;
import java.util.List;
import java.util.Map;

import mwteck.ettda.util.L;

/**
 * CreateTime 2019/6/4 15:12
 * Author LiuShiHua
 * Description:
 */

public class ClientSocketManager {
    private final int FRAME_QUEUE_SIZE = 5;
    private final int CONNECT_TIMEOUT = 120 * 1000;
    private WebSocket clientSocket;
    private EventBus bus = null;
    private String socketUrl;
    private WebSocketAdapter adapter = new WebSocketAdapter() {
        @Override
        public void onTextMessage(WebSocket websocket, String text) throws Exception {
            super.onTextMessage(websocket, text);
            String[] msgs = text.split("\\|");
            if (msgs.length >= 2) {
                for (int i = 0; i < msgs.length; i++) {
                    L.d("收到消息:" + msgs[i]);
                    bus.post("收到消息:" + msgs[i]);
                }
            } else {
                L.d("收到消息:" + text);
                bus.post("收到消息:" + text);
            }
        }

        @Override
        public void onConnected(WebSocket websocket, Map<String, List<String>> headers)
                throws Exception {
            super.onConnected(websocket, headers);
            bus.post("连接成功");
            L.d("连接成功");
        }

        @Override
        public void onConnectError(WebSocket websocket, WebSocketException exception)
                throws Exception {
            super.onConnectError(websocket, exception);
            bus.post("连接错误:" + exception.getMessage());
            L.e("连接错误:" + exception.getMessage());
        }

        @Override
        public void onDisconnected(WebSocket websocket, WebSocketFrame serverCloseFrame, WebSocketFrame clientCloseFrame, boolean closedByServer)
                throws Exception {
            super.onDisconnected(websocket, serverCloseFrame, clientCloseFrame, closedByServer);
            L.e("断开连接");
            bus.post("断开连接");
        }
    };


    public ClientSocketManager(String socketUrl) {
        this.socketUrl = socketUrl;
        bus = EventBus.getDefault();
    }

    //链接socket
    public void connectSocket() {
        L.d("链接socket");
        if (clientSocket != null && clientSocket.isOpen()) {
            clientSocket.sendClose();
            clientSocket = null;
        }
        try {
            //ws地址,和设置超时时间
            //设置帧队列最大值为5
            //设置不允许服务端关闭连接却未发送关闭帧
            //添加回调监听
            //异步连接
            clientSocket = new WebSocketFactory().createSocket(socketUrl, CONNECT_TIMEOUT) //ws地址,和设置超时时间
                    .setFrameQueueSize(FRAME_QUEUE_SIZE)//设置帧队列最大值为5
                    .setMissingCloseFrameAllowed(false)//设置不允许服务端关闭连接却未发送关闭帧
                    .addListener(adapter)//添加回调监听
                    .connectAsynchronously();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //发送消息
    public void sendMsg(String msg) {
        if (clientSocket != null) {
            if (clientSocket.isOpen()) {//判断已连接
                clientSocket.sendText(msg);
            } else {
                L.d("clientSocket已经关闭");
            }
        } else {
            connectSocket();
            sendMsg(msg);
            L.e("clientSocket == null");
        }
    }

    //离线
    public void turnOff() {
        if (clientSocket != null) {
            if (clientSocket.isOpen()) {//判断已连接
                clientSocket.sendClose();//下线
                clientSocket = null;
            }
        } else {
            L.e("clientSocket == null");
        }
    }
}


import android.os.Handler;
import android.os.Message;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collections;

import mwteck.ettda.util.L;

/**
 * CreateTime 2019/6/5 14:24
 * Author LiuShiHua
 * Description:
 */

public class InternetUtil {

    //获取本地ipv4地址
    public static String getLocalIpV4Address() {
        try {
            String ipv4;
            ArrayList<NetworkInterface> nilist = Collections.list(NetworkInterface.getNetworkInterfaces());
            for (NetworkInterface ni : nilist) {
                ArrayList<InetAddress> ialist = Collections.list(ni.getInetAddresses());
                for (InetAddress address : ialist) {
                    if (!address.isLoopbackAddress() && !address.isLinkLocalAddress()) {
                        ipv4 = address.getHostAddress();
                        L.d("本地ipv4地址:" + ipv4);
                        return ipv4;
                    }
                }

            }

        } catch (SocketException ex) {
            L.e("getLocalIpV4Address Error:" + ex.getMessage());
        }
        return null;
    }

    /**
     * 获取外网IP地址
     *
     * @return
     */
    public static final int GET_NET_IP_BACK_CODE = 0x220;

    public static void getNetIp(final Handler handler) {
        new Thread() {
            @Override
            public void run() {
                String line = "";
                URL infoUrl = null;
                InputStream inStream = null;
                try {
                    infoUrl = new URL("http://pv.sohu.com/cityjson?ie=utf-8");
                    URLConnection connection = infoUrl.openConnection();
                    HttpURLConnection httpConnection = (HttpURLConnection) connection;
                    int responseCode = httpConnection.getResponseCode();
                    if (responseCode == HttpURLConnection.HTTP_OK) {
                        inStream = httpConnection.getInputStream();
                        BufferedReader reader = new BufferedReader(new InputStreamReader(inStream, "utf-8"));
                        StringBuilder strber = new StringBuilder();
                        while ((line = reader.readLine()) != null)
                            strber.append(line + "\n");
                        inStream.close();
                        // 从反馈的结果中提取出IP地址
                        int start = strber.indexOf("{");
                        int end = strber.indexOf("}");
                        String json = strber.substring(start, end + 1);
                        if (json != null) {
                            try {
                                JSONObject jsonObject = new JSONObject(json);
                                line = jsonObject.optString("cip");
                            } catch (JSONException e) {
                                e.printStackTrace();
                            }
                        }
                        Message msg = new Message();
                        msg.what = GET_NET_IP_BACK_CODE;
                        msg.obj = line;
                        //向主线程发送消息
                        handler.sendMessage(msg);
                        L.d("外网地址:" + line);
                    }
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }
}


import android.content.Intent;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;

import mwteck.ettda.R;
import mwteck.ettda.base.BaseActivity;
import mwteck.ettda.pages.demosocket.client.ClientSocketManager;
import mwteck.ettda.pages.demosocket.server.ServerSocketManager;
import mwteck.ettda.pages.demosocket.util.InternetUtil;
import mwteck.ettda.util.Tools;

public class SocketActivity extends BaseActivity {

    private TextView text;
    private Button sendB;
    private Button offB;
    private ClientSocketManager clientSocketManager;
    //    private final String SOCKET_URL = "ws://192.168.50.191:8888/websocket";
    private final String SOCKET_URL = "ws://192.168.50.139:8201";
    private Button connectB;
    private EditText edit;
    private Button startServerB;
    private int socket_type = 0;
    private final int SOCKET_TYPE_SERVER = 1;//服务端
    private final int SOCKET_TYPE_CLIENT = 2;//客户端
    private ServerSocketManager serverSocketManager;

    @Override
    protected void setContentView() {
        setContentView(R.layout.activity_socket);
        EventBus.getDefault().register(this);
    }

    @Override
    protected void initView() {
        text = (TextView) findViewById(R.id.text);
        edit = (EditText) findViewById(R.id.edit);
        sendB = (Button) findViewById(R.id.sendB);
        startServerB = (Button) findViewById(R.id.startServerB);
        connectB = (Button) findViewById(R.id.connectB);
        offB = (Button) findViewById(R.id.offB);
        startServerB.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                socket_type = SOCKET_TYPE_SERVER;
                connectB.setClickable(false);
                serverSocketManager.start();
            }
        });
        connectB.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                socket_type = SOCKET_TYPE_CLIENT;
                startServerB.setClickable(false);
                clientSocketManager.connectSocket();
            }
        });
        sendB.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String msg = edit.getText().toString();
                edit.setText("");
                edit.clearFocus();
                Tools.hideInputKeyboard(SocketActivity.this, edit);
                if (socket_type == SOCKET_TYPE_CLIENT) {
                    clientSocketManager.sendMsg(msg);
                } else {
                    serverSocketManager.sendMessageToAll(msg);
                }
            }
        });
        offB.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (socket_type == SOCKET_TYPE_CLIENT) {
                    clientSocketManager.turnOff();
                } else {
                    serverSocketManager.stop();
                }
                connectB.setClickable(true);
                startServerB.setClickable(true);
            }
        });
    }

    @Override
    protected void initData() {
        clientSocketManager = new ClientSocketManager(SOCKET_URL);
        serverSocketManager = new ServerSocketManager();
        String ipv4 = InternetUtil.getLocalIpV4Address();
        setText("ipv4:" + ipv4);
        InternetUtil.getNetIp(new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                switch (msg.what) {
                    case InternetUtil.GET_NET_IP_BACK_CODE:
                        setText("外网地址:" + msg.obj.toString());
                        break;
                }
            }
        });
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void receiveMsg(String msg) {
        setText(msg);
    }

    //设置文本信息
    private void setText(String msg) {
        if (Tools.isEmpty(text.getText().toString())) {
            text.setText(msg);
            return;
        }
        String content = text.getText().toString() + "\n" + msg;
        text.setText(content);
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(this);
    }
}

Activity界面代码

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_socket"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="mwteck.ettda.pages.demosocket.SocketActivity">

    <Button
        android:id="@+id/startServerB"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="@android:color/holo_blue_light"
        android:text="开启Socket服务"
        android:textColor="@color/white" />

    <Button
        android:id="@+id/connectB"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_below="@+id/startServerB"
        android:layout_marginTop="5dp"
        android:background="@android:color/holo_blue_light"
        android:text="连接到Socket"
        android:textColor="@color/white" />

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/connectB">

        <TextView
            android:id="@+id/text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="接收到的内容..."
            android:padding="5dp"
            android:textColor="@color/black_33"
            android:textSize="14sp" />
    </ScrollView>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:focusableInTouchMode="true"
        android:orientation="horizontal">

        <EditText
            android:id="@+id/edit"
            android:layout_width="0dp"
            android:layout_height="50dp"
            android:layout_weight="1"
            android:background="@drawable/edit_bg_style"
            android:hint="请输入要发送的内容"
            android:padding="5dp"
            android:textSize="14sp" />

        <Button
            android:id="@+id/sendB"
            android:layout_width="70dp"
            android:layout_height="match_parent"
            android:layout_marginLeft="5dp"
            android:background="@drawable/linear_press_style"
            android:text="发送" />

        <Button
            android:id="@+id/offB"
            android:layout_width="70dp"
            android:layout_height="match_parent"
            android:layout_marginLeft="5dp"
            android:background="@drawable/linear_press_style"
            android:text="下线" />
    </LinearLayout>
</RelativeLayout>

版权声明:本文为yingtian648原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/yingtian648/article/details/91043353

智能推荐

模拟按键 —— 鼠标

背景 之前写自动化脚本的时候总是遇到一些很尴尬的问题: 跑脚本时模拟鼠标按键时,光标是真实的跑到了那个位置的,也就是说跑脚本的时候会很影响电脑的正常使用,导致不得不开一个虚拟机专门跑。 另外因为光标只有一个所以很难实现多线程去同时操作多个窗口,当线程1 模拟鼠标但还没有结束时,线程2 已经开始执行模拟操作,这就导致了线程1 的模拟操作被终止了,被迫之下只能开多个虚拟机(但实在太占用性能🙄) 解决...

Hibernate学习总结(一)

一、Hibernate简介 一个持久层的ORM框架。ORM:Object Relational Mapping(对象关系映射)。指的是将一个Java中的对象与关系型数据库中的表建立一种映射关系,从而操作对象就可以操作数据库中的表。 二、Hibernate入门 1、创建一个项目,引入jar包 hibernate用到的jar包 2、创建表 3、创建实体类 4、创建映射(*****) 映射需要通过XML...

Linux系统NFS

文章目录 1. nfs简介 1.1 nfs特点 1.2 使用nfs的好处 1.3 nfs的体系组成 1.4 nfs的应用场景 2. nfs工作机制 2.1 RPC 2.2 NIS 2.3 nfs工作机制 3. exports文件的格式 4. nfs管理 5. 作业 5.1手动搭建一个nfs服务器 5.1.1开放/nfs/shared目录,供所有用户查阅资料 5.1.2 开放/nfs/upload目...

关于java中String,StringBuffer,StringBuilder的区别以及StringBuffer,StringBuilder的安全性问题

这里的结果就是正确的然后我们来看他的append方法 它在前边加了一个synchronized来修饰,相当于同时只能有一个线程来访问他,这样就不会产生上边的问题但同时他的效率也就比StringBuilder低,...

Django连接现有mysql数据库

1、打开cmd后cd到项目位置 2、建立项目 django-admin startproject test2 3、编辑项目中的配置文件, mysite/settings.py ,告诉Django你的数据库连接参数和数据库名。具体的说,要提供 DATABASE_NAME , DATABASE_ENGINE , DATAB...

猜你喜欢

ShareSDK新浪微博登录时报错error:redirect_uri_mismatch

今天用 ShareSDK 做第三方登录的时候碰到个问题,明明在微博平台的应用审核已经通过了,但是调用登录接口的时候一直报错,错误如下: 出现这个错误是因为在微博开放平台上没有设置回调地址,或者设置的回调地址与本地XML中的地址不一致。 在sharesdk.xml文件当中对于微博的设置: 其中RedirectUrl为设置的回调地址,这里的地址必须要与微博开发平台设置的地址相同,否则就会出现上面的错误...

python解析网络封包方法

2019独角兽企业重金招聘Python工程师标准>>> 在使用Python解析网络数据包时,使用网络字节序解析,参见下表。 C语言的数据类型和Python的数据类型对照表请参见下表。 接下来对封包与解包进行举例说明。 version type id content unsigned short unsigned short unsigned int unsigned int 封包...

python3:时间方法,异常处理,系统文件相关模块(os)

文章目录 时间方法 time模块 时间表示方法: time模块的方法 datetime模块 异常处理 触发异常 创建mydiv.py脚本,要求如下: 创建myerror.py脚本,要求如下: os模块 实现ls -R(os.walk) os.path pickle模块 记账脚本 时间方法 time模块 时间表示方法: 时间戳:自1970-1-1 0:00:00到某一时间点之间的秒数 UTC时间:世...

负载均衡群集——LVS+DR模型

一、实验组成 调度器 192.168.100:41 web1 192.168.100:42 web2 192.168.100.43 NFS共享服务器 192.168.100.44 二、实验拓扑 三、实验配置 3.1在调度器配置:192.168.100.41 配置虚拟IP地址(VIP) 调整/proc响应参数 对于 DR 群集模式来说,由于 LVS 负载调度器和各节点需要共用 VIP 地址,应该关闭...

adb无线连接时appium找不到设备

问题描述 以前使用USB连接真机,运行appium时一直正常,连接参数如下: 最近为了方便,使用adb无线连接真机,adb版本为1.0.40,真机安卓版本10,连接后,通过adb devices能够查看到连接的设备: adb无线连接是正常的,但每次运行时appium都找不到无线连接的设备,陷入重启adb循环: 解决流程 1.因为是没找到设备,所以在appium连接参数中增加了"udid&...