Java socket сервер + ESP-01 клиент
- Войдите на сайт для отправки комментариев
Комрады прошу помощи, при постороении умного дома наткнулся самую банальную проблемму, передачу информации. На малинке поднят сервер на яве вот с таким кодом
import java.net.ServerSocket;
import java.net.Socket;
import java.io.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
/** * Класс сервера. Сидит тихо на порту, принимает сообщение, создает SocketProcessor на каждое сообщение */
public class ChatServer {
private ServerSocket ss; // сам сервер-сокет
private Thread serverThread; // главная нить обработки сервер-сокета
private int port; // порт сервер сокета.
//очередь, где храняться все SocketProcessorы для рассылки
BlockingQueue<SocketProcessor> q = new LinkedBlockingQueue<SocketProcessor>();
/**
* Конструктор объекта сервера
* @param port Порт, где будем слушать входящие сообщения.
* @throws IOException Если не удасться создать сервер-сокет, вылетит по эксепшену, объект Сервера не будет создан
*/
public ChatServer(int port) throws IOException {
ss = new ServerSocket(port); // создаем сервер-сокет
this.port = port; // сохраняем порт.
}
/**
* главный цикл прослушивания/ожидания коннекта.
*/
void run() {
serverThread = Thread.currentThread(); // со старта сохраняем нить (чтобы можно ее было interrupt())
while (true) { //бесконечный цикл, типа...
Socket s = getNewConn(); // получить новое соединение или фейк-соедиение
if (serverThread.isInterrupted()) { // если это фейк-соединение, то наша нить была interrupted(),
// надо прерваться
break;
} else if (s != null){ // "только если коннект успешно создан"...
try {
final SocketProcessor processor = new SocketProcessor(s); // создаем сокет-процессор
final Thread thread = new Thread(processor); // создаем отдельную асинхронную нить чтения из сокета
thread.setDaemon(true); //ставим ее в демона (чтобы не ожидать ее закрытия)
thread.start(); //запускаем
q.offer(processor); //добавляем в список активных сокет-процессоров
} //тут прикол в замысле. Если попытка создать (new SocketProcessor()) безуспешна,
// то остальные строки обойдем, нить запускать не будем, в список не сохраним
catch (IOException ignored) {} // само же исключение создания коннекта нам не интересно.
}
}
}
/**
* Ожидает новое подключение.
* @return Сокет нового подключения
*/
private Socket getNewConn() {
Socket s = null;
try {
s = ss.accept(); //метод библиотеки
} catch (IOException e) {
shutdownServer(); // если ошибка в момент приема - "гасим" сервер
}
return s;
}
/**
* метод "глушения" сервера
*/
private synchronized void shutdownServer() { // обрабатываем список рабочих коннектов, закрываем каждый
for (SocketProcessor s: q) {
s.close();
}
if (!ss.isClosed()) {
try {
ss.close();
} catch (IOException ignored) {}
}
}
/**
* входная точка программы
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
new ChatServer(45000).run(); // если сервер не создался, программа
// вылетит по эксепшену, и метод run() не запуститься
}
/** *****************************************************************
* вложенный класс асинхронной обработки одного коннекта.
*/
private class SocketProcessor implements Runnable{
Socket s; // наш сокет
BufferedReader br; // буферизировнный читатель сокета
BufferedWriter bw; // буферизированный писатель в сокет
/**
* Сохраняем сокет, пробуем создать читателя и писателя. Если не получается - вылетаем без создания объекта
* @param socketParam сокет
* @throws IOException Если ошибка в создании br || bw
*/
SocketProcessor(Socket socketParam) throws IOException {
s = socketParam;
br = new BufferedReader(new InputStreamReader(s.getInputStream(), "UTF-8"));
bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream(), "UTF-8") );
}
/**
* Главный цикл чтения сообщений/рассылки
*/
public void run() {
while (!s.isClosed()) { // пока сокет не закрыт...
String line = null;
try {
line = br.readLine(); // пробуем прочесть.
} catch (IOException e) {
close(); // если не получилось - закрываем сокет.
}
if (line == null) { // если строка null - клиент отключился в штатном режиме.
close(); // то закрываем сокет
} else if ("shutdown".equals(line)) { // если поступила команда "погасить сервер", то...
serverThread.interrupt(); // сначала возводим флаг у северной нити о необходимости прерваться.
try {
new Socket("localhost", port); // создаем фейк-коннект (чтобы выйти из .accept())
} catch (IOException ignored) { //ошибки неинтересны
} finally {
shutdownServer(); // а затем глушим сервер вызовом его метода shutdownServer().
}
} else { // иначе - банальная рассылка по списку сокет-процессоров
for (SocketProcessor sp:q) {
sp.send(line);
}
}
}
}
/**
* Метод посылает в сокет полученную строку
* @param line строка на отсылку
*/
public synchronized void send(String line) {
try {
bw.write(line); // пишем строку
bw.write("\n"); // пишем перевод строки
bw.flush(); // отправляем
} catch (IOException e) {
close(); //если глюк в момент отправки - закрываем данный сокет.
}
}
/**
* метод аккуратно закрывает сокет и убирает его со списка активных сокетов
*/
public synchronized void close() {
q.remove(this); //убираем из списка
if (!s.isClosed()) {
try {
s.close(); // закрываем
} catch (IOException ignored) {}
}
}
/**
* финализатор просто на всякий случай.
* @throws Throwable
*/
@Override
protected void finalize() throws Throwable {
super.finalize();
close();
}
}
}
Для него есть вот такой Java Клиент вместе они отлично работают.
import java.net.Socket;
import java.io.*;
/**
* Класс-клиент чат-сервера. Работает в консоли. Командой с консоли shutdown посылаем сервер в оффлайн
*/
public class ChatClient {
final Socket s; // это будет сокет для сервера
final BufferedReader socketReader; // буферизированный читатель с сервера
final BufferedWriter socketWriter; // буферизированный писатель на сервер
final BufferedReader userInput; // буферизированный читатель пользовательского ввода с консоли
/**
* Конструктор объекта клиента
* @param host - IP адрес или localhost или доменное имя
* @param port - порт, на котором висит сервер
* @throws java.io.IOException - если не смогли приконнектиться, кидается исключение, чтобы
* предотвратить создание объекта
*/
public ChatClient(String host, int port) throws IOException {
s = new Socket(host, port); // создаем сокет
// создаем читателя и писателя в сокет с дефолной кодировкой UTF-8
socketReader = new BufferedReader(new InputStreamReader(s.getInputStream(), "UTF-8"));
socketWriter = new BufferedWriter(new OutputStreamWriter(s.getOutputStream(), "UTF-8"));
// создаем читателя с консоли (от пользователя)
userInput = new BufferedReader(new InputStreamReader(System.in));
new Thread(new Receiver()).start();// создаем и запускаем нить асинхронного чтения из сокета
}
/**
* метод, где происходит главный цикл чтения сообщений с консоли и отправки на сервер
*/
public void run() {
System.out.println("Type phrase(s) (hit Enter to exit):");
while (true) {
String userString = null;
try {
userString = userInput.readLine(); // читаем строку от пользователя
} catch (IOException ignored) {} // с консоли эксепшена не может быть в принципе, игнорируем
//если что-то не так или пользователь просто нажал Enter...
if (userString == null || userString.length() == 0 || s.isClosed()) {
close(); // ...закрываем коннект.
break; // до этого break мы не дойдем, но стоит он, чтобы компилятор не ругался
} else { //...иначе...
try {
socketWriter.write(userString); //пишем строку пользователя
socketWriter.write("\n"); //добавляем "новою строку", дабы readLine() сервера сработал
socketWriter.flush(); // отправляем
} catch (IOException e) {
close(); // в любой ошибке - закрываем.
}
}
}
}
/**
* метод закрывает коннект и выходит из
* программы (это единственный выход прервать работу BufferedReader.readLine(), на ожидании пользователя)
*/
public synchronized void close() {//метод синхронизирован, чтобы исключить двойное закрытие.
if (!s.isClosed()) { // проверяем, что сокет не закрыт...
try {
s.close(); // закрываем...
System.exit(0); // выходим!
} catch (IOException ignored) {
ignored.printStackTrace();
}
}
}
public static void main(String[] args) { // входная точка программы
try {
new ChatClient("localhost", 45000).run(); // Пробуем приконнетиться...
} catch (IOException e) { // если объект не создан...
System.out.println("Unable to connect. Server not running?"); // сообщаем...
}
}
/** *******************************************************************************
* Вложенный приватный класс асинхронного чтения
*/
//основной класс только отправляет сообщения на сервер а этот постоянно принимет.
private class Receiver implements Runnable{
/**
* run() вызовется после запуска нити из конструктора клиента чата.
*/
public void run() {
while (!s.isClosed()) { //сходу проверяем коннект.
String line = null; //читает то что прислал сервер.
try {
line = socketReader.readLine(); // пробуем прочесть
} catch (IOException e) { // если в момент чтения ошибка, то...
// проверим, что это не банальное штатное закрытие сокета сервером
if ("Socket closed".equals(e.getMessage())) {
break;
}
System.out.println("Connection lost"); // а сюда мы попадем в случае ошибок сети.
close(); // ну и закрываем сокет (кстати, вызвается метод класса ChatClient, есть доступ)
}
if (line == null) { // строка будет null если сервер прикрыл коннект по своей инициативе, сеть работает
System.out.println("Server has closed connection");
close(); // ...закрываемся
} else { // иначе печатаем то, что прислал сервер.
System.out.println("Server:" + line);
}
}
}
}
}
Но как заставить ESP - 01 обмениваться инфой?
На ESP нашёл вот такой вот код, но он только конектится, выкидывает на сервер отладочную информациб, и дисконектит каждые 2 минуты.
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <SocketIoClient.h>
#define USE_SERIAL Serial
ESP8266WiFiMulti WiFiMulti;
SocketIoClient webSocket;
WebSocketsClient WebClient;
void event(const char * payload, size_t length) {
//USE_SERIAL.printf("got message: %s\n", payload);
}
void setup() {
USE_SERIAL.begin(9600);
//USE_SERIAL.setDebugOutput(true);
USE_SERIAL.println();
USE_SERIAL.println();
USE_SERIAL.println();
for(uint8_t t = 4; t > 0; t--) {
USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
USE_SERIAL.flush();
delay(1000);
}
WiFiMulti.addAP("11111","111111");
while(WiFiMulti.run() != WL_CONNECTED) {
delay(100);
}
webSocket.on("event", event);
webSocket.begin("192.168.1.71",45000);
WebClient.begin("192.168.1.71",45000);
// use HTTP Basic Authorization this is optional remove if not needed
//webSocket.setAuthorization("username", "password");
// WebClient.onEvent(webSocketEvent);
}
void loop() {
webSocket.loop();
USE_SERIAL.println("111") ;
//webSocket.emit("chat_message", "Arduino here, hello!");
USE_SERIAL.flush();
WebClient.sendTXT("111111111111111111") ;
// webSocket.print("1111") ;
delay(10000) ;
}
Мучаюсь третий день, нервные клетки на грани истощения.
Вопросов собственно 3
1) как научить ESP Общаться с сервером?
2) почму сервер каждые 2 минуты дисконектит
3) как зотфильтровать отладочную информацию после коннекта
p.s. если посоветуете хорошую литературу для профанов и чайников, буду признателен весьма.
Как говориться, утро вечера мудренее.
Возможно я нашёл ответ.
https://stackoverflow.com/questions/43703021/sending-data-from-java-serv...