描述:一种简单的远程交互功能,通过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端可以接收到消息。
链接:工程代码