package javaforce.voip; /** SIP TCP Transport Server * * This is very complex since this transport needs to track all endpoints. * * @author pquiring * * Created : Jan 29, 2014 */ import java.net.*; import java.io.*; import java.util.*; import javaforce.*; public class SIPTCPTransportServer implements SIPTransport { protected ServerSocket ss; private HashMap<String, Socket> clients = new HashMap<String, Socket>(); private Object clientsLock = new Object(); private boolean active; public String getName() { return "TCP"; } public boolean open(int localport) { try { ss = new ServerSocket(localport); active = true; new WorkerAccepter().start(); } catch (Exception e) { JFLog.log(e); return false; } return true; } public boolean close() { active = false; try { if (ss != null) { ss.close(); ss = null; } } catch (Exception e) { JFLog.log(e); } return true; } public boolean send(byte[] data, int off, int len, InetAddress host, int port) { String id = host.toString() + ":" + port; Socket socket; synchronized(clientsLock) { socket = clients.get(id); } try { if (socket == null) socket = connect(host, port, id); OutputStream os = socket.getOutputStream(); os.write(data, off, len); } catch (Exception e) { JFLog.log(e); return false; } return true; } private ArrayList<SIP.Packet> packets = new ArrayList<SIP.Packet>(); private Object packetsLock = new Object(); public boolean receive(SIP.Packet packet) { while (true) { synchronized(packetsLock) { if (packets.size() == 0) { try{packetsLock.wait();} catch (Exception e) {} } // if (packets.size() == 0) continue; SIP.Packet tmp = packets.remove(0); packet.data = tmp.data; packet.host = tmp.host; packet.length = tmp.length; packet.port = tmp.port; return true; } } } private Socket connect(InetAddress host, int port, String id) throws Exception { Socket socket = new Socket(host, port); synchronized(clientsLock) { clients.put(id, socket); } return socket; } protected class WorkerAccepter extends Thread { public void run() { //accepts inbound connections while (active) { try { Socket socket = ss.accept(); InetAddress host = socket.getInetAddress(); int port = socket.getPort(); String id = host.toString() + ":" + port; synchronized(clientsLock) { clients.put(id, socket); } new WorkerReader(socket, id, host, port).start(); } catch (Exception e) { JFLog.log(e); } } } } private class WorkerReader extends Thread { private Socket socket; private String id; private InputStream is; private InetAddress host; private int port; public WorkerReader(Socket socket, String id, InetAddress host, int port) { this.socket = socket; this.id = id; this.host = host; this.port = port; } public void run() { //reads packets from client process(); synchronized(clientsLock) { clients.remove(id); } } private byte extra[] = null; private int detectLength(byte data[], int off, int len) { for(int a=0;a<len-3;a++) { if ( (data[off+a+0] == '\r') && (data[off+a+1] == '\n') && (data[off+a+2] == '\r') && (data[off+a+3] == '\n') ) { return a+4; } } return -1; } public void process() { try { is = socket.getInputStream(); } catch (Exception e) { JFLog.log(e); return; } while (active) { try { byte data[] = new byte[1500]; SIP.Packet packet = new SIP.Packet(); packet.data = data; if (extra != null) { System.arraycopy(extra, 0, packet.data, 0, extra.length); packet.length = extra.length; extra = null; } else { packet.length = is.read(packet.data); } int plen, tlen; do { //detect end of packet (double \r\n) plen = detectLength(packet.data, 0, packet.length); if (plen == -1) { //not enough read (frag?) packet.length += is.read(packet.data, packet.length, packet.data.length - packet.length); } } while (plen == -1); tlen = plen; //now find Content-Length: String msg[] = new String(packet.data, 0, plen).split("\r\n"); String clenstr = SIP.getHeader("Content-Length:", msg); if (clenstr == null) SIP.getHeader("l:", msg); if (clenstr != null) { int clen = JF.atoi(clenstr); tlen += clen; } while (packet.length < tlen) { //not enough read (frag?) packet.length += is.read(packet.data, packet.length, packet.data.length - packet.length); } if (packet.length > tlen) { //extra read (from next packet) extra = new byte[packet.length - tlen]; System.arraycopy(packet.data, tlen, extra, 0, packet.length - tlen); packet.length = tlen; } //host and port never change packet.host = host.getHostAddress(); packet.port = port; synchronized(packetsLock) { packets.add(packet); packetsLock.notify(); } } catch (Exception e) { JFLog.log(e); } } } } }