描述:一種簡單的遠程交互功能,通過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端可以接收到消息。
鏈接:工程代碼