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>

  • 0
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值