Android 基於UDP的Socket通訊
參考
1、 Android Socket通訊--UdpClient
2、 Android UDP
3、 Android Socket通訊(一)--基於UDP協議通訊
截圖
1、Android 客戶端

client.png
2、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(); }
2019 (* ̄(oo) ̄) 諸事順利!