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>
智能推荐
模拟按键 —— 鼠标
背景 之前写自动化脚本的时候总是遇到一些很尴尬的问题: 跑脚本时模拟鼠标按键时,光标是真实的跑到了那个位置的,也就是说跑脚本的时候会很影响电脑的正常使用,导致不得不开一个虚拟机专门跑。 另外因为光标只有一个所以很难实现多线程去同时操作多个窗口,当线程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&...