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端可以接收到消息。
链接:工程代码

发布了15 篇原创文章 · 获赞 9 · 访问量 2645

猜你喜欢

转载自blog.csdn.net/qq_38413498/article/details/104854936