Свой сокет-чат на JAVA и FLASH, без БД!

Уроки по PHP, Javascript и т.п.

Свой сокет-чат на JAVA и FLASH, без БД!

Сообщение sasha^ » 12 окт 2013, 10:55

Здравствуйте, друзья! :)

Небольшой пролог:
Сегодня я напишу урок по создаю чата на JAVA и FLASH... Для обмена сообщениями мы будем использовать бинарный сокет(flash.net.Socket)... Сервер будет хранить все данные в оперативной памяти, потому БД нам вообще не нужно!
Думаю, некоторые, форумчане знают, что я занят изучением создания сокет-сервера и клиента в связке java+as3.. Эти знания я буду использовать для создания pvp и multiplayer-игр!
Ну и решил написать этот урок, поделиться заработанными знаниями... Приятного чтения и изучения ;)
P.S. Также, в скором времени займусь изучением 3д на флеше, потому, может, и по этой теме буду выкладывать уроки...)
Возможно еще заведу свой блог=)

Уровень знаний должен быть средний или приблизительно такой... Новичкам этот урок, пожалуй, ни к чему и сложноват...

Что нам нужно:
Для компиляции:
1) jdk(для компиляции java) - можно скачать с официального сайта http://www.oracle.com/technetwork/java/javase/downloads/index.html
2) adobe flash или другой компилятор as3-кода, но это, думаю, у вас уже должно быть))

Для запуска:(чтобы любой мог присоединится)
1) выделенный IP у вас на компе, если нету VPS(я именно VPS и взял, т.к. у меня и, думаю, еще у многих ip компа динамический) + программа для ssh-доступа(я использую putty)
2) установленная jvm(вместе с jdk идет..)
3) запустить сокет-сервер на 843 порту(это главный адресс для получения политики безопасности для флеша..), он будет на perl

Итак, начнем...

Создаем клиент...

1. Создаем Main.as-файл и туда вносим этот код(полностью прокомментирован):
Меняем только на 31-33 строчках ip(домен) сервера и порт(если захотите другой...)
  1. package {
  2.     import flash.events.*;
  3.     import flash.display.*;
  4.     import flash.net.*;
  5.     import flash.utils.*;
  6.     import flash.text.*;
  7.     import flash.ui.*;
  8.     import flash.system.*;
  9.    
  10.     public class Main extends Sprite {
  11.         // цвет ошибка
  12.         public static const ERROR_COLOR:uint = 0x8e3b1a;
  13.         // цвет нормального
  14.         public static const NORMAL_COLOR:uint = 0x1d6a96;
  15.         // цвет успеного
  16.         public static const GOOD_COLOR:uint = 0x126114;
  17.        
  18.         // цвет имени пользователей
  19.         public static const USER_COLOR:uint = 0xa25226;
  20.         // цвет самого себя в чате...
  21.         public static const USER_I:uint = 0x99D43C;
  22.         // цвет сообщения...
  23.         public static const MESSAGE_COLOR:uint = 0x474747;
  24.        
  25.         // сейчас читаем длину...
  26.         public static const READ_LENGTH:uint = 0;
  27.         // сейчас читаем сообщение...
  28.         public static const READ_MESSAGE:uint = 1;
  29.        
  30.         // сервер, к которому будем подключатся...
  31.         public static const HOST:String = "144.76.161.242";
  32.         // порт для подключения...
  33.         public static const PORT:uint = 17030;
  34.        
  35.         // флеш варс
  36.         private var _flashVars:Object;
  37.         // сокет...
  38.         private var _socket:Socket;
  39.         // поле чата...
  40.         private var _chatField:TextField;
  41.         // поле для ввода...
  42.         private var _messageField:TextField;
  43.         // массив байтов сообщений
  44.         private var _buffer:ByteArray = new ByteArray();
  45.         // состояние чтения...
  46.         private var _state:uint = Main.READ_LENGTH;
  47.         // последняя прочитанная длина...
  48.         private var _length:int = 0;
  49.         // авторизованы ли....
  50.         private var _authorized:Boolean;
  51.        
  52.        
  53.         // конструктор
  54.         public function Main() {
  55.             if(this.stage != null) startChat();
  56.             else addEventListener(Event.ADDED_TO_STAGE, onAdded);
  57.         }
  58.    
  59.         // когда добавлен в список отображения
  60.         private function onAdded(e:Event) {
  61.             removeEventListener(Event.ADDED_TO_STAGE, onAdded);
  62.             startChat();
  63.         }
  64.        
  65.         // запуск игры...
  66.         private function startChat(e:Event = null):void {
  67.             // получаем данные о юзере...
  68.             _flashVars = this.stage.loaderInfo.parameters as Object;
  69.            
  70.             // создаем текстовое поле и добавляем в список отображения
  71.             _chatField = new TextField();
  72.             _chatField.width = 500;
  73.             _chatField.height = 400;
  74.             _chatField.border = true;
  75.             _chatField.x = _chatField.y = 10;
  76.             _chatField.background = true;
  77.             _chatField.backgroundColor = 0xf8f8f8;
  78.             _chatField.wordWrap = true;
  79.             addChild(_chatField);
  80.            
  81.             // создаем поле для ввода и отправки текста...
  82.             _messageField = new TextField();
  83.             _messageField.width = 500;
  84.             _messageField.height = 30;
  85.             _messageField.border = true;
  86.             _messageField.x = 10;
  87.             _messageField.y = 420;
  88.             _messageField.background = true;
  89.             _messageField.backgroundColor = 0xf9f9f9;
  90.             _messageField.type = TextFieldType.INPUT;
  91.             _messageField.defaultTextFormat = new TextFormat(null,22,0x000000);
  92.             addChild(_messageField);
  93.            
  94.             // слушатель события для ентер...
  95.             this.stage.addEventListener(KeyboardEvent.KEY_DOWN, keyboardListener);
  96.            
  97.             // создаем сокет и назначаем слушатели событий...
  98.             _socket = new Socket();
  99.             _socket.addEventListener(Event.CONNECT, connectListener);
  100.             _socket.addEventListener(Event.CLOSE, closeListener);
  101.             _socket.addEventListener(ProgressEvent.SOCKET_DATA, dataListener);
  102.             _socket.addEventListener(IOErrorEvent.IO_ERROR, errorListener);
  103.             _socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorListener);
  104.            
  105.             // укажем явно чтобы грузить файл политики(perl-сокет)
  106.             Security.loadPolicyFile("xmlsocket://144.76.161.242:843");
  107.            
  108.             // пытаемся подключится...
  109.             try {
  110.                 _socket.connect(Main.HOST, Main.PORT);
  111.             } catch(error:Error) {
  112.                 showStatusMessage("Ошибка подключения :(", Main.ERROR_COLOR);
  113.             }
  114.         }
  115.        
  116.         // присоединились
  117.         private function connectListener(e:Event):void {
  118.             showStatusMessage("Соединение установлено!", Main.GOOD_COLOR);
  119.            
  120.             // авторизуемся...
  121.             sendAuthorize();
  122.         }
  123.        
  124.         // отключились
  125.         private function closeListener(e:Event):void {
  126.             showStatusMessage("Соединение закрыто.", Main.NORMAL_COLOR);
  127.         }
  128.        
  129.         // прибыли данные
  130.         private function dataListener(e:ProgressEvent):void {
  131.             // читаем данные
  132.             _socket.readBytes(_buffer, _buffer.length, _socket.bytesAvailable);
  133.             trace(_buffer);
  134.             // читаем или длину или сообщение...
  135.             switch(_state) {
  136.                 case Main.READ_LENGTH:
  137.                     readLength();
  138.                     break;
  139.                    
  140.                 case Main.READ_MESSAGE:
  141.                     readMessage();
  142.                     break;
  143.             }
  144.         }
  145.        
  146.         // посылка сообщений...
  147.         private function send(message:String):void {
  148.             // массив байт для посылки...
  149.             var sendBytes:ByteArray = new ByteArray();
  150.            
  151.             // байты самого сообщения...
  152.             var mBytes:ByteArray = new ByteArray();
  153.             // запишем сюда строку...
  154.             mBytes.writeUTFBytes(message);
  155.            
  156.             // запишем блину сообщения...
  157.             sendBytes.writeInt(mBytes.length);
  158.             // запишем сам массив байт сообщения...
  159.             sendBytes.writeBytes(mBytes);
  160.            
  161.             // отошлем байты...
  162.             _socket.writeBytes(sendBytes);
  163.             _socket.flush();
  164.         }
  165.        
  166.         // проверка на нажатие enter - отправка сообщения...
  167.         private function keyboardListener(e:KeyboardEvent):void {
  168.             // нам нужно только enter и если поле не пустое...
  169.             if(e.keyCode == Keyboard.ENTER && _messageField.text != '' && _messageField.text != ' ') {
  170.                 // если не авторизованы - подождем...
  171.                 if(!_authorized) {
  172.                     showStatusMessage("Еще не авторизованы... Подождем...", Main.NORMAL_COLOR);
  173.                     return;
  174.                 }
  175.                
  176.                 // покажем сообщение..
  177.                 showUserMessage(int(_flashVars['viewer_id']), _messageField.text, true);
  178.                
  179.                 // отошлем сообщение и очистим поле для ввода...
  180.                 send( JSON.stringify( {message:_messageField.text} ) );
  181.                 _messageField.text = "";
  182.             }
  183.         }
  184.        
  185.         // ждем подтверждения авторизации...
  186.         private function readAuthorization(isOK:int):void {
  187.             if(isOK) {
  188.                 showStatusMessage("Авторизация прошла успешно!", Main.GOOD_COLOR);
  189.                 _authorized = true;
  190.             }
  191.             else {
  192.                 showStatusMessage("Авторизация не удалась :(", Main.ERROR_COLOR);
  193.                 _socket.close();
  194.             }
  195.         }
  196.        
  197.         private function sendAuthorize():void {
  198.             // если не авторизованы - отошлем сообщение чтобы авторизоватся....
  199.             if(!_authorized) send( JSON.stringify( {uid:int(_flashVars['viewer_id']), auth_key:_flashVars['auth_key']} ) );
  200.         }
  201.        
  202.         // читаем длину пакета...
  203.         private function readLength():void {
  204.             // если в буфере меньше 4 байт - мы не можем прочитать длину...
  205.             if(_buffer.length < _buffer.position + 4) return;
  206.            
  207.             // если мы тут, значит читаем длину...
  208.             _length = _buffer.readInt();
  209.            
  210.             // изменяем состояние...
  211.             _state = Main.READ_MESSAGE;
  212.            
  213.             // вызываем функцию чтобы прочитать сообщение(вдруг прибыло тоже...)
  214.             readMessage();
  215.         }
  216.        
  217.         // читаем сообщение юзера...
  218.         private function readMessage():void {
  219.             // если в буфере меньше длина сообщения, чем нужная длина -
  220.             // ждем получения данных...
  221.             if(_buffer.length < _buffer.position + _length) return;
  222.            
  223.             // читаем сообщение...
  224.             var user_message:String = _buffer.readUTFBytes(_length);
  225.            
  226.             // если в буфере нет данных после считывания сообщения
  227.             // и разделителя - очистим его...
  228.             // если есть - очистим в следующий раз :)
  229.             if(_buffer.bytesAvailable == 0) _buffer.clear();
  230.                
  231.             // если мы тут - сообщение нормально пришло...
  232.             // декодируем сообщения..
  233.             var decodedMessage:Object = JSON.parse(user_message);
  234.                
  235.             // если не авторизованы - посмотрим авторизовались ли..
  236.             // если авторизованы - покажем сообщение чела какого-то...
  237.             if(_authorized) {
  238.                 // теперь выводим сообщение...
  239.                 showUserMessage(decodedMessage.uid, decodedMessage.message);
  240.             }
  241.             else {
  242.                 // на авторизацию...
  243.                 readAuthorization(decodedMessage.isOK);
  244.             }
  245.             // тут все ок, значит изменим состояние на ожидание длины...
  246.             _state = Main.READ_LENGTH;
  247.         }
  248.        
  249.         // ошибка подключения
  250.         private function errorListener(e:IOErrorEvent):void {
  251.             showStatusMessage("Ошибка соединения...\n\"" + e.text + "\"", Main.ERROR_COLOR);
  252.         }
  253.        
  254.         // ошибка безопасности..
  255.         // ошибка подключения
  256.         private function securityErrorListener(e:SecurityErrorEvent):void {
  257.             showStatusMessage("Ошибка соединения...\n\"" + e + "\"", Main.ERROR_COLOR);
  258.         }
  259.        
  260.         // показать сообщение человека... с его айди...
  261.         private function showUserMessage(user:int, mess:String, isI:Boolean = false):void {
  262.             showMessage("id" + user.toString() + ": ", isI ? Main.USER_I : Main.USER_COLOR);
  263.             showMessage(mess + "\n", Main.MESSAGE_COLOR);
  264.         }
  265.        
  266.         // показать статусное сообщение...
  267.         private function showStatusMessage(mess:String, color:uint):void {
  268.             showMessage(mess + "\n", color);
  269.         }
  270.        
  271.         // показать сообщение вообще...
  272.         private function showMessage(mess:String, color:uint):void {
  273.             _chatField.appendText(mess);
  274.             _chatField.setTextFormat(new TextFormat(null,16,color), _chatField.text.length - mess.length, _chatField.text.length);
  275.             _chatField.scrollV = _chatField.maxScrollV;
  276.         }
  277.     }
  278. }


2. Создаем chat.fla и прописываем класс документа - Main (файлы в одной папке должны быть)

3. Компилируем наш chat.fla
Нажав ctrl+enter.. получаем chat.swf

4. Грузим этот файл в ВК как flash-приложение..., клиент готов...


Создаем сервер...

1. Код сервера...
Создаем файл Server.java и в любом текстовом редакторе пишем, меняя 16-22 строки (айди приложения, секретный ключ, и если нужно - порт)...
  1. // импортируем классы для работы с потоками
  2. import java.io.*;
  3. // импортируем классы для работы с сокетами
  4. import java.net.*;
  5. // импортируем классы для работы с буферами, потоками, получением байтов от значений и т.д.
  6. // т.е. new io - новый input output... именно тут класс ByteBuffer
  7. import java.nio.*;
  8. // импортируем Vector - массив автоматической длины...
  9. import java.util.Vector;
  10. // классы для работы JSON
  11. import org.json.simple.*;
  12. import org.json.simple.parser.*;
  13.  
  14. public class Server {
  15.     // порт для работы сервера...
  16.     public static final int PORT = 17030;
  17.    
  18.     // айди приложения...
  19.     public static final long APP_ID = 3870844;
  20.    
  21.     // ключ приложения...
  22.     public static final String APP_SECRET = "xxx";
  23.    
  24.     // сокет для принятия соединений...
  25.     private ServerSocket serverSocket;
  26.    
  27.     // массив клиентов...
  28.     private Vector<Client> clients;
  29.    
  30.     // конструктор...
  31.     public Server() throws Exception {
  32.         try {
  33.             // созданим вектор - массив автоматической длинны для клиентов...
  34.             clients = new Vector<Client>();
  35.            
  36.             // создадим серверный сокет...
  37.             serverSocket = new ServerSocket(Server.PORT);
  38.            
  39.             // напишем что сервер запускается
  40.             out("Server start...");
  41.            
  42.             // ждем подключений бесконечно...
  43.             while(true) {
  44.                 // ждем подключений... поток блокируется...
  45.                 Socket newClient = serverSocket.accept();
  46.                
  47.                 // мы получили подключение... теперь создадим класс Client
  48.                 // и запишем его в массив клиентов..
  49.                 out("New client connected... Online: " + (clients.size()+1) );
  50.                 clients.addElement( new Client(newClient, this) );
  51.             }
  52.         }
  53.         catch(Exception ex) {
  54.             // если ошибка любая, то перед выходом закроем серверный сокет...
  55.             if(serverSocket != null) serverSocket.close();
  56.             // покажем, что не удалось :(
  57.             out("Error in start server :(");
  58.         }
  59.     }
  60.    
  61.     // функция рассылки сообщения всем...
  62.     public void sendMessage(String message, long uid) throws Exception {
  63.         // json с сообщением...
  64.         JSONObject messageJSON = new JSONObject();
  65.         messageJSON.put("uid", uid);
  66.         messageJSON.put("message", message);
  67.        
  68.         // массив байт готового сообщения для рассылки...
  69.         byte[] finalMessage = getMessageBytes(messageJSON);
  70.        
  71.         // разошлем сообщение...
  72.         for(int i = 0; i < clients.size(); i++) {
  73.             // берем из вектора клиент...
  74.             Client client = (Client)clients.get(i);
  75.            
  76.             // если он не подключен - удалим...
  77.             if(!client.isConnected()) {
  78.                 deleteClient(client);
  79.                 continue;
  80.             }
  81.            
  82.             // если тут - все ок... отошлем...
  83.             client.send(finalMessage, uid);
  84.         }
  85.     }
  86.    
  87.     // функция удаления из массива клиента...
  88.     public void deleteClient(Client client) {
  89.         // удалим...
  90.         deleteReal(client);
  91.         // скажем что отсоединился...
  92.         out("Client disconnected... Online: " + clients.size());
  93.     }
  94.    
  95.     // функция удаления с сообщением...
  96.     public void deleteClient(Client client, String message) {
  97.         // удалим...
  98.         deleteReal(client);
  99.         // покажем сообщение...
  100.         out("Client disconnected... Online: " + clients.size() + message);
  101.     }
  102.    
  103.     // именно тут идет удаление...
  104.     private void deleteReal(Client client) {
  105.         // удалим из вектора...
  106.         clients.remove(client);
  107.         // и деактивируем...
  108.         client.deactivate();
  109.     }
  110.    
  111.     // просто функция-оболочка для упрощения...
  112.     public void out(String s) {
  113.         System.out.println(s);
  114.     }
  115.    
  116.     // функция получения байт сообщения...
  117.     public byte[] getMessageBytes(JSONObject json) throws Exception {
  118.         // определили массив байт для сохранения самого сообщения...
  119.         // заносим сюда байты сообщения в кодировке utf-8
  120.         byte[] messageBytes = (json.toJSONString()).getBytes("UTF8");
  121.        
  122.         // массив байт указывающий длину сообщения...
  123.         // allocate(4) - взять 4 байта, поскольку int = 4 байта...
  124.         // putInt(XXX) - добавить число - int
  125.         // array() - получить массив байт в ответе...
  126.         byte[] messageLength = ByteBuffer.allocate(4).putInt(messageBytes.length).array();
  127.        
  128.         // собираем наконец, готовый массив...
  129.         // массив байт готового сообщения...
  130.         byte[] finalMessage = new byte[messageBytes.length + 4];
  131.         System.arraycopy(messageLength, 0, finalMessage, 0, 4);
  132.         System.arraycopy(messageBytes, 0, finalMessage, 4, messageBytes.length);
  133.        
  134.         return finalMessage;
  135.     }
  136.    
  137.     // функция удаления всех потоков с этим клиентом, кроме текущего(переданного)...
  138.    
  139.    
  140.    
  141.     // начало программы....
  142.     public static void main(String[] args) throws Exception {
  143.         Server s = new Server();
  144.     }
  145. }


Создаем файл Client.java и туда вносим такой код:
  1. // импортируем классы для работы с потоками
  2. import java.io.*;
  3. // импортируем классы для работы с сокетами
  4. import java.net.*;
  5. // импортируем классы для работы с буферами, потоками, получением байтов от значений и т.д.
  6. // т.е. new io - новый input output... именно тут класс ByteBuffer
  7. import java.nio.*;
  8. // импортируем Vector - массив автоматической длины...
  9. import java.util.Vector;
  10. // классы для работы JSON
  11. import org.json.simple.*;
  12. import org.json.simple.parser.*;
  13. // классы для md5
  14. import java.security.MessageDigest;
  15. import java.security.NoSuchAlgorithmException;
  16. import java.math.BigInteger;
  17.  
  18. public class Client implements Runnable {
  19.     // сокет текущего чела...
  20.     private volatile Socket thisClientSocket;
  21.     // входной поток(от клиента)...
  22.     private InputStream thisInput;
  23.     // выходной поток(к нашему клиенту)...
  24.     private OutputStream thisOutput;
  25.    
  26.     // ссылка на сервер...
  27.     private volatile Server linkForServer;
  28.    
  29.     // ссылка на текущий поток...
  30.     private volatile Thread thisThread;
  31.    
  32.     // нужно ли выполнятся....
  33.     public volatile boolean isAlive = true;
  34.    
  35.     // авторизовались ли...
  36.     private boolean isAuthorized;
  37.    
  38.     // айди клиента...
  39.     private long uid = -1;
  40.    
  41.     // максимальный размер буфера...
  42.     public static final int MAX_BUFFER_SIZE = 10240;
  43.     // буфер для хранения входных данных размером MAX_BUFFER_SIZE...
  44.     private byte[] buffer;
  45.    
  46.     // сейчас читаем длину...
  47.     public static final int READ_LENGTH = 0;
  48.     // сейчас читаем сообщение...
  49.     public static final int READ_MESSAGE = 1;
  50.    
  51.     // состояние чтения...
  52.     private int state = Client.READ_LENGTH;
  53.    
  54.     // насколько заполнен буфер...
  55.     private int buffer_length = 0;
  56.    
  57.     // позиция в буфере....
  58.     private int position = 0;
  59.    
  60.     // длина последнего пакета...
  61.     private int length = 0;
  62.    
  63.     // время ожидания авторизации... 10 секунд...
  64.     public final int READ_TIME_AUTH = 10000;
  65.    
  66.     // время ожидания сообщения... 150 секунд..
  67.     public final int READ_TIME_MESS = 150000;
  68.    
  69.     // конструктор...
  70.     public Client(Socket clientSocket,Server server) throws Exception {
  71.         // установим переменные....
  72.         thisClientSocket = clientSocket;
  73.         // время ожидания авторизации..
  74.         thisClientSocket.setSoTimeout(READ_TIME_AUTH);
  75.         thisInput = thisClientSocket.getInputStream();
  76.         thisOutput = thisClientSocket.getOutputStream();
  77.         linkForServer = server;
  78.        
  79.         // создадим буфер..
  80.         buffer = new byte[Client.MAX_BUFFER_SIZE+1];
  81.        
  82.         // создадим и запустим поток....
  83.         thisThread = new Thread(this);
  84.         thisThread.start();
  85.     }
  86.    
  87.     // это старт потока...
  88.     public void run() {
  89.         try {
  90.             // пока потоку не нужно остановиться...
  91.             while(isAlive) {
  92.                 try {
  93.                     // временная переменная... количество прочитаных байт...
  94.                     int readed;
  95.                     // читаем данные...
  96.                     while((readed = thisInput.read(buffer, buffer_length, Client.MAX_BUFFER_SIZE-buffer_length)) != -1) {
  97.                         // увеличим значение прочитанных байтов, если не переполнен буфер...
  98.                         buffer_length += readed;
  99.                        
  100.                         // вызываем функцию чтения или длины или сообщения...
  101.                         try {
  102.                             if(state == Client.READ_LENGTH) readLength();
  103.                             else readMessage();
  104.                         }
  105.                         catch(Exception e) {
  106.                             continue;
  107.                         }
  108.                     }
  109.                 }
  110.                 catch(Exception e) {
  111.                     // удалим из вектора сервера и он деактивирует текущего клиента...
  112.                     linkForServer.deleteClient(this, " (non active)");
  113.                 }
  114.             }
  115.         }
  116.         catch(Exception e) {
  117.             // удалим из вектора сервера и он деактивирует текущего клиента...
  118.             linkForServer.deleteClient(this);
  119.         }
  120.     }
  121.    
  122.     // функция чтения длины...
  123.     private void readLength() throws Exception {
  124.         // если меньше 4 байтов - не можем получить длину...
  125.         if(buffer_length < position + 4) return;
  126.        
  127.         // все ок... читаем длину и изменяем позицию...
  128.         length = (int)ByteBuffer.allocate(4).put(buffer, position, 4).getInt(0);
  129.         position += 4;
  130.        
  131.         // меняем состояние...
  132.         state = Client.READ_MESSAGE;
  133.        
  134.         // вдруг есть и сообщение...
  135.         readMessage();
  136.     }
  137.    
  138.     // функция чтения сообщения...
  139.     private void readMessage() throws Exception {
  140.         // если длина меньше чем нужно - ждем получения данных...
  141.         if(buffer_length < position + length) return;
  142.        
  143.         // получаем строку и изменяем позицию...
  144.         String user_message = new String(buffer, position, length, "UTF8");
  145.         position += length;
  146.        
  147.         // если позиция и длина буфера равны - обнулим их(будет идти перезапись)...
  148.         if(position == buffer_length) position = buffer_length = 0;
  149.        
  150.         // декодируем сообщение...
  151.         JSONParser parser = new JSONParser();
  152.         JSONObject decoded_message = (JSONObject) parser.parse(user_message);
  153.        
  154.         // если не авторизованы - проверим авторизацию...
  155.         // если авторизованы - отошлем сообщение другим...
  156.         if(isAuthorized) {
  157.             // рассылаем сообщение...
  158.             linkForServer.sendMessage((String)decoded_message.get("message"), uid);
  159.         }
  160.         else {
  161.             // установим айди..
  162.             uid = (long)decoded_message.get("uid");
  163.             // создадим объект json...
  164.             JSONObject messageJSON = new JSONObject();
  165.             if( md5(Long.toString(Server.APP_ID) + '_' + Long.toString(uid) + '_' + Server.APP_SECRET).equals((String)decoded_message.get("auth_key")) ) {
  166.                 messageJSON.put("isOK", 1);
  167.                 isAuthorized = true;
  168.                 // время ожидания сообщение..
  169.                 thisClientSocket.setSoTimeout(READ_TIME_MESS);
  170.             }
  171.             else messageJSON.put("isOK", 0);
  172.            
  173.             // отсылаем ответ...
  174.             send( linkForServer.getMessageBytes(messageJSON) );
  175.            
  176.             // если не авторизовались - закроем соединение...
  177.             if(!isAuthorized) linkForServer.deleteClient(this);
  178.         }
  179.        
  180.         // установим состояние что нужно читать длину..
  181.         state = Client.READ_LENGTH;
  182.     }
  183.    
  184.     // функция отсылки сообщения....
  185.     public void send(byte[] messageBytes, long senderUID) {
  186.         // если у клиента id тот-же или он не авторизовался - выходим...
  187.         if(!isAuthorized || senderUID == uid) return;
  188.        
  189.         // отправляем байты...
  190.         send(messageBytes);
  191.     }
  192.    
  193.     // функция для отправки только массива байт...
  194.     public void send(byte[] messageBytes) { 
  195.         try {
  196.             // запишем байты...
  197.             thisOutput.write(messageBytes);
  198.            
  199.             // и отошлем...
  200.             thisOutput.flush();
  201.         }
  202.         catch(Exception e) {
  203.             // удалим из вектора сервера и он деактивирует текущего клиента...
  204.             linkForServer.deleteClient(this);
  205.         }
  206.     }
  207.    
  208.     // открыт ли сокет...
  209.     public boolean isConnected() {
  210.         return thisClientSocket.isConnected();
  211.     }
  212.    
  213.     // функция деактивации...
  214.     public void deactivate() {
  215.         // закрываем сокет....
  216.         try {
  217.             if(thisClientSocket != null) thisClientSocket.close();
  218.         }
  219.         catch(IOException e) {
  220.             thisClientSocket = null;
  221.         }
  222.        
  223.         // обнуляем ссылку на поток
  224.         thisThread = null;
  225.        
  226.         // обнуляем ссылку на сервер
  227.         linkForServer = null;
  228.        
  229.         // поток не должен выполнятся...
  230.         isAlive = false;
  231.        
  232.         // удалим ссылку с буфера...
  233.         buffer = null;
  234.     }
  235.    
  236.     // функция шифрования md5
  237.     public static String md5(String s) {
  238.         String hash;
  239.         try {
  240.             MessageDigest m = MessageDigest.getInstance("MD5");
  241.             m.update(s.getBytes("UTF8"), 0, s.length());
  242.             hash = new BigInteger(1, m.digest()).toString(16);
  243.         } catch (Exception e) {
  244.             return "";
  245.         }
  246.         if(hash.length() == 31) hash = "0" + hash;
  247.         return hash;
  248.     }
  249. }


2. Тут используется библиотека json для java, но ее в стандартных нету, потому качаем http://yadi.sk/d/jsHORtgKAjurc, папку org кидаем туда-же, где и эти файлы .java

3. Компилируем через cmd, думаю знаете как, но на всякий случай(переменные окружения должны быть)... javac -classpath ./ Server.java
После компиляции у нас появились 2 новых файла - Client.class и Server.class их мы грузим на VPS(на нем должна быть установлена java)...

4. Теперь на VPS можно запустить...(SSH)
Для запуска используем команду java -classpath ./ Server, но при подключении из флеша будет ошибка безопасности... ее можно исправить запустив на 843 порту отдачу файла политики безопасности.. сделаем это..

5. Файл полити безопасности...perl...
Создадим файл flashpolicyd.pl и внесем туда код:
  1. #!/usr/bin/perl
  2. #
  3. # policyd.pl
  4. # Simple socket policy file server
  5. #
  6. # Usage: policyd.pl [-port=N] -file=FILE
  7. # Logs to stdout
  8. #
  9.  
  10. use strict;
  11. use Socket;
  12.  
  13. my $NULLBYTE = pack( 'c', 0 );
  14.  
  15. my $port = 843;
  16. my $filePath;
  17. my $content;
  18.  
  19. ### READ ARGS
  20.  
  21. while ( my $arg = shift @ARGV )
  22. {
  23.     if ( $arg =~ m/^--port=(\d+)$/ )
  24.     {
  25.         $port = $1;
  26.     }
  27.     elsif ( $arg =~ m/^--file=(.*)/ )
  28.     {
  29.         $filePath = $1;
  30.     }
  31. }
  32.  
  33. unless ( $filePath )
  34. {
  35.     die "Usage: policyd.pl [--port=N] --file=FILE\n";
  36. }
  37.  
  38. ### READ FILE
  39.  
  40. -f $filePath or die "No such file: '$filePath'\n";
  41. -s $filePath < 10_000 or die "File probably too large to be a policy file: '$filePath'\n";
  42.  
  43. local $/ = undef;
  44. open POLICYFILE, "<$filePath" or die "Can't open '$filePath': $!\n";
  45. $content = <POLICYFILE>;
  46. close POLICYFILE;
  47.  
  48. $content =~ m/cross-domain-policy/ or die "Not a valid policy file: '$filePath'\n";
  49.  
  50. ### BEGIN LISTENING
  51.  
  52. socket( LISTENSOCK, PF_INET, SOCK_STREAM, getprotobyname( 'tcp' ) ) or die "socket() error: $!";
  53. setsockopt( LISTENSOCK, SOL_SOCKET, SO_REUSEADDR, pack( 'l', 1 ) ) or die "setsockopt() error: $!";
  54. bind( LISTENSOCK, sockaddr_in( $port, INADDR_ANY ) ) or die "bind() error: $!";
  55. listen( LISTENSOCK, SOMAXCONN ) or die "listen() error: $!";
  56.  
  57. print STDOUT "\nListening on port $port\n\n";
  58.  
  59. ### HANDLE CONNECTIONS
  60.  
  61. while ( my $clientAddr = accept( CONNSOCK, LISTENSOCK ) )
  62. {
  63.     my ( $clientPort, $clientIp ) = sockaddr_in( $clientAddr );
  64.     my $clientIpStr = inet_ntoa( $clientIp );
  65.     print STDOUT "Connection from $clientIpStr:$clientPort\n";
  66.    
  67.     local $/ = $NULLBYTE;
  68.     my $request = <CONNSOCK>;
  69.     chomp $request;
  70.  
  71.     if ( $request eq '<policy-file-request/>' )
  72.     {
  73.         print STDOUT "Valid request received\n";
  74.     }
  75.     else
  76.     {
  77.         print STDOUT "Unrecognized request: $request\n\n";
  78.         close CONNSOCK;
  79.         next;
  80.     }
  81.  
  82.     print CONNSOCK $content;
  83.     print CONNSOCK $NULLBYTE;
  84.     close CONNSOCK;
  85.  
  86.     print STDOUT "Sent policy file\n\n";
  87. }
  88.  
  89. # End of file.
  90.  


Создадим файл flashpolicy.xml и заполним:
  1. <?xml version="1.0"?>
  2. <!DOCTYPE cross-domain-policy SYSTEM "/xml/dtds/cross-domain-policy.dtd">
  3. <cross-domain-policy>
  4.    <site-control permitted-cross-domain-policies="master-only"/>
  5.    <allow-access-from domain="*" to-ports="*" />
  6. </cross-domain-policy>


Теперь создадим файл start.sh и заполним:
  1. perl ./flashpolicyd.pl --file=./flashpolicy.xml --port=843 &


Зальем все 3 файла в 1 папку... и запустим:
./start.sh

Все... все работать должно...(если это не так - задавайте вопросы в этой теме)..

Делаем круглосуточным...
Все что описано выше будет работать, но до тех пор, пока вы не закроете окно ssh...
Но сейчас мы сделаем, чтобы приложение работало круглосуточно, т.е. без нас...
Для этого будем использовать screen..:
создать: screen -S имя_на_англ
войти: screen -r имя_на_англ
выйти: ctrl+a, потом d
просмотреть все скрины: screen -ls

Ссылка на исходник всего урока: http://yadi.sk/d/FsJqR2iGAnv43
Временно работающий чат(я могу его отключать, перезагружать, потому если уж хотите увидеть - обращайтесь в лс вк) - http://vk.com/app3870844

Вот и закончился урок.... Все кто понял - молодец, а не понял - спрашивай в теме :)

За это сообщение автора sasha^ поблагодарили - 10:
Abarmotina, alexandr_ratush, antibuker, Апчхи, Casperovskii, cool137, Den_k, Garch, Soo_Jin, tolmasoft
sasha^

 
Автор темы
Сообщения: 711
Зарегистрирован: 10 сен 2012, 16:52
Благодарил (а): 1 раз.
Поблагодарили: 136 раз.

Чтобы убрать блок с рекламой, зарегистрируйтесь на форуме или войдите.

Google
 



Re: Свой сокет-чат на JAVA и FLASH, без БД!

Сообщение Abarmotina » 12 окт 2013, 12:01

Молодец. Лови +! ;)
Пока они пили водку и катались на скейтах, мы строили бизнес и делали деньги!
Аватара пользователя
Abarmotina

 
Сообщения: 469
Зарегистрирован: 21 апр 2012, 19:34
Благодарил (а): 16 раз.
Поблагодарили: 19 раз.

Re: Свой сокет-чат на JAVA и FLASH, без БД!

Сообщение Soo_Jin » 13 окт 2013, 18:43

Все нормально, только мне кажется, что проще будет создать стринговую переменную на сервере с кроссдомайном и при подключении пользователей, в ответ на <policy-file-request/> отправлять эту переменную.
Аватара пользователя
Soo_Jin

 
Сообщения: 18
Зарегистрирован: 16 июн 2013, 21:16
Откуда: Санкт-Петербург
Благодарил (а): 4 раз.
Поблагодарили: 0 раз.

Re: Свой сокет-чат на JAVA и FLASH, без БД!

Сообщение sasha^ » 13 окт 2013, 18:53

Soo_Jin , так будет проще, но тогда нельзя будет сокет создать ниже 1024 порта.. а если на 843 порту - то можно и ниже 1024 создать сокет(хотя не знаю зачем это нужно :) ) и отдельно отдавать - мы разделяем отдачу файла политики и самого сервера, что вполне нормально...
p.s. признаюсь, что структуру можно и лучше делать..(но для урока слишком много кода, потому такая реализация, считаю, хорошая). я сейчас занят 3d.. потом сделаю уже через классы пакетов и пр... сейчас минимально рабочий чат...
sasha^

 
Автор темы
Сообщения: 711
Зарегистрирован: 10 сен 2012, 16:52
Благодарил (а): 1 раз.
Поблагодарили: 136 раз.

Re: Свой сокет-чат на JAVA и FLASH, без БД!

Сообщение Soo_Jin » 13 окт 2013, 19:16

хех, минимально :D, помню экзамплы 100 летней давности,примерно <150 строчек кода сервер-клиент, правда там без авторизации в вк.
Аватара пользователя
Soo_Jin

 
Сообщения: 18
Зарегистрирован: 16 июн 2013, 21:16
Откуда: Санкт-Петербург
Благодарил (а): 4 раз.
Поблагодарили: 0 раз.

Re: Свой сокет-чат на JAVA и FLASH, без БД!

Сообщение sasha^ » 13 окт 2013, 19:48

минимально не всегда хорошо.. а у меня минимально хорошо :D
sasha^

 
Автор темы
Сообщения: 711
Зарегистрирован: 10 сен 2012, 16:52
Благодарил (а): 1 раз.
Поблагодарили: 136 раз.

Re: Свой сокет-чат на JAVA и FLASH, без БД!

Сообщение razerw » 25 ноя 2013, 08:47

А зачем измерять длину сообщения? На ява сервере у меня оно не работает length сообщения очень большое поэтому и не отрабатывает
String user_message = new String(buffer, position, length, "UTF8");
И потом позиция с которой начинает чтение прыгает, в клиенте тоже самое ЗАЧЕМ? сокетное соединение тсп так что сообшение в любом случае либо придет либо не придет. На сервере заметил что приём идет в формате UTF8 а отправка просто так через writeBytes я пользуюсь writeMultiByte(param1 + "\n", "UTF-8"); срузу в формате UTF8 на сервер читаю так же readMultiByte(event.bytesLoaded, "UTF-8");
я тоже практиковал без кодировок русский текст приходил кракозёброй
razerw

 
Сообщения: 15
Зарегистрирован: 30 май 2013, 21:27
Благодарил (а): 1 раз.
Поблагодарили: 1 раз.

Re: Свой сокет-чат на JAVA и FLASH, без БД!

Сообщение sasha^ » 25 ноя 2013, 09:16

А зачем измерять длину сообщения? На ява сервере у меня оно не работает length сообщения очень большое поэтому и не отрабатывает
String user_message = new String(buffer, position, length, "UTF8");

у меня читай внимательнее бинарный сокет!
длина нужна для распознания сообщения.. если мы, например, отправим 1мб то оно придет частями по X кбайт и нужно будет соединить... откуда ты узнаешь где начало? :)
этот сокет подойдет не только для чата, но и отправки файлов и пр.. а если оч длинное сообщение - даже не скажу.. я тестировал и у меня все работало... насколько болшая?))) не привысил ли ты размеры string-a или int-а? :)
И потом позиция с которой начинает чтение прыгает, в клиенте тоже самое ЗАЧЕМ? сокетное соединение тсп так что сообшение в любом случае либо придет либо не придет.
нет.. ты кардинально ошибаешся.. tcp всегда приходит, потому оно tcp и в отосланном порядке... но порой при больших сообщениях оно разделяется на части!
На сервере заметил что приём идет в формате UTF8 а отправка просто так через writeBytes я пользуюсь writeMultiByte(param1 + "\n", "UTF-8"); срузу в формате UTF8 на сервер читаю так же readMultiByte(event.bytesLoaded, "UTF-8");
я тоже практиковал без кодировок русский текст приходил кракозёброй
это же для чата ток сойдет.. для бинарного никак... читай внимательно
Для обмена сообщениями мы будем использовать бинарный сокет(flash.net.Socket)...
sasha^

 
Автор темы
Сообщения: 711
Зарегистрирован: 10 сен 2012, 16:52
Благодарил (а): 1 раз.
Поблагодарили: 136 раз.

Re: Свой сокет-чат на JAVA и FLASH, без БД!

Сообщение razerw » 25 ноя 2013, 13:06

кстати забыл упомянуть, все это дело у меня заработало без отправки политик данных.
Это я тестировал твой код. До этого писал сервер в концепции с палитик сервером, то есть первому подключившемуся клиенту я отправлял строку ХМЛ. А тут на прямую пошло.
razerw

 
Сообщения: 15
Зарегистрирован: 30 май 2013, 21:27
Благодарил (а): 1 раз.
Поблагодарили: 1 раз.

Re: Свой сокет-чат на JAVA и FLASH, без БД!

Сообщение sasha^ » 25 ноя 2013, 16:03

кстати забыл упомянуть, все это дело у меня заработало без отправки политик данных.

локально то будет работать... а если загрузить, например, в ВК и сервер на vps запустить - то не будет :)
Это я тестировал твой код. До этого писал сервер в концепции с палитик сервером, то есть первому подключившемуся клиенту я отправлял строку ХМЛ. А тут на прямую пошло.
тупое дело... лучше отдельным отправлять файл полити.. разделяя тупую задачу отправки xml со всем остальным...
sasha^

 
Автор темы
Сообщения: 711
Зарегистрирован: 10 сен 2012, 16:52
Благодарил (а): 1 раз.
Поблагодарили: 136 раз.

След.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в Уроки на другие темы



Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 0

cron