截图1、Android 客户端
client.png2、PC服务端:用的是SocketTool软件模拟
server.png
流程
1、连接DatagramSocket的服务端(ip和port):开启异步线程和socket
2、发送数据(DatagramPacket):异步
3、接收数据(DatagramPacket):注意连接状态,异步读取
4、关闭连接:关闭DatagramSocket和对应线程
注意
1、异常:android.os.NetworkOnMainThreadException。 socket需要在线程中使用
2、前后端统一传输或者接收协议 [requestcode size d1 d2 d3 ... ],在解析时候用得到
3、实施监控socket的连接状态,还是用心跳包发过去,然后返回数据,一段时间没有的话则代表socket连接失败。
4、注意receive接收数据后的有效长度(一个是预存的buffer,一个是有效结果buffer)
5、客户端连上去后不知道为何一定要先发送一次,才能接收?
6、UDP不安全,有长度限制64K
代码1、UdpClient.java:udp-socket的客户端,略微做了通用封装,主要是连接,发送,接收,然后设置监听/**
* Created by wujn on 2019/2/15.
* Version : v1.0
* Function: udp client 64k限制
*/public class UdpClient { /**
* single instance UdpClient
* */
private static UdpClient mSocketClient = null; private UdpClient(){} public static UdpClient getInstance(){ if(mSocketClient == null){ synchronized (UdpClient.class) {
mSocketClient = new UdpClient();
}
} return mSocketClient;
}
String TAG_log = "Socket"; private DatagramSocket mSocket; private DatagramPacket sendPacket; //发送
private DatagramPacket receivePacket; //接受// private OutputStream mOutputStream;// private InputStream mInputStream;
private SocketThread mSocketThread; private boolean isStop = false;//thread flag
/**
* 128 - 数据按照最长接收,一次性
* */
private class SocketThread extends Thread { private String ip; private int port; public SocketThread(String ip, int port){ this.ip = ip; this.port = port;
} @Override
public void run() {
Log.d(TAG_log,"SocketThread start "); super.run(); //connect ...
try { if (mSocket != null) {
mSocket.close();
mSocket = null;
}
InetAddress ipAddress = InetAddress.getByName(ip);
mSocket = new DatagramSocket();
mSocket.connect(ipAddress, port); //连接
//设置timeout
//mSocket.setSoTimeout(3000);
Log.d(TAG_log,"udp connect = "+isConnect()); if(isConnect()){
isStop = false;
uiHandler.sendEmptyMessage(1);
} /* 此处这样做没什么意义不大,真正的socket未连接还是靠心跳发送,等待服务端回应比较好,一段时间内未回应,则socket未连接成功 */
else{
uiHandler.sendEmptyMessage(-1);
Log.e(TAG_log,"SocketThread connect fail"); return;
}
} catch (IOException e) {
uiHandler.sendEmptyMessage(-1);
Log.e(TAG_log,"SocketThread connect io exception = "+e.getMessage());
e.printStackTrace(); return;
}
Log.d(TAG_log,"SocketThread connect over "); //发送一次,否则不发送则收不到,不知道为啥。。。
sendByteCmd(new byte[]{00},-1);//send once
//read ...
while (isConnect() && !isStop && !isInterrupted()) { int size; try { byte[] preBuffer = new byte[4 * 1024];//预存buffer
receivePacket = new DatagramPacket(preBuffer, preBuffer.length);
mSocket.receive(receivePacket); if (receivePacket.getData() == null) return;
size = receivePacket.getLength(); //此为获取后的有效长度,一次最多读64k,预存小的话可能分包
Log.d(TAG_log, "pre data size = "+receivePacket.getData().length + ", value data size = "+size); byte[] dataBuffer = Arrays.copyOf(preBuffer, size); if (size > 0) {
Message msg = new Message();
msg.what = 100;
Bundle bundle = new Bundle();
bundle.putByteArray("data",dataBuffer);
bundle.putInt("size",size);
bundle.putInt("requestCode",requestCode);
msg.setData(bundle);
uiHandler.sendMessage(msg);
}
Log.i(TAG_log, "SocketThread read listening"); //Thread.sleep(100);//log eof
} catch (IOException e) {
uiHandler.sendEmptyMessage(-1);
Log.e(TAG_log,"SocketThread read io exception = "+e.getMessage());
e.printStackTrace(); return;
}
}
}
} //==============================socket connect============================
private String ip; private int port; /**
* connect socket in thread
* Exception : android.os.NetworkOnMainThreadException
* */
public void connect(String ip, int port){ this.ip = ip; this.port = port;
mSocketThread = new SocketThread(ip, port);
mSocketThread.start();
} /**
* socket is connect
* */
public boolean isConnect(){ boolean flag = false; if (mSocket != null) {
flag = mSocket.isConnected();
} return flag;
}
/**
* socket disconnect
* */
public void disconnect() {
isStop = true; if (mSocket != null) {
mSocket.close();
mSocket = null;
} if (mSocketThread != null) {
mSocketThread.interrupt();//not intime destory thread,so need a flag
}
} /**
* send byte[] cmd
* Exception : android.os.NetworkOnMainThreadException
* */
public void sendByteCmd(final byte[] mBuffer,int requestCode) { this.requestCode = requestCode; new Thread(new Runnable() { @Override
public void run() { try {
InetAddress ipAddress = InetAddress.getByName(ip);
sendPacket = new DatagramPacket(mBuffer, mBuffer.length, ipAddress, port);
mSocket.send(sendPacket);
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
Handler uiHandler = new Handler() { @Override
public void handleMessage(Message msg) { super.handleMessage(msg); switch(msg.what){ //connect error
case -1: if (null != onDataReceiveListener) {
onDataReceiveListener.onConnectFail();
disconnect();
} break; //connect success
case 1: if (null != onDataReceiveListener) {
onDataReceiveListener.onConnectSuccess();
} break; //receive data
case 100:
Bundle bundle = msg.getData(); byte[] buffer = bundle.getByteArray("data"); int size = bundle.getInt("size"); int mequestCode = bundle.getInt("requestCode"); if (null != onDataReceiveListener) {
onDataReceiveListener.onDataReceive(buffer, size, mequestCode);
} break;
}
}
};
/**
* socket response data listener
* */
private OnDataReceiveListener onDataReceiveListener = null; private int requestCode = -1; public interface OnDataReceiveListener { public void onConnectSuccess(); public void onConnectFail(); public void onDataReceive(byte[] buffer, int size, int requestCode);
} public void setOnDataReceiveListener(
OnDataReceiveListener dataReceiveListener) {
onDataReceiveListener = dataReceiveListener;
}
}MainActivity.java:使用,简单明了private void initListener(){ //socket connect
btn_connect.setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View v) {
String ip = et_ip.getText().toString();
String port = et_port.getText().toString(); if(TextUtils.isEmpty(ip)){
Toast.makeText(UdpActivity.this,"IP地址为空",Toast.LENGTH_SHORT).show(); return;
} if(TextUtils.isEmpty(port)){
Toast.makeText(UdpActivity.this,"端口号为空",Toast.LENGTH_SHORT).show(); return;
}
connect(ip, Integer.parseInt(port));
}
}); //socket disconnect
btn_disconnect.setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View v) {
disconnect();
}
}); //socket send
btn_send.setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View v) { if (UdpClient.getInstance().isConnect()) { byte[] data = et_send.getText().toString().getBytes();
send(data);
} else {
Toast.makeText(UdpActivity.this,"尚未连接,请连接Socket",Toast.LENGTH_SHORT).show();
}
}
}); //clear receive
btn_clear.setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View v) {
tv_receive.setText("");
}
});
} /**
* socket data receive
* */
private void initDataReceiver(){
UdpClient.getInstance().setOnDataReceiveListener(dataReceiveListener);
} /**
* socket connect
* */
private void connect(String ip, int port){
UdpClient.getInstance().connect(ip, port);
} /**
* socket disconnect
* */
private void disconnect(){
UdpClient.getInstance().disconnect();
tv_state.setText("未连接");
} /**
* socket send
* */
private void send(byte[] data){
String ip = et_ip.getText().toString();
String port = et_port.getText().toString();
UdpClient.getInstance().sendByteCmd(data,1001);
} /**
* socket data receive
* data(byte[]) analyze
* */
private UdpClient.OnDataReceiveListener dataReceiveListener = new UdpClient.OnDataReceiveListener() { @Override
public void onConnectSuccess() {
Log.i(TAG_log,"onDataReceive connect success");
tv_state.setText("已连接");
} @Override
public void onConnectFail() {
Log.e(TAG_log,"onDataReceive connect fail");
tv_state.setText("未连接");
} @Override
public void onDataReceive(byte[] buffer, int size, int requestCode) { //获取有效长度的数据
byte[] data = new byte[size];
System.arraycopy(buffer, 0, data, 0, size); final String oxValue = Arrays.toString(HexUtil.Byte2Ox(data));
Log.i(TAG_log,"onDataReceive requestCode = "+requestCode + ", content = "+oxValue);
tv_receive.setText(tv_receive.getText().toString() + oxValue + "\n");
}
}; @Override
protected void onDestroy() {
UdpClient.getInstance().disconnect(); super.onDestroy();
}
作者:Kandy_JS
链接:https://www.jianshu.com/p/9dbb8ac146f6