Guest User

Untitled

a guest
Dec 14th, 2018
82
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.62 KB | None | 0 0
  1. /*
  2. * author: Max DeLiso <maxdeliso@gmail.com>
  3. * purpose: graphical UDP chat program
  4. */
  5.  
  6. package teflon;
  7.  
  8. import java.awt.BorderLayout;
  9. import java.awt.event.KeyEvent;
  10. import java.awt.event.KeyListener;
  11. import java.awt.event.WindowEvent;
  12. import java.awt.event.WindowListener;
  13. import java.io.IOException;
  14. import java.lang.reflect.InvocationTargetException;
  15. import java.net.DatagramPacket;
  16. import java.net.DatagramSocket;
  17. import java.net.InetAddress;
  18. import java.net.PortUnreachableException;
  19. import java.net.SocketException;
  20. import java.net.SocketTimeoutException;
  21. import java.net.UnknownHostException;
  22. import java.nio.channels.IllegalBlockingModeException;
  23. import java.nio.charset.Charset;
  24. import java.text.DateFormat;
  25. import java.util.Arrays;
  26. import java.util.Date;
  27. import java.util.LinkedList;
  28. import java.util.Queue;
  29.  
  30. import javax.swing.JButton;
  31. import javax.swing.JFrame;
  32. import javax.swing.JPanel;
  33. import javax.swing.JScrollPane;
  34. import javax.swing.JTextArea;
  35. import javax.swing.JTextField;
  36. import javax.swing.SwingUtilities;
  37.  
  38. class Teflon {
  39. public static final int TEFLON_PORT = 1337;
  40. public static final byte[] TEFLON_RECV_ADDRESS = new byte[] { 0, 0, 0, 0 };
  41. public static final byte[] TEFLON_SEND_ADDRESS = new byte[] { (byte) 0xff, (byte) 0xff,
  42. (byte) 0xff, (byte) 0xff };
  43. public static final int IO_TIMEOUT_MS = 50;
  44. public static final int INPUT_BUFFER_LEN = 1024;
  45. public static final Charset UTF8_CHARSET = Charset.forName("UTF-8");
  46.  
  47. private TeflonRemoteHandler remoteHandler;
  48. private TeflonLocalHandler localHandler;
  49. private boolean alive;
  50.  
  51. private static void reportException(Exception ex) {
  52. System.err.println("ERROR: " + Thread.currentThread() + " : " + ex);
  53. ex.printStackTrace();
  54. System.err.println("there was a fatal error. please report it. aborting.");
  55. System.exit(1);
  56. }
  57.  
  58. private static void debugMessage(String message) {
  59. System.out.println("DEBUG: " + Thread.currentThread() + " : " + message);
  60. }
  61.  
  62. public static void main(String args[]) {
  63. new Teflon();
  64. }
  65.  
  66. public static String decodeUTF8(byte[] bytes) {
  67. return new String(bytes, UTF8_CHARSET);
  68. }
  69.  
  70. public static byte[] encodeUTF8(String string) {
  71. return string.getBytes(UTF8_CHARSET);
  72. }
  73.  
  74. public TeflonRemoteHandler remote() {
  75. return remoteHandler;
  76. }
  77.  
  78. public TeflonLocalHandler local() {
  79. return localHandler;
  80. }
  81.  
  82. public synchronized boolean alive() {
  83. return alive;
  84. }
  85.  
  86. public synchronized void kill() {
  87. alive = false;
  88. }
  89.  
  90. public Teflon() {
  91. alive = new Boolean(true);
  92. remoteHandler = new TeflonRemoteHandler(this);
  93. localHandler = new TeflonLocalHandler(this);
  94. Thread remoteHandlerThread = new Thread(remoteHandler);
  95. Thread localHandlerThread = new Thread(localHandler);
  96.  
  97. remoteHandlerThread.start();
  98. localHandlerThread.start();
  99.  
  100. try {
  101. remoteHandlerThread.join();
  102. localHandlerThread.join();
  103. } catch (InterruptedException ie) {
  104. reportException(ie);
  105. }
  106.  
  107. debugMessage("main thread exiting");
  108. }
  109.  
  110. private class Message {
  111. public String id, body;
  112.  
  113. public Message(String id, String body) {
  114. this.id = id;
  115. this.body = body;
  116. }
  117.  
  118. @Override
  119. public String toString() {
  120. return id + ": " + body;
  121. }
  122. }
  123.  
  124. private interface CommDestiny {
  125. public void queueMessage(Message msg);
  126. }
  127.  
  128. @SuppressWarnings("serial")
  129. private class TeflonLocalHandler extends JFrame implements Runnable, CommDestiny {
  130. private static final int TEFLON_WIDTH = 512;
  131. private static final int TEFLON_HEIGHT = 316;
  132. private static final String TEFLON_TITLE = "Teflon";
  133.  
  134. private Queue<Message> sendQueue = new LinkedList<Message>();
  135. private JTextArea outputTextArea;
  136. private JTextField inputTextField;
  137. private JPanel headerPanel;
  138. private JButton helpButton;
  139. final private Teflon parent;
  140.  
  141. public TeflonLocalHandler(Teflon parent) {
  142. this.parent = parent;
  143. }
  144.  
  145. private KeyListener localKeyListener = new KeyListener() {
  146. @Override
  147. public void keyPressed(KeyEvent ke) {
  148. }
  149.  
  150. @Override
  151. public void keyReleased(KeyEvent ke) {
  152. if (ke.getKeyCode() == KeyEvent.VK_ENTER) {
  153. parent.remote().queueMessage(new Message("L", inputTextField.getText()));
  154.  
  155. inputTextField.setText("");
  156. }
  157. }
  158.  
  159. @Override
  160. public void keyTyped(KeyEvent ke) {
  161.  
  162. }
  163. };
  164.  
  165. private WindowListener localWindowListener = new WindowListener() {
  166. @Override
  167. public void windowActivated(WindowEvent we) {
  168. inputTextField.requestFocus();
  169. }
  170.  
  171. @Override
  172. public void windowClosed(WindowEvent we) {
  173. }
  174.  
  175. @Override
  176. public void windowClosing(WindowEvent we) {
  177. parent.kill();
  178. }
  179.  
  180. @Override
  181. public void windowDeactivated(WindowEvent we) {
  182. }
  183.  
  184. @Override
  185. public void windowDeiconified(WindowEvent we) {
  186. }
  187.  
  188. @Override
  189. public void windowIconified(WindowEvent we) {
  190. }
  191.  
  192. @Override
  193. public void windowOpened(WindowEvent we) {
  194. inputTextField.requestFocus();
  195. }
  196. };
  197.  
  198. private Runnable createInitRunnable() {
  199. final TeflonLocalHandler teflonLocalHandler = this;
  200.  
  201. return new Runnable() {
  202.  
  203. @Override
  204. public void run() {
  205. headerPanel = new JPanel();
  206. headerPanel.setLayout(new BorderLayout());
  207.  
  208. helpButton = new JButton("Help");
  209. headerPanel.add(BorderLayout.LINE_END, helpButton);
  210.  
  211. outputTextArea = new JTextArea();
  212. outputTextArea.setLineWrap(true);
  213. outputTextArea.setEditable(false);
  214.  
  215. inputTextField = new JTextField();
  216. inputTextField.addKeyListener(localKeyListener);
  217.  
  218. teflonLocalHandler.setSize(TEFLON_WIDTH, TEFLON_HEIGHT);
  219. teflonLocalHandler.setTitle(TEFLON_TITLE);
  220. teflonLocalHandler.setLayout(new BorderLayout());
  221.  
  222. teflonLocalHandler.addWindowListener(localWindowListener);
  223. teflonLocalHandler.add(BorderLayout.PAGE_START, headerPanel);
  224. teflonLocalHandler.add(BorderLayout.CENTER, new JScrollPane(outputTextArea));
  225. teflonLocalHandler.add(BorderLayout.PAGE_END, new JScrollPane(inputTextField));
  226.  
  227. teflonLocalHandler.setVisible(true);
  228. }
  229. };
  230. }
  231.  
  232. @Override
  233. public void run() {
  234. try {
  235. SwingUtilities.invokeAndWait(createInitRunnable());
  236. } catch (InterruptedException ie) {
  237. reportException(ie);
  238. } catch (InvocationTargetException ite) {
  239. reportException(ite);
  240. }
  241.  
  242. displayMessageWithDate(new Message("system", "started up"));
  243.  
  244. while (parent.alive()) {
  245. try {
  246. synchronized (sendQueue) {
  247. final Message msg = sendQueue.poll();
  248.  
  249. if (msg != null) {
  250. displayMessageWithDate(msg);
  251. }
  252. }
  253.  
  254. Thread.sleep(IO_TIMEOUT_MS);
  255. } catch (InterruptedException ie) {
  256. reportException(ie);
  257. }
  258. }
  259.  
  260. try {
  261. SwingUtilities.invokeAndWait(createDisposalRunnable());
  262. } catch (InterruptedException ie) {
  263. reportException(ie);
  264. } catch (InvocationTargetException ite) {
  265. reportException(ite);
  266. }
  267.  
  268. debugMessage("swing context destroyed, local handler thread exiting");
  269. }
  270.  
  271. private Runnable createDisposalRunnable() {
  272. final TeflonLocalHandler teflonLocalHandler = this;
  273.  
  274. return new Runnable() {
  275.  
  276. @Override
  277. public void run() {
  278. teflonLocalHandler.dispose();
  279. }
  280. };
  281. }
  282.  
  283. @Override
  284. public void queueMessage(Message msg) {
  285. debugMessage("in local handler thread with message: " + msg);
  286.  
  287. synchronized (sendQueue) {
  288. sendQueue.add(msg);
  289. }
  290. }
  291.  
  292. private void displayMessageWithDate(final Message msg) {
  293. SwingUtilities.invokeLater(new Runnable() {
  294. @Override
  295. public void run() {
  296. outputTextArea.append(DateFormat.getInstance().format(new Date()) + " : "
  297. + msg.toString() + "\n");
  298. }
  299. });
  300. }
  301. }
  302.  
  303. private class TeflonRemoteHandler implements Runnable, CommDestiny {
  304. private InetAddress listeningAddress;
  305. private DatagramSocket udpSocket;
  306. private Teflon parent;
  307. private Queue<Message> sendQueue = new LinkedList<Message>();
  308.  
  309. public TeflonRemoteHandler(Teflon parent) {
  310. this.parent = parent;
  311. }
  312.  
  313. private boolean init() {
  314. try {
  315. listeningAddress = InetAddress.getByAddress(TEFLON_RECV_ADDRESS);
  316. udpSocket = new DatagramSocket(TEFLON_PORT, listeningAddress);
  317. udpSocket.setBroadcast(true);
  318. udpSocket.setSoTimeout(IO_TIMEOUT_MS);
  319. } catch (UnknownHostException uhe) {
  320. reportException(uhe);
  321. return false;
  322. } catch (SocketException soe) {
  323. reportException(soe);
  324. return false;
  325. } catch (SecurityException se) {
  326. reportException(se);
  327. return false;
  328. }
  329.  
  330. return true;
  331. }
  332.  
  333. @Override
  334. public void run() {
  335. if (!init()) {
  336. parent.kill();
  337. return;
  338. }
  339.  
  340. byte[] inputBuffer = new byte[INPUT_BUFFER_LEN];
  341. DatagramPacket inputDatagram = new DatagramPacket(inputBuffer, INPUT_BUFFER_LEN);
  342.  
  343. while (parent.alive()) {
  344. try {
  345. udpSocket.receive(inputDatagram);
  346.  
  347. debugMessage("received block with offset/length of: " + inputDatagram.getOffset()
  348. + " / " + inputDatagram.getLength());
  349.  
  350. /* TODO: verify this, it smells a little funny */
  351. final byte[] messageBytes = Arrays.copyOfRange(inputDatagram.getData(),
  352. inputDatagram.getOffset(),
  353. inputDatagram.getOffset() + inputDatagram.getLength());
  354.  
  355. parent.local().queueMessage(new Message("", decodeUTF8(messageBytes)));
  356. } catch (IllegalBlockingModeException ibme) {
  357. reportException(ibme);
  358. } catch (PortUnreachableException pue) {
  359. reportException(pue);
  360. } catch (SocketTimeoutException ste) {
  361.  
  362. } catch (IOException ioe) {
  363. reportException(ioe);
  364. }
  365.  
  366. synchronized (sendQueue) {
  367. Message msg = sendQueue.poll();
  368.  
  369. if (msg != null) {
  370. byte[] encodedMessage = encodeUTF8(msg.toString());
  371.  
  372. try {
  373. DatagramPacket outgoingPacket = new DatagramPacket(encodedMessage,
  374. encodedMessage.length, InetAddress.getByAddress(TEFLON_SEND_ADDRESS),
  375. TEFLON_PORT);
  376.  
  377. udpSocket.send(outgoingPacket);
  378. } catch (UnknownHostException uhe) {
  379. reportException(uhe);
  380. } catch (IOException ioe) {
  381. reportException(ioe);
  382. }
  383. }
  384. }
  385.  
  386. }
  387.  
  388. udpSocket.close();
  389. debugMessage("remote handler thread exiting");
  390. }
  391.  
  392. @Override
  393. public void queueMessage(Message msg) {
  394. synchronized (sendQueue) {
  395. sendQueue.add(msg);
  396. }
  397. }
  398. }
  399. }
Add Comment
Please, Sign In to add comment