Java Source Code: ttt.UDPServer


   1: /*
   2:  * TeleTeachingTool - Platform-independent recording and transmission of 
   3:  * arbitrary content
   4:  * 
   5:  * Copyright (C) 2003 by Peter Ziewer
   6:  *
   7:  * This program is free software; you can redistribute it and/or
   8:  * modify it under the terms of the GNU General Public License
   9:  * as published by the Free Software Foundation; either version 2
  10:  * of the License, or (at your option) any later version.
  11:  *
  12:  * This program is distributed in the hope that it will be useful,
  13:  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15:  * GNU General Public License for more details.
  16:  * 
  17:  * You should have received a copy of the GNU General Public License
  18:  * along with this program; if not, write to the Free Software
  19:  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  20:  */
  21: package ttt;
  22: 
  23: import java.io.DataInputStream;
  24: import java.io.IOException;
  25: import java.io.OutputStream;
  26: import java.net.DatagramPacket;
  27: import java.net.InetAddress;
  28: import java.net.MulticastSocket;
  29: import java.net.UnknownHostException;
  30: import java.util.ArrayList;
  31: import java.util.zip.Deflater;
  32: 
  33: /**
  34:  * @author Peter Ziewer (University of Trier,Germany)
  35:  * 
  36:  * ttt server for udp clients
  37:  */
  38:	  public class UDPServer extends Thread {
  39:
  40:    //    Protocol protocol;
  41:    Connection connection;
  42:    DataInputStream in;
  43:    OutputStream out;
  44:    MulticastSocket socket;
  45:
  46:    int aliveControllerPort;
  47:
  48:    ArrayList clients = new ArrayList();
  49:
  50:    byte[][] updates = new byte[24][];
  51:
  52:    /**
  53:     * Constructor for UDPOutputServer.
  54:     */
  55:	      UDPServer(Protocol protocol) throws IOException {
  56:        connection = protocol.getConnection();
  57:        in = connection.in;
  58:        out = connection.out;
  59:
  60:        socket = new MulticastSocket();
  61:        socket.setSendBufferSize(1024 * 64);
  62:        socket.setTimeToLive(Parameter.serverParameter.getTimeToLive());
  63:        System.out.println("UDP Socket SendBufferSize: " + socket.getSendBufferSize());
  64:
  65:        // start alive controller
  66:        new AliveController(this);
  67:
  68:        // start audio/video transmitter
  69:        if (Parameter.serverParameter.getTransmitAudio())
  70://            && !Parameter.serverParameter.getTransmitLocalDevices())
  71:            new UDPTransmitter(
  72:                this,
  73:                Parameter.serverParameter.getAudioMulticastGroup(),
  74:                Parameter.serverParameter.getAudioMulticastPort());
  75:        if (Parameter.serverParameter.getTransmitVideo())
  76://            && !Parameter.serverParameter.getTransmitLocalDevices())
  77:            new UDPTransmitter(
  78:                this,
  79:                Parameter.serverParameter.getVideoMulticastGroup(),
  80:                Parameter.serverParameter.getVideoMulticastPort());
  81:        
  82:        start();
  83:    }
  84:
  85:	      void setTimeToLive(int timeToLive) {
  86:	          try {
  87:            socket.setTimeToLive(timeToLive);
  88:        } catch (IOException e) {
  89:            System.out.println("Set time to live failed.");
  90:            System.out.println("    Error: " + e);
  91:        }
  92:    }
  93:
  94:	      int getClientNumber(String host) {
  95:	          try {
  96:            InetAddress client = InetAddress.getByName(host);
  97:	              for (int i = 0; i < clients.size(); i++) {
  98:                if (((InetAddressWithTimestamp) clients.get(i)).address.equals(client))
  99:                    return i;
 100:            }
 101:        } catch (UnknownHostException e) {
 102:            System.out.println("Unknown UDP client: " + host);
 103:            System.out.println("    Error: " + e);
 104:        }
 105:
 106:        return -1;
 107:    }
 108:
 109:	      boolean addClient(String host) {
 110:        if (getClientNumber(host) >= 0)
 111:            // client is listed
 112:            return true;
 113:
 114:	          try {
 115:            InetAddressWithTimestamp client =
 116:                new InetAddressWithTimestamp(InetAddress.getByName(host));
 117:            clients.add(client);
 118:            System.out.println(
 119:                "UDP client added: "
 120:                    + client.address.getHostName()
 121:                    + (client.address.isMulticastAddress() ? " (multicast)" : " (unicast)"));
 122:        } catch (UnknownHostException e) {
 123:            System.out.println("Failed to add UDP client: " + host);
 124:            System.out.println("    Error: " + e);
 125:            return false;
 126:        }
 127:
 128:        return true;
 129:    }
 130:
 131:	      void update(InetAddress address) {
 132:	          for (int i = 0; i < clients.size(); i++) {
 133:	              if (((InetAddressWithTimestamp) clients.get(i)).address.equals(address)) {
 134:                ((InetAddressWithTimestamp) clients.get(i)).timestamp = System.currentTimeMillis();
 135:            }
 136:        }
 137:    }
 138:
 139:	      void remove(InetAddress address) {
 140:	          for (int i = 0; i < clients.size(); i++) {
 141:	              if (((InetAddressWithTimestamp) clients.get(i)).address.equals(address)) {
 142:                clients.remove(i);
 143:                System.out.println(
 144:                    "UDP client removed: "
 145:                        + address.getHostName()
 146:                        + (address.isMulticastAddress() ? " (multicast)" : " (unicast)"));
 147:                break;
 148:            }
 149:        }
 150:    }
 151:
 152:	      void controlClients() {
 153:        //        System.out.println("\nControl Clients");
 154:        long now = System.currentTimeMillis();
 155:        int i = 0;
 156:        //        System.out.println("\nControlling "+clients.size()+" clients.");
 157:	          while (i < clients.size()) {
 158:            InetAddressWithTimestamp client = (InetAddressWithTimestamp) clients.get(i);
 159:            //            System.out.println(i+". "+client.address.getHostName()+" timestamp "+new java.util.Date(client.timestamp));
 160:	              if (client.timestamp + 185000 < now && !client.address.isMulticastAddress()) {
 161:                Date today = new java.util.Date();
 162:                System.out.println(
 163:                    "Removing " + client.address.getHostName() + " (time out) " + today);
 164:                System.out.println("  last alive message: " + new java.util.Date(client.timestamp));
 165:                System.out.println();
 166:                clients.remove(i);
 167:            } else
 168:                i++;
 169:        }
 170:    }
 171:
 172:    boolean newClient;
 173:
 174:	      public void run() {
 175:        byte[] msg = new byte[1024 * 70];
 176:        DatagramPacket packet;
 177:        InetAddressWithTimestamp client;
 178:        int length = 1; // reserve first byte for header
 179:
 180:	          try {
 181:            // buffer messages until buffer is full
 182:            // or an empty message indicates flush()
 183:	              while (true) {
 184:                // read size of next message
 185:                int size = in.readInt();
 186:
 187:	                  if (size == 0 || length + 4 + size > 65000) {
 188:                    // buffer full or flush message
 189:                    // send buffered messages
 190:                    if (length > 1)
 191:                        send(msg, length);
 192:
 193:                    // clean buffer
 194:                    length = 1;
 195:
 196:                    // drop flush message
 197:                    while (size == 0)
 198:                        size = in.readInt();
 199:                }
 200:
 201:                int encoding = in.readUnsignedByte();
 202:                //                System.out.println("Enc "+(encoding&127)+((encoding&Protocol.EncodingTimestamp)!=0 ? " + timestamp":""));
 203:
 204:                // remove timestamps (not needed for online transmission)
 205:	                  if ((encoding & Protocol.EncodingTimestamp) != 0) {
 206:                    in.skipBytes(4);
 207:                    size -= 4;
 208:                    encoding &= 127;
 209:                }
 210:
 211:                // buffer message size
 212://                int startPos = length;
 213:                msg[length++] = (byte) (size >> 24 & 0xff);
 214:                msg[length++] = (byte) (size >> 16 & 0xff);
 215:                msg[length++] = (byte) (size >> 8 & 0xff);
 216:                msg[length++] = (byte) (size & 0xff);
 217:
 218:                // buffer encoding
 219:                msg[length++] = (byte) (encoding & 0xff);
 220:
 221:                // fix size
 222:                size--;
 223:
 224:                // buffer message
 225:                in.readFully(msg, length, size);
 226:                length += size;
 227:
 228:                // store non incremental updates                
 229:                if (encoding == Protocol.EncodingHextile
 230:                    && (0
 231:                        == (getUnsigned(msg[length - size])
 232:	                              << 8 | getUnsigned(msg[length - size + 1])))) {
 233:                    int pos = length - (size);
 234:
 235:                    int x = getUnsigned(msg[pos++]) << 8 | getUnsigned(msg[pos++]);
 236:                    int y = getUnsigned(msg[pos++]) << 8 | getUnsigned(msg[pos++]);
 237:                    int w = getUnsigned(msg[pos++]) << 8 | getUnsigned(msg[pos++]);
 238:                    int h = getUnsigned(msg[pos++]) << 8 | getUnsigned(msg[pos++]);
 239:
 240:                    // TODO: enable variable partion size (now only fixed size (24) works)
 241:                    if (w == connection.framebufferWidth
 242:	                          && h == connection.framebufferHeight / 24) {
 243:                        int index = y / (connection.framebufferHeight / 24);
 244://                        System.out.println("Hex: at ("+x+","+y+")\tw "+w+" h "+h+" #"+index+"\tsize: "+(size+5));
 245:	                          if(size<20000) {
 246:                            updates[index] = new byte[size + 5 + 1];
 247:                            System.arraycopy(msg, length - (size + 5), updates[index], 1, size + 5);
 248:                            // set update flag
 249:                            updates[index][5] |= Protocol.EncodingUpdate;
 250:                        }else{
 251:                            // drop huge update
 252:                            updates[index] = null;
 253:                        }
 254:
 255:                        // send updates if new client has connected
 256:	                          if (newClient) {
 257:                            //                    System.out.println("new client");
 258:	                              for (int i = 0; i < 24; i++) {
 259:	                                  if (updates[i] != null) {
 260:                                    //                            System.out.print("send #"+i+"  "+updates[i].length);
 261:                                    send(updates[i], updates[i].length);
 262:                                }
 263:                            }
 264:                            newClient = false;
 265:                        }
 266:                    }
 267:                }
 268:            }
 269:        } catch (IOException e) {
 270:            System.out.println("UDPOutput Error: " + e);
 271:        }
 272:    }
 273:
 274:	      static int getUnsigned(byte signed) {
 275:        return (signed & 127) | (signed & 128);
 276:    }
 277:
 278:    byte[] packed = new byte[1024 * 64];
 279:    Deflater def = new Deflater(Deflater.BEST_SPEED);
 280:    byte count;
 281:    final static byte Packed = (byte) 128;
 282:
 283:    //    Random random = new Random(System.currentTimeMillis());
 284:
 285:	      void send(byte[] unpacked, int size) throws IOException {
 286:        byte[] msg = unpacked;
 287:
 288:        // packet counter
 289:        count++;
 290:	          if (count < 0) {
 291:            count = 0;
 292:            controlClients();
 293:        }
 294:
 295:        // msg size without header
 296:        size--;
 297:
 298:        msg[0] = count;
 299:        int packedSize = 0;
 300:
 301:        //        System.out.println(count + ".\t" + size);
 302:
 303:        // don't pack small buffer
 304:	          if (size > 100) {
 305:            // pack buffer
 306:            def.reset();
 307:            def.setInput(unpacked, 1, size);
 308:            def.finish();
 309:            packedSize = def.deflate(packed, 1, packed.length - 1);
 310:
 311:            // compare sizes
 312:	              if (def.finished() && packedSize < size) {
 313:                // send packed buffer
 314:                msg = packed;
 315:                msg[0] = (byte) (count | Packed);
 316:                size = packedSize;
 317:            }
 318:        }
 319:        //        System.out.println("\t-> " + size + ((msg[0] & Packed) == 0 ? "" : " packed"));
 320:
 321:        // msg size plus 1 byte header
 322:        size++;
 323:
 324:        //        if(newClient) System.out.println(" -> "+size);
 325:        sendToAll(msg, size);
 326:    }
 327:
 328:	      void sendToAll(byte[] msg, int size) throws IOException {
 329:        sendToAll(msg, size, Parameter.serverParameter.getDesktopMulticastPort(), true, true);
 330:    }
 331:
 332:    synchronized void sendToAll(byte[] msg, int size, int port, boolean unicastEnabled, boolean multicastEnabled)
 333:	          throws IOException {
 334:        // create packet
 335:        DatagramPacket packet = new DatagramPacket(msg, 0, size);
 336:
 337:        // send to all clients
 338:	          for (int i = 0; i < clients.size(); i++) {
 339:            // send to client
 340:            InetAddressWithTimestamp client = (InetAddressWithTimestamp) clients.get(i);
 341:            
 342://            System.out.print(client.address+":"+port);
 343:            
 344:            if ((multicastEnabled && client.address.isMulticastAddress())
 345:	              || (unicastEnabled && !client.address.isMulticastAddress())) {
 346:                packet.setAddress(client.address);
 347:                packet.setPort(port);            
 348:                socket.send(packet);
 349://                System.out.println(" - send");
 350://                if(Parameter.debug) System.out.println(client.address.getHostName()+":"+port+" - "+packet.getLength()+" bytes");
 351:            }
 352://            else System.out.println(" - NOT send");
 353:
 354:        }
 355:    }
 356:}
 357:	  class InetAddressWithTimestamp {
 358:    InetAddress address;
 359:    long timestamp;
 360:
 361:	      InetAddressWithTimestamp(InetAddress address) {
 362:        this.address = address;
 363:        timestamp = System.currentTimeMillis();
 364:    }
 365:}