Files
jGPIO/src/customsocket/TCPSocketPrefixMode.java
2024-12-04 08:59:37 +07:00

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});
}
}
}