package ejip123; import joprt.RtThread; import ejip123.util.Dbg; /** A wrapper class emulating (non-blocking) sockets. */ public class TcpStreamConnection implements TcpHandler{ final static public int ESTABLISHED = 0; final static public int CLOSED = 1; private int status = CLOSED; private TcpConnection tc = null; private Packet in = null; private int inOff = 0; private Packet out = null; private int outOff = 0; private int timeout = Integer.MAX_VALUE; /** @param prio Priority @param us Period in microseconds */ public TcpStreamConnection(int prio, int us){ new RtThread(prio, us){ public void run(){ for(; ;){ waitForNextPeriod(); loop(); } } }; } private void loop(){ synchronized(this){ if(out == null){ out = PacketPool.getFreshPacket(); outOff = Tcp.OFFSET<<2; } else{ if((timeout - ((int)(System.currentTimeMillis()))) < 0) flush(); } } } public boolean isBusy(TcpConnection newCon){ synchronized(this){ return status == ESTABLISHED; } } public boolean request(TcpConnection con, Packet p, int off){ synchronized(this){ if(con != tc || in != null) return false; p.setStatus(Packet.ALLOC); in = p; inOff = off<<2; return true; } } public void established(TcpConnection newCon){ synchronized(this){ if(tc == null){ tc = newCon; status = ESTABLISHED; } else newCon.close(); } } public void closed(TcpConnection closedCon){ synchronized(this){ if(closedCon == tc){ status = CLOSED; tc = null; Dbg.wr("con closed in stream\n"); } } } public void reset(TcpConnection closedCon){ synchronized(this){ if(closedCon == tc){ status = CLOSED; tc = null; Dbg.wr("con reset in stream\n"); } } } /** Checks for unread data. @return Number of unread bytes ready to be read. */ public int freeToRead(){ synchronized(this){ if(in == null) return 0; else return (in.len()) - inOff; } } /** Reads one byte from the stream. @return The next byte in the stream, or -1 if there is currently nothing to be read. */ public int read(){ synchronized(this){ if(in == null) return -1; int ret = ((in.buf[inOff>>2]>>>(24 - ((inOff&3)<<3)))&0xff); inOff++; if(inOff >= in.len()){ in.free(); in = null; } return ret; } } /** Checks the send buffer. @return Number of free bytes in the send buffer. */ public int freeToWrite(){ synchronized(this){ if(out == null || tc == null) return 0; else return out.buf.length - outOff; } } /** Writes one byte to the stream. @param octet The octet to be written. @return True on success, false if there is no free buffer. */ public boolean write(int octet){ synchronized(this){ if(out == null) return false; int place = outOff&0x3; int off = outOff>>2; int t = out.buf[off]; switch(place){ case 1: out.buf[off] = (t&0xff000000)&((octet&0xff)<<16); break; case 2: out.buf[off] = (t&0xff0000)&((octet&0xff)<<8); break; case 3: out.buf[off] = t&0xff00&(octet&0xff); break; default: out.buf[off] = octet<<24; break; } if(++outOff >= PacketPool.PACKET_SIZE()){ flush(); } return true; } } /** Opens a connections to with the given parameters. */ public boolean open(int remIp, int locIp, int remPort, int locPort){ return tc.open(remIp, locIp, remPort, locPort, this); } public int getStatus(){ return status; } /** Writes the whole content of a String to the stream. @param cs The CharSequence @return Number of bytes written (0 on failure). */ public int write(CharSequence cs){ if(out == null) return 0; int cnt = out.setData(outOff, cs); Dbg.wr("wrote "); Dbg.wr(cnt); Dbg.lf(); outOff += cnt; int nxtOff = outOff + 1; if(nxtOff >= PacketPool.PACKET_SIZE()){ flush(); } return cnt; } /** Ensures that all previous written data is sent to the remote host. */ public void flush(){ synchronized(this){ if(out != null){ out.setLen(outOff); // out.print(0); if(tc.send(out, true)){ out = null; Dbg.wr("sent ok\n"); } else{ Dbg.wr("sent failed! setting timeout\n"); timeout = (int)(System.currentTimeMillis() + 500); } } } } }