/** * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ * * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). * * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation; either version 3.0 of the License, or (at your option) any later * version. * * BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along * with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. * */ package org.bigbluebutton.deskshare.client.net; import java.awt.Point; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.Socket; import java.net.UnknownHostException; import java.util.Vector; import org.bigbluebutton.deskshare.client.ExitCode; import org.bigbluebutton.deskshare.common.Dimension; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; public class NetworkSocketStreamSender implements Runnable { private Socket socket = null; private DataOutputStream outstream = null; private String room; private Dimension screenDim; private Dimension blockDim; private boolean useSVC2; private final NextBlockRetriever retriever; private volatile boolean processMessages = false; private final int id; private NetworkStreamListener listener; private final SequenceNumberGenerator seqNumGenerator; private SSLSocket sslSocket = null; private SSLSocketFactory sslSocketFactory = null ; private Boolean useTLS = false; public NetworkSocketStreamSender(int id, NextBlockRetriever retriever, String room, Dimension screenDim, Dimension blockDim, SequenceNumberGenerator seqNumGenerator, boolean useSVC2) { this.id = id; this.retriever = retriever; this.room = room; this.screenDim = screenDim; this.blockDim = blockDim; this.seqNumGenerator = seqNumGenerator; this.useSVC2 = useSVC2; } public void addListener(NetworkStreamListener listener) { this.listener = listener; } private void notifyNetworkStreamListener(ExitCode reason) { if (listener != null) listener.networkException(id,reason); } public void connect(String host, int port , boolean useTLS ) throws ConnectionException { //We use this value to devie how to create the socket this.useTLS = useTLS; System.out.println("NetworkSocketStreamSender: connecting to " + host + ":" + port); try { //Creating the ssl socket //Host and port should point to Stunnel or the TLS terminating service //Handling if TLS is enabled or not if(useTLS){ System.out.println("Connecting over TLS"); sslSocketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault(); sslSocket = (SSLSocket) sslSocketFactory.createSocket(host, port); outstream = new DataOutputStream(sslSocket.getOutputStream()); } else{ //If not use regular socket socket = new Socket(host, port); outstream = new DataOutputStream(socket.getOutputStream()); } } catch (UnknownHostException e) { e.printStackTrace(); throw new ConnectionException("UnknownHostException: " + host); } catch (IOException e) { e.printStackTrace(); throw new ConnectionException("IOException: " + host + ":" + port); } } public void sendStartStreamMessage() throws ConnectionException { try { ByteArrayOutputStream dataToSend = new ByteArrayOutputStream(); dataToSend.reset(); BlockStreamProtocolEncoder.encodeStartStreamMessage(room, screenDim, blockDim, dataToSend, seqNumGenerator.getNext(), useSVC2); BlockStreamProtocolEncoder.encodeDelimiter(dataToSend); sendHeader(BlockStreamProtocolEncoder.encodeHeaderAndLength(dataToSend)); sendToStream(dataToSend); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private void sendCursor(Point mouseLoc, String room) throws IOException { ByteArrayOutputStream dataToSend = new ByteArrayOutputStream(); dataToSend.reset(); BlockStreamProtocolEncoder.encodeMouseLocation(mouseLoc, room, dataToSend, seqNumGenerator.getNext()); BlockStreamProtocolEncoder.encodeDelimiter(dataToSend); sendHeader(BlockStreamProtocolEncoder.encodeHeaderAndLength(dataToSend)); sendToStream(dataToSend); } private void sendBlock(BlockVideoData block) throws IOException { ByteArrayOutputStream dataToSend = new ByteArrayOutputStream(); dataToSend.reset(); BlockStreamProtocolEncoder.encodeBlock(block, dataToSend, seqNumGenerator.getNext()); sendHeader(BlockStreamProtocolEncoder.encodeHeaderAndLength(dataToSend)); sendToStream(dataToSend); } private void sendHeader(byte[] header) throws IOException { if (outstream != null) outstream.write(header); } private void sendToStream(ByteArrayOutputStream dataToSend) throws IOException { if (outstream != null) dataToSend.writeTo(outstream); } public void disconnect() throws ConnectionException { System.out.println("Disconnecting socket stream"); if (!processMessages) return; } private void processNextMessageToSend(Message message) throws IOException { if (message.getMessageType() == Message.MessageType.BLOCK) { long start = System.currentTimeMillis(); ByteArrayOutputStream dataToSend = new ByteArrayOutputStream(); dataToSend.reset(); BlockStreamProtocolEncoder.encodeRoomAndSequenceNumber(room, seqNumGenerator.getNext(), dataToSend); Integer[] changedBlocks = ((BlockMessage)message).getBlocks(); BlockStreamProtocolEncoder.numBlocksChanged(changedBlocks.length, dataToSend); String blockSize = "Block length ["; String encodeTime = "Encode times ["; long encStart = 0; long encEnd = 0; int totalBytes = 0; long totalMillis = 0; for (int i = 0; i < changedBlocks.length; i++) { encStart = System.currentTimeMillis(); EncodedBlockData block = retriever.getBlockToSend((Integer)changedBlocks[i]); totalBytes += block.getVideoData().length; blockSize += block.getVideoData().length + ","; encEnd = System.currentTimeMillis(); totalMillis += (encEnd - encStart); encodeTime += (encEnd - encStart) + ","; BlockVideoData bv = new BlockVideoData(room, block.getPosition(), block.getVideoData(), false /* should remove later */); BlockStreamProtocolEncoder.encodeBlock(bv, dataToSend); } // System.out.println(blockSize + "] total=" + totalBytes + " bytes"); // System.out.println(encodeTime + "] total=" + totalMillis + " ms"); BlockStreamProtocolEncoder.encodeDelimiter(dataToSend); sendHeader(BlockStreamProtocolEncoder.encodeHeaderAndLength(dataToSend)); sendToStream(dataToSend); for (int i = 0; i< changedBlocks.length; i++) { retriever.blockSent((Integer)changedBlocks[i]); } long end = System.currentTimeMillis(); // System.out.println("[Socket Thread " + id + "] Sending " + changedBlocks.length + " blocks took " + (end - start) + " millis"); } else if (message.getMessageType() == Message.MessageType.CURSOR) { CursorMessage msg = (CursorMessage)message; sendCursor(msg.getMouseLocation(), msg.getRoom()); } else if (message.getMessageType() == Message.MessageType.POISON) { System.out.println("Received poison message."); try { ByteArrayOutputStream dataToSend = new ByteArrayOutputStream(); dataToSend.reset(); BlockStreamProtocolEncoder.encodeEndStreamMessage(room, dataToSend, seqNumGenerator.getNext()); BlockStreamProtocolEncoder.encodeDelimiter(dataToSend); sendHeader(BlockStreamProtocolEncoder.encodeHeaderAndLength(dataToSend)); sendToStream(dataToSend); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { processMessages = false; System.out.println("Disconnected socket stream"); } } } public void run() { processMessages = true; while (processMessages) { Message message; try { message = retriever.getNextMessageToSend(); processNextMessageToSend(message); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); processMessages = false; notifyNetworkStreamListener(ExitCode.CONNECTION_TO_DESKSHARE_SERVER_DROPPED); } } try { outstream.close(); outstream = null; if(this.useTLS){ sslSocket.close(); }else{ socket.close(); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }