ESP8266 NodeMCU與遠程客戶端之間的交互——基於JavaSocket實現

描述:一種簡單的遠程交互功能,通過socket方式,實現esp8266與遠程客戶端的消息交互。

服務端代碼:

package chat;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
/**
 * 運行在服務端的SeverSocket主要負責;
 * 1.向系統申請服務端口號客戶端是通過這個端口號與之連接的.
 * 2.監聽申請的服務端口號,當一個客戶端通過該端口號嘗試建立聯繫連接時,SeverSocket會在服務端創建一個Socket與客戶端建立連接服務端正對同客戶端建立多個Socket.
 */
public class Server {

    public ServerSocket server;
    /*用來保存客戶端輸出流的集合,因爲線程安全的也不和遍歷互斥,要自己維護也可以保證安全*/
    private List<PrintWriter> allOut;     //私聊可以用map,key爲暱稱,value是對應消息,廣播則遍歷value
    /*初始化 服務端*/
    public Server()throws Exception  {
        /*初始化的同時申請端口號*/
        server = new ServerSocket(10088);
        allOut = new ArrayList<PrintWriter>();
    }
    /**
     * 將給定的輸出流存入共享集合
     * @param out
     */
    private synchronized void addOut(PrintWriter out) {
        allOut.add(out);
    }
    /**
     * 將給定的輸出流從共享集合中刪除
     * @param out
     */
    private synchronized void removeOut(PrintWriter out) {
        allOut.remove(out);
    }
    /**
     * 將給定的消息發送給所有客戶端
     * @param message
     */
    private synchronized void  sendMessage(String message) {
        for(PrintWriter out : allOut) {
            out.println(message);
        }
    }
    /*服務端開始工作的方法*/
    public void start() {
        try {
            /*ServerSocket的accept的方法是一個阻塞的方法,作用是監聽服務端口號,知道一個客戶端;連接並創建一個Socket,使用該Socket即可與剛纔鏈接的客戶端進行交互.*/
            while(true) {
                System.out.println("等待客戶端連接...");
                Socket socket = server.accept();
                System.out.println("一個客戶端連接了!");
                /*啓動一個線程,來完成與該客戶端的交互*/
                ClientHandler handler= new ClientHandler(socket);
                Thread t = new Thread(handler);
                t.start();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
    public static void main(String[] args) {
        try {
            Server server = new Server();
            server.start();
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("服務端建立聯繫失敗!");
        }
    }
    /*該線程負責處理一個客戶端的交互*/
    class ClientHandler implements Runnable{
        /*該線程處理的客戶端的Socket*/
        private Socket socket ;
        private String host;    //客戶端的地址信息,區分不同客戶端
        private String nickName;//用戶的暱稱
        public ClientHandler(Socket socket) {
            this.socket = socket;
            /*通過Socket可以獲取遠端計算機的地址信息*/
            InetAddress address = socket.getInetAddress();
            host = address.getHostAddress();//獲取IP地址
        }
        public void run() {
            PrintWriter pw = null;
            try {
                InputStream in = socket.getInputStream();
                InputStreamReader isr = new InputStreamReader(in,"UTF-8");
                BufferedReader br = new BufferedReader(isr);
                nickName =  br.readLine();//首先讀取一行字符串爲暱稱
                sendMessage(nickName+"上線了");
                /*通過Socket創建輸出流用於將消息發送給客戶端*/
                OutputStream out = socket.getOutputStream();
                OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");
                pw = new PrintWriter(osw,true);
                /*將該客戶端對的輸出流存入到共享集合中*/
                addOut(pw);
                String message = null;
                /**
                 * br.readLine()在讀取客戶戶端發送過來的消息時,由於客戶端斷線,
                 * 而操作系統的不同,這裏讀取後的結果不同:
                 * 1.當windows的客戶端斷開時:br.readLine會拋出異;
                 * 2.當linux的客戶端斷開時:br.readLine 會返回null。
                 */
                while((message = br.readLine())!=null) {
                    /*廣播消息*/
                    sendMessage(nickName+":"+message);
                }
            } catch (Exception e) {
                System.out.println(e.toString());
            }finally {
                /*處理當前客戶端斷開後的邏輯*/
                removeOut(pw);//將該客戶端的輸出流從共享集合中刪除
                sendMessage(nickName+"下線了");
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

客戶端代碼:
client端:

package chat;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

public class Client {
    private Socket socket;
    public Client() throws Exception {
        System.out.println("正在連接服務端...");
        socket = new Socket("localhost",10088);
        System.out.println("已經和服務端建立聯繫");
    }
    public void start() {
        try {
            Scanner scanner = new Scanner(System.in);
            /*先要求用戶創建一個暱稱*/
            String nickName = null;
            nickName = "Client";
            System.out.println("歡迎你"+nickName+"! 開始聊天吧!");
            OutputStream out = socket.getOutputStream();
            OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");
            PrintWriter pw =  new  PrintWriter(osw,true);  /*不能指明字節流,所以要加一個轉換流,第二參數爲true會行刷新*/
            pw.println(nickName);  /*先將暱稱發給服務器*/
            ServerHandler  handler = new ServerHandler();/*啓動讀取服務端發送過來消息的線程*/
            Thread t = new Thread(handler);
            t.start();
            while(true) {
                pw.println(scanner.nextLine());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        try {
            Client client = new Client();
            client.start();
        }catch(Exception e) {
            e.printStackTrace();
            System.out.println("客戶端啓動失敗!");
        }
    }
    /**
     * @function 該線程用來讀取服務器端發送來的消息,並輸出到客戶端控制檯顯示
     */
    class ServerHandler implements Runnable {
        public void run() {
            try {
                InputStream in = socket.getInputStream();
                InputStreamReader isr = new InputStreamReader(in,"UTF-8");
                BufferedReader br = new BufferedReader(isr);

                String message = null;
                while ((message = br.readLine())!=null) {
                    System.out.println(message);
                }
            } catch (Exception e) {
                e.getStackTrace();
            }
        }
    }
}

esp8266端:

/**
 * IDE:Arduino IDE
 * Demo:
 *    Java socket通信,Java_client端發送指令“LED_ON,LED_OFF”,
 *    實現遠程控制點燈效果;ESP端通過串口助手發送數據,Java_client端加收數據。
 */
#include <ESP8266WiFi.h>
 
#define AP_SSID "FAST_210284" //wifi名字
#define AP_PSW  "6322860bgs"  //wifi密碼
 
const uint16_t port = 10088;         //端口號
const char * host = "192.168.1.103"; //IP地址
String incomedate = "";

WiFiClient client;  //創建一個tcp client連接

void setup() {
  pinMode(D4, OUTPUT);
  digitalWrite(D4, HIGH);//初始狀態爲滅燈狀態
  Serial.begin(115200);//設置串口波特率,以便打印信息
  WiFi.mode(WIFI_STA);
  WiFi.begin(AP_SSID,AP_PSW);
  
  Serial.print("\n嘗試連接網絡.");//等待wifi連接成功
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(1000);//1秒重連
  }
  Serial.print("\n網絡連接成功,本機IP地址:");
  Serial.println(WiFi.localIP());
  
  Serial.print("嘗試連接服務器.");
  while(!client.connect(host, port)) {
    Serial.print(".");
    delay(1000);//1秒重連
    return;
  }
  Serial.println("\n服務器連接成功");
  client.println(String("ESP-12"));
}
void loop() {
  while (Serial.available()>0){
    incomedate += char(Serial.read());
  }
  if (incomedate.length() > 0){
    Serial.println(incomedate);
    client.println(String(incomedate));
    incomedate = "";
   }
  //讀取從server返回到響應數據
  String line = client.readStringUntil('\n');
  line.trim();
  if (line != NULL){
    Serial.println(line);
    if(line=="ClientA:LED_ON") 
    {
     Serial.println("開燈");
     digitalWrite(D4, LOW);
    }
    if(line=="ClientA:LED_OFF") 
    {
      Serial.println("關燈");
      digitalWrite(D4, HIGH);
    }
  }
}

測試效果: client端發送約定好的指令“LED_ON”或“LED_OFF”,esp端可實現開關燈效果;esp端用串口發送數據,client端可以接收到消息。
鏈接:工程代碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章