package lcm.lcm; import java.net.*; import java.io.*; import java.util.*; import java.util.concurrent.locks.*; import java.util.regex.*; import java.nio.*; public class TCPService { ServerSocket serverSocket; AcceptThread acceptThread; ArrayList<ClientThread> clients = new ArrayList<ClientThread>(); ReadWriteLock clients_lock = new ReentrantReadWriteLock(); int bytesCount = 0; public TCPService(int port) throws IOException { serverSocket = new ServerSocket(port); // sock.setReuseAddress(true); // sock.setLoopbackMode(false); // true *disables* loopback acceptThread = new AcceptThread(); acceptThread.start(); long inittime = System.currentTimeMillis(); long starttime = System.currentTimeMillis(); while (!Thread.interrupted()) { try { Thread.sleep(1000); } catch (InterruptedException ex) { break; } long endtime = System.currentTimeMillis(); double dt = (endtime - starttime) / 1000.0; starttime = endtime; System.out.printf("%10.3f : %10.1f kB/s, %d clients\n",(endtime - inittime)/1000.0, bytesCount/1024.0/dt, clients.size()); bytesCount = 0; } // interrupt signal received closeResources(); } private void closeResources() throws IOException { acceptThread.interrupt(); serverSocket.close(); synchronized(clients) { for (ClientThread clientThread : clients) { clientThread.closeResources(); } } } public void relay(byte channel[], byte data[]) { // synchronously send to all clients. String chanstr = new String(channel); try { clients_lock.readLock().lock(); for (ClientThread client : clients) { client.send(chanstr, channel, data); } } finally { clients_lock.readLock().unlock(); } } class AcceptThread extends Thread { public void run() { while (!Thread.interrupted()) { try { Socket clientSock = serverSocket.accept(); ClientThread client = new ClientThread(clientSock); client.start(); try { clients_lock.writeLock().lock(); clients.add(client); } finally { clients_lock.writeLock().unlock(); } } catch (IOException ex) { } } } } class ClientThread extends Thread { Socket sock; DataInputStream ins; DataOutputStream outs; class SubscriptionRecord { String regex; Pattern pat; SubscriptionRecord(String regex) { this.regex = regex; this.pat = Pattern.compile(regex); } } ArrayList<SubscriptionRecord> subscriptions = new ArrayList<SubscriptionRecord>(); ReadWriteLock subscriptions_lock = new ReentrantReadWriteLock(); public ClientThread(Socket sock) throws IOException { this.sock = sock; ins = new DataInputStream(sock.getInputStream()); outs = new DataOutputStream(sock.getOutputStream()); outs.writeInt(TCPProvider.MAGIC_SERVER); outs.writeInt(TCPProvider.VERSION); } public void run() { /////////////////////// // read messages until something bad happens. try { while (true) { int type = ins.readInt(); if (type == TCPProvider.MESSAGE_TYPE_PUBLISH) { int channellen = ins.readInt(); byte channel[] = new byte[channellen]; ins.readFully(channel); int datalen = ins.readInt(); byte data[] = new byte[datalen]; ins.readFully(data); TCPService.this.relay(channel, data); bytesCount += channellen + datalen + 8; } else if(type == TCPProvider.MESSAGE_TYPE_SUBSCRIBE) { int channellen = ins.readInt(); byte channel[] = new byte[channellen]; ins.readFully(channel); try { subscriptions_lock.writeLock().lock(); subscriptions.add(new SubscriptionRecord(new String(channel))); } finally { subscriptions_lock.writeLock().unlock(); } } else if(type == TCPProvider.MESSAGE_TYPE_UNSUBSCRIBE) { int channellen = ins.readInt(); byte channel[] = new byte[channellen]; ins.readFully(channel); String re = new String(channel); try { subscriptions_lock.writeLock().lock(); for(int i=0, n=subscriptions.size(); i<n; i++) { if(subscriptions.get(i).regex.equals(re)) { subscriptions.remove(i); break; } } } finally { subscriptions_lock.writeLock().unlock(); } } } } catch (IOException ex) { } /////////////////////// // Something bad happened, close this connection. try { closeResources(); } catch (IOException ex) { } try { clients_lock.writeLock().lock(); clients.remove(this); } finally { clients_lock.writeLock().unlock(); } } public void closeResources() throws IOException { sock.close(); } public void send(String chanstr, byte channel[], byte data[]) { try { subscriptions_lock.readLock().lock(); for(SubscriptionRecord sr : subscriptions) { if(sr.pat.matcher(chanstr).matches()) { synchronized(outs) { outs.writeInt(TCPProvider.MESSAGE_TYPE_PUBLISH); outs.writeInt(channel.length); outs.write(channel); outs.writeInt(data.length); outs.write(data); outs.flush(); return; } } } } catch (IOException ex) { } finally { subscriptions_lock.readLock().unlock(); } } } public static void main(String args[]) { try { int port = 7700; if (args.length > 0) port = Integer.parseInt(args[0]); new TCPService(port); } catch (IOException ex) { System.out.println("Ex: "+ex); } } }