419 lines
11 KiB
Java
419 lines
11 KiB
Java
package customsocket;
|
|
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.UnsupportedEncodingException;
|
|
import java.net.InetSocketAddress;
|
|
import java.net.Socket;
|
|
import java.net.SocketAddress;
|
|
import java.net.UnknownHostException;
|
|
import java.nio.BufferUnderflowException;
|
|
import java.nio.ByteBuffer;
|
|
import java.nio.ByteOrder;
|
|
import java.util.concurrent.Callable;
|
|
|
|
import anywheresoftware.b4a.BA;
|
|
import anywheresoftware.b4a.keywords.DateTime;
|
|
import jGPIO.mycodes;
|
|
|
|
@BA.ShortName("TCPSocketPrefixMode")
|
|
@BA.Events(values= {
|
|
"log(msg as string)",
|
|
"connected(targetip as string, targetport as int)",
|
|
"disconnected(targetip as string, targetport as int, duration as long)",
|
|
"newbytesdata(value() as byte)",
|
|
"newstringdata(value as String)",
|
|
|
|
})
|
|
public class TCPSocketPrefixMode {
|
|
private BA bax;
|
|
private String event;
|
|
private boolean need_log_event = false;
|
|
private boolean need_connected_event = false;
|
|
private boolean need_disconnected_event = false;
|
|
private boolean need_newbytesdata_event = false;
|
|
private boolean need_newstringdata_event = false;
|
|
private boolean inited = false;
|
|
private Socket _socket;
|
|
|
|
private long _connected_tick = 0;
|
|
|
|
private int _txokcount = 0;
|
|
private long _txbytescount = 0;
|
|
private int _rxokcount = 0;
|
|
private long _rxbytescount = 0;
|
|
|
|
private String _remoteip = "";
|
|
private int _remoteport = 0;
|
|
private String _localip = "";
|
|
private int _localport = 0;
|
|
|
|
|
|
|
|
/**
|
|
* Initialize TCP Socket Prefix Mode
|
|
* @param EventName Eventname
|
|
*/
|
|
public void Initialize(BA ba, String EventName) {
|
|
this.bax = ba;
|
|
this.event = EventName.toLowerCase();
|
|
if (bax!=null) {
|
|
if (event != null && event.length() > 0) {
|
|
need_log_event = bax.subExists(event+"_log");
|
|
need_connected_event = bax.subExists(event+"_connected");
|
|
need_disconnected_event = bax.subExists(event+"_disconnected");
|
|
need_newbytesdata_event = bax.subExists(event+"_newbytesdata");
|
|
need_newstringdata_event = bax.subExists(event+"_newstringdata");
|
|
inited = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
public boolean IsInitialized() {
|
|
return inited;
|
|
}
|
|
|
|
public String getRemoteIP() {
|
|
return _remoteip;
|
|
}
|
|
|
|
public int getRemotePort() {
|
|
return _remoteport;
|
|
}
|
|
|
|
public String getLocalIP() {
|
|
return _localip;
|
|
}
|
|
|
|
public int getLocalPort() {
|
|
return _localport;
|
|
}
|
|
|
|
public void ConnectTo(String targetip, int port) {
|
|
if (targetip != null && targetip.length()>0) {
|
|
if (port>0 && port < 65536) {
|
|
BA.runAsync(bax, TCPSocketPrefixMode.this, event+"_connected", new Object[] {"",0}, new Callable<Object[]>(){
|
|
|
|
@Override
|
|
public Object[] call() throws Exception {
|
|
try {
|
|
Socket trysock = new Socket();
|
|
SocketAddress rem = new InetSocketAddress(targetip, port);
|
|
trysock.connect(rem);
|
|
Thread tx = new Thread(new tcpsocketrun(trysock));
|
|
tx.start();
|
|
return new Object[] {targetip,port};
|
|
} catch (UnknownHostException e) {
|
|
raise_log_error("UnknownHostException on ConnectTo ["+targetip+":"+port+"]",e);
|
|
|
|
} catch (IOException e) {
|
|
raise_log_error("IOException on ConnectTo ["+targetip+"]:["+port+"]",e);
|
|
}
|
|
return new Object[] {"",0};
|
|
}
|
|
|
|
});
|
|
return;
|
|
} else raise_log("ConnectTo failed, invalid targetport");
|
|
} else raise_log("ConnectTo failed, Invalid targetip");
|
|
raise_connected("",0);
|
|
}
|
|
|
|
public void ConnectTo_withBind(String targetip, int port, String localip) {
|
|
if (targetip != null && !targetip.isEmpty()) {
|
|
if (port>0 && port < 65536) {
|
|
if (localip!=null && !localip.isEmpty()) {
|
|
BA.runAsync(bax, TCPSocketPrefixMode.this, event+"_connected", new Object[] {"",0}, new Callable<Object[]>() {
|
|
|
|
@Override
|
|
public Object[] call() throws Exception {
|
|
try {
|
|
SocketAddress loc = new InetSocketAddress(localip, port);
|
|
SocketAddress rem = new InetSocketAddress (targetip,port);
|
|
Socket trysocket = new Socket();
|
|
trysocket.bind(loc);
|
|
trysocket.connect(rem);
|
|
Thread tx = new Thread(new tcpsocketrun(trysocket));
|
|
tx.start();
|
|
return new Object[] {targetip,port};
|
|
} catch(IOException e) {
|
|
raise_log_error("IOException on ConnectTo_withBind ["+targetip+"]:]"+port+"] Bind="+localip,e);
|
|
}
|
|
return new Object[] {"",0};
|
|
}
|
|
|
|
});
|
|
} else raise_log("ConnectTo_withBind failed, invalid localip");
|
|
} else raise_log("ConnectTo_withBind failed, invalid targetport");
|
|
} else raise_log("ConnectTo_withBind failed, Invalid targetip");
|
|
raise_connected("",0);
|
|
}
|
|
|
|
public void Disconnect() {
|
|
|
|
|
|
if (_socket != null) {
|
|
try {
|
|
_socket.close();
|
|
raise_disconnected(_remoteip, _remoteport);
|
|
} catch (IOException e) {
|
|
raise_log_error("IOException on Disconnect",e);
|
|
raise_disconnected("",0);
|
|
}
|
|
} else {
|
|
raise_log("Disconnect has not effect, Socket is not created yet");
|
|
raise_disconnected("",0);
|
|
}
|
|
|
|
_socket = null;
|
|
_remoteip = "";
|
|
_localip = "";
|
|
_remoteport =0;
|
|
_localport = 0;
|
|
|
|
|
|
}
|
|
|
|
public boolean WriteBytes(byte[] data) {
|
|
if (IsConnected()) {
|
|
if (data != null && data.length>0) {
|
|
ByteBuffer bb = ByteBuffer.allocate(data.length+4);
|
|
bb.order(ByteOrder.BIG_ENDIAN);
|
|
bb.putInt(data.length);
|
|
bb.put(data);
|
|
try {
|
|
_socket.getOutputStream().write(bb.array());
|
|
|
|
_txokcount++;
|
|
_txbytescount += data.length;
|
|
return true;
|
|
} catch (IOException e) {
|
|
raise_log_error("IOException on WriteBytes", e);
|
|
Disconnect();
|
|
}
|
|
} else raise_log("WriteBytes failed, data is null or empty");
|
|
} else raise_log("WriteBytes failed, Socket is not connected");
|
|
return false;
|
|
}
|
|
|
|
public boolean WriteString(String data) {
|
|
try {
|
|
byte[] xx = data.getBytes("UTF-8");
|
|
return WriteBytes(xx);
|
|
} catch (UnsupportedEncodingException e) {
|
|
raise_log_error("UnsupportedEncodingException on WriteString", e);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public long ConnectionDuration() {
|
|
if (_connected_tick>0) {
|
|
long now = DateTime.getNow();
|
|
if ((now - _connected_tick)>0) {
|
|
return ((now - _connected_tick) / DateTime.TicksPerSecond);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
public String Connected_DateTime_String() {
|
|
if (_connected_tick>0) {
|
|
return mycodes.Tick_To_DDMMYYYY_HHMMSS(_connected_tick);
|
|
}
|
|
return "";
|
|
}
|
|
|
|
public int getTX_OK_Counter() {
|
|
|
|
return _txokcount;
|
|
}
|
|
|
|
public int getRX_OK_Counter() {
|
|
return _rxokcount;
|
|
}
|
|
|
|
public long getTX_Bytes_Counter() {
|
|
return _txbytescount;
|
|
}
|
|
|
|
public long getRX_Bytes_Counter() {
|
|
return _rxbytescount;
|
|
}
|
|
|
|
/**
|
|
* Check if Socket is connected
|
|
* @return true if connected
|
|
*/
|
|
public boolean IsConnected() {
|
|
if (_socket != null) {
|
|
if (_socket.isConnected()) {
|
|
if (!_socket.isInputShutdown()) {
|
|
if (!_socket.isOutputShutdown()) {
|
|
return !_socket.isClosed();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
class tcpsocketrun implements Runnable{
|
|
private boolean validrun = false;
|
|
private InputStream _input;
|
|
public tcpsocketrun(Socket xx) {
|
|
// reset
|
|
_remoteip = "";
|
|
_remoteport = 0;
|
|
_localip = "";
|
|
_localport = 0;
|
|
_connected_tick = 0;
|
|
_rxbytescount = 0;
|
|
_rxokcount = 0;
|
|
_txbytescount = 0;
|
|
_txokcount = 0;
|
|
|
|
try {
|
|
BA.Log("tcpsocketrun about to call Disconnect on previous _socket if available");
|
|
if (_socket != null) Disconnect();
|
|
_socket = xx;
|
|
_input = _socket.getInputStream();
|
|
|
|
_remoteip = _socket.getInetAddress() == null ? "" : _socket.getInetAddress().getHostAddress();
|
|
_localip = _socket.getLocalAddress() == null ? "" : _socket.getLocalAddress().getHostAddress();
|
|
_remoteport = _socket.isClosed() ? 0 : _socket.getPort();
|
|
_localport = _socket.getLocalPort();
|
|
|
|
validrun = true;
|
|
|
|
} catch(IOException e) {
|
|
raise_log_error("IOException on tcpsocketrun",e);
|
|
}
|
|
|
|
if (validrun) {
|
|
/// shutdownhook taruh di sini, buat auto close ketika socket connected
|
|
Runtime.getRuntime().addShutdownHook(new Thread() {
|
|
public void run() {
|
|
|
|
Disconnect();
|
|
}
|
|
});
|
|
}
|
|
|
|
}
|
|
|
|
public void run() {
|
|
|
|
if (validrun) {
|
|
raise_log("tcpsocketrun running");
|
|
//raise_connected(_socket.getInetAddress().getHostAddress(), _socket.getPort());
|
|
_connected_tick = DateTime.getNow();
|
|
|
|
int readcount;
|
|
byte[] xx;
|
|
while(IsConnected()) {
|
|
xx = new byte[15000];
|
|
readcount = 0;
|
|
try {
|
|
|
|
readcount = _input.read(xx); // ngeblok di sini sampai dapat data
|
|
|
|
} catch (IOException e) {
|
|
raise_log_error("IOException on tcpsocketrun read",e);
|
|
break;
|
|
}
|
|
|
|
// prefix mode, 4 byte awal adalah size, sisanya adalah data
|
|
// jadi minimal length = 4
|
|
if (readcount>4) {
|
|
ByteBuffer bb = ByteBuffer.wrap(xx);
|
|
bb.order(ByteOrder.BIG_ENDIAN);
|
|
try {
|
|
int datasize = bb.getInt();
|
|
byte[] data = new byte[datasize];
|
|
bb.get(data);
|
|
_rxokcount++;
|
|
_rxbytescount += readcount;
|
|
byte[] yy = new byte[readcount];
|
|
System.arraycopy(xx, 0, yy, 0, readcount);
|
|
raise_newbytesdata(yy);
|
|
try {
|
|
String zz = new String(yy, "UTF-8");
|
|
raise_newstringdata(zz);
|
|
} catch (UnsupportedEncodingException e) {
|
|
raise_log_error("UnsupportedEncodingException on tcpsocketrun read", e);
|
|
}
|
|
} catch (BufferUnderflowException e) {
|
|
|
|
raise_log_error("BufferUnderflowException on tcpsocketrun read, prefix invalid", e);
|
|
break;
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
Disconnect();
|
|
|
|
|
|
} else raise_disconnected("",0);
|
|
raise_log("tcpsocketrun ended");
|
|
|
|
}
|
|
}
|
|
|
|
|
|
private void raise_log_error(String msg, Exception e) {
|
|
StringBuffer sbf = new StringBuffer();
|
|
sbf.append(msg);
|
|
if (e instanceof Exception) {
|
|
if (e.getMessage() instanceof String) {
|
|
sbf.append("\r\n");
|
|
sbf.append("Msg : "+e.getMessage());
|
|
}
|
|
if (e.getCause() instanceof Throwable) {
|
|
sbf.append("\r\n");
|
|
sbf.append(e.getCause());
|
|
}
|
|
}
|
|
raise_log(sbf.toString());
|
|
e.printStackTrace();
|
|
}
|
|
|
|
private void raise_log(String msg) {
|
|
if (need_log_event) bax.raiseEventFromDifferentThread(TCPSocketPrefixMode.this, null, 0, event+"_log", false, new Object[] {msg});
|
|
}
|
|
|
|
private void raise_connected(String remoteip, int remoteport) {
|
|
if (need_connected_event) {
|
|
bax.raiseEventFromDifferentThread(TCPSocketPrefixMode.this, null, 0, event+"_connected", false, new Object[] {remoteip, remoteport});
|
|
}
|
|
}
|
|
|
|
private void raise_disconnected(String remoteip, int remoteport) {
|
|
if (need_disconnected_event) {
|
|
if (remoteport > 0 && remoteip.length() > 0) {
|
|
// pernah connect
|
|
long tt = 0;
|
|
if (_connected_tick > 0) {
|
|
tt = (DateTime.getNow() - _connected_tick) / DateTime.TicksPerSecond;
|
|
}
|
|
bax.raiseEventFromDifferentThread(TCPSocketPrefixMode.this, null, 0, event+"_disconnected", false, new Object[] {remoteip,remoteport, tt / DateTime.TicksPerSecond });
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
private void raise_newbytesdata(byte[] value) {
|
|
if (need_newbytesdata_event) {
|
|
bax.raiseEventFromDifferentThread(TCPSocketPrefixMode.this, null, 0, event+"_newbytesdata", false, new Object[] {value});
|
|
}
|
|
}
|
|
|
|
private void raise_newstringdata(String value) {
|
|
if (need_newstringdata_event) {
|
|
bax.raiseEventFromDifferentThread(TCPSocketPrefixMode.this, null, 0, event+"_newstringdata", false, new Object[] {value});
|
|
}
|
|
}
|
|
}
|