实现效果:
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>