重点参考长链接http://blog.csdn.net/fengyuzhengfan/article/details/38830115
http://blog.csdn.net/Jsagacity/article/details/78531819#
http://www.runoob.com/w3cnote/android-tutorial-socket1.html
1 电脑(或ESP8266)连接WIFI,建立服务 192.168.3.8 8080
1.1 查看电脑地址
1.2 电脑建立服务 192.168.3.8 8080
2 手机连接WIFI,创建客户申请服务
手机在wifi 下分配的地址是 192.168.3.9
2.1 手机打开软件
点击发送
电脑接收
2手机端工程
手机端分为两个工程
1短链接。发送完消息就自动断开。不牵扯链接中断问题,但无法接收消息。
2长连接。发送完消息还可以接收消息。实现数据互传,但是还没有加入重链接功能。
2.1 AsyncTask建立的短链接
1 布局管理
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.dongdong.myapplication.MainActivity"> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/LinearLayout1" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" tools:context=".MainActivity" android:weightSum="1"> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center" android:weightSum="1"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="IP:" android:layout_weight="0.04" /> <EditText android:id="@+id/SIP" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="0.65" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="端口:"/> <EditText android:id="@+id/IPort" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="0.17" /> </LinearLayout> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="left" android:weightSum="1"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="连接"/> <Button android:id="@+id/bt_send" android:layout_width="88dp" android:layout_height="wrap_content" android:text="发送" android:layout_below="@+id/tv_content" android:layout_alignParentStart="true" tools:layout_editor_absoluteY="0dp" tools:layout_editor_absoluteX="8dp" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="清空"/> </LinearLayout> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" android:gravity="left" android:weightSum="1"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="发送区:" android:layout_weight="0.04" /> <EditText android:id="@+id/sentText" android:layout_width="fill_parent" android:layout_height="wrap_content" /> <TextView android:id="@+id/tv_send_text" android:layout_width="241dp" android:layout_height="43dp" android:text="发送的内容" android:layout_below="@+id/bt_send" tools:layout_editor_absoluteX="8dp" tools:layout_editor_absoluteY="8dp" android:layout_weight="0.03" /> </LinearLayout> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" android:gravity="left" android:weightSum="1" android:layout_weight="0.12"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="接收区:" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="清空"/> <TextView android:id="@+id/tv_content" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="left" android:text="接收的内容" android:layout_alignParentTop="true" android:layout_alignParentStart="true" tools:layout_editor_absoluteY="0dp" tools:layout_editor_absoluteX="8dp" /> </LinearLayout> </LinearLayout> </android.support.constraint.ConstraintLayout>
2 添加网络权限
<uses-permission android:name="android.permission.INTERNET"/>
3 工程目录
1采用AsyncTask 异步模式
输入参数问题
2.1 函数输入参数1可以设置输入参数,可以是控件
SendAsyncTask myTask = new SendAsyncTask(SIP_target,IPort__target);
2.2
myTask.execute(str);
对应这个参数是
doInBackground(String... params)
package com.example.dongdong.myapplication; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.View; import android.view.View.OnClickListener; import android.widget.TextView; import android.widget.EditText; import android.widget.Button; import android.widget.Toast; public class MainActivity extends AppCompatActivity implements OnClickListener { // 1.1 定义 private Button bt_send; private TextView tv_content; private TextView tv_send_text; private EditText SIP; private EditText IPort; private EditText sentText; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 1.2 控件初始化 InitView(); // 2 开启服务器 MobileServer mobileServer = new MobileServer(); mobileServer.setHandler(handler); new Thread(mobileServer).start(); } private void InitView() { tv_content = (TextView) findViewById(R.id.tv_content); tv_send_text = (TextView) findViewById(R.id.tv_send_text); sentText=(EditText) findViewById(R.id.sentText); SIP=(EditText) findViewById(R.id.SIP); IPort=(EditText) findViewById(R.id.IPort); bt_send = (Button) findViewById(R.id.bt_send); bt_send.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.bt_send: // String str = "Sent to the ESP8266"; String str="请输入命令"; int IPort__target=8080; String SIP_target="192.168.1.1"; if( sentText!=null) { str=sentText.getText().toString().trim(); } if (SIP!= null) { SIP_target=SIP.getText().toString().trim(); } if (IPort!= null) { String msg = IPort.getText().toString().trim(); if (msg != null && msg.length() > 0) { IPort__target = Integer.parseInt(msg); } } // 链接IP SendAsyncTask myTask = new SendAsyncTask(SIP_target,IPort__target); //SendAsyncTask myTask = new SendAsyncTask(); // 初始发送的消息 myTask.execute(str); tv_send_text.setText(str); break; } } Handler handler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case 1: tv_content.setText("WiFi模块发送的:" + msg.obj); Toast.makeText(MainActivity.this, "接收到信息", Toast.LENGTH_LONG) .show(); } } }; }
package com.example.dongdong.myapplication; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.io.DataInputStream; import android.os.Handler; import android.os.Message; public class MobileServer implements Runnable { private ServerSocket server; private DataInputStream in; private byte[] receice; private Handler handler = new Handler(); public MobileServer() { } public void setHandler(Handler handler) { this.handler = handler; } @Override public void run() { try { //5000是手机端开启的服务器的端口号,ESP8266进行TCP连接时使用的端口,而IP也是通过指令查询的联入设备的IP server = new ServerSocket(5000); while (true) { Socket client = server.accept(); in = new DataInputStream(client.getInputStream()); receice = new byte[50]; in.read(receice); in.close(); Message message = new Message(); message.what = 1; message.obj = new String(receice); handler.sendMessage(message); } } catch (IOException e) { e.printStackTrace(); } try { server.close(); } catch (IOException e) { e.printStackTrace(); } } }
package com.example.dongdong.myapplication; import java.io.IOException; import java.io.PrintStream; import java.net.Socket; import android.os.AsyncTask; public class SendAsyncTask extends AsyncTask<String, Void, Void> { // public class SendAsyncTask extends AsyncTask<String, Void, Void> { //这里是连接ESP8266的IP和端口号,IP是通过指令在单片机开发板查询到,而端口号可以自行设置,也可以使用默认的,333就是默认的 private static String IP ; private static int PORT ; private Socket client = null; private PrintStream out = null; public SendAsyncTask(String ip,int port) { super(); this.IP = ip; this.PORT = port; } @Override protected Void doInBackground(String... params) { String str = params[0]; try { client = new Socket(IP, PORT); client.setSoTimeout(5000); // 获取Socket的输出流,用来发送数据到服务端 out = new PrintStream(client.getOutputStream()); out.print(str); out.flush(); if (client == null) { return null; } else { out.close(); client.close(); } } catch (IOException e) { e.printStackTrace(); } return null; } }
2 Handler 建立的长连接
功能描述
- 建立长链接,链接按钮建立链接 ,发射按钮发射信号,停止按钮结束。也可以改成短链接,生成类的时候把信号加进去。
- 界面和通信线程各自有访问彼此的Handler,从而实现数据线程互传互传。
- 主界面之外,建立通信线程发送消息。通信线程里又新建一个线程,用于专门接收服务器的数据。
现有问题
- 若连超时异常处理
- 若连接上但之后中断,连接重检测问题。
工程代码
如果自己新建工程要加入这些代码,注意修改工程名字。一般每个文件的第一行,只一句根据实际建立的工程确定。
2.1 网路权限
<uses-permission android:name="android.permission.INTERNET"/>
添加后完成整的代码
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.espressif.myapplication"> <uses-permission android:name="android.permission.INTERNET"/> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
2 布局文件
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/LinearLayout1" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" tools:context=".MainActivity" android:weightSum="1"> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center" android:weightSum="1"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="IP:" android:layout_weight="0.04" /> <EditText android:id="@+id/SIP" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="0.65" android:text="192.168.1.102" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="端口:"/> <EditText android:id="@+id/IPort" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="0.17" android:text="8080" /> </LinearLayout> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="left" android:weightSum="1"> <Button android:id="@+id/bt_connect" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="连接"/> <Button android:id="@+id/bt_send" android:layout_width="88dp" android:layout_height="wrap_content" android:text="发送" android:layout_below="@+id/tv_content" android:layout_alignParentStart="true" tools:layout_editor_absoluteY="0dp" tools:layout_editor_absoluteX="8dp" /> <Button android:id="@+id/bt_stop" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=""/> </LinearLayout> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" android:gravity="left" android:weightSum="1"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="发送区:" android:layout_weight="0.04" /> <EditText android:id="@+id/sentText" android:layout_width="fill_parent" android:layout_height="wrap_content" /> <TextView android:id="@+id/tv_send_text" android:layout_width="241dp" android:layout_height="43dp" android:text="发送的内容" android:layout_below="@+id/bt_send" tools:layout_editor_absoluteX="8dp" tools:layout_editor_absoluteY="8dp" android:layout_weight="0.03" /> </LinearLayout> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" android:gravity="left" android:weightSum="1" android:layout_weight="0.12"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="接收区:" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="清空"/> <TextView android:id="@+id/tv_content" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="left" android:text="接收的内容" android:layout_alignParentTop="true" android:layout_alignParentStart="true" tools:layout_editor_absoluteY="0dp" tools:layout_editor_absoluteX="8dp" /> </LinearLayout> </LinearLayout> </android.support.constraint.ConstraintLayout>
3 工程文件
package com.espressif.myapplication; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.View; import android.view.View.OnClickListener; import android.widget.TextView; import android.widget.EditText; import android.widget.Button; import android.widget.Toast; public class MainActivity extends AppCompatActivity implements OnClickListener { // 1.1 定义 private Button bt_connect;// 链接 private Button send;// 发送 private Button bt_stop; //停止 public TextView tv_content; // 显示接收的命令 private TextView tv_send_text; // 显示发送的命令 private EditText SIP; //IP地址 private EditText IPort; //端口 private EditText sentText; // 发送内容 // 1.2 定义与服务器通信的子线程 boolean isConnect=true;//连接还是断开 ClientThread clientThread; Handler handler; // 2 主函数 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 2.1 控件初始化 InitView(); } // 1 界面控件初始化 private void InitView() { tv_content = (TextView) findViewById(R.id.tv_content); tv_send_text = (TextView) findViewById(R.id.tv_send_text); sentText=(EditText) findViewById(R.id.sentText); SIP=(EditText) findViewById(R.id.SIP); IPort=(EditText) findViewById(R.id.IPort); send = (Button) findViewById(R.id.bt_send); send.setOnClickListener(this); bt_connect=(Button) findViewById(R.id.bt_connect); bt_connect.setOnClickListener(this); bt_stop = (Button) findViewById(R.id.bt_stop); bt_stop.setOnClickListener(this); // 2.2 用于和主界面通信 handler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case 0x123: tv_content.setText("WiFi模块发送的:" + msg.obj.toString()); // tv_content.append(" " + msg.obj.toString()); break; } } }; } // 2 链接TCP/IP 链接目标服务 public void connectIP(){ int IPort__target=8080; String SIP_target="192.168.1.1"; if (SIP!= null) { SIP_target=SIP.getText().toString().trim(); } if (IPort!= null) { String msg = IPort.getText().toString().trim(); if (msg != null && msg.length() > 0) { IPort__target = Integer.parseInt(msg); } } // 2 开启服务器 if (isConnect == true) //标志位 = true表示连接 { // 链接IP clientThread = new ClientThread(handler); clientThread.setip_port(SIP_target, IPort__target); new Thread(clientThread).start(); if(clientThread.isConnect==false){ // if(clientThread.s.isConnected()&& !clientThread.s.isClosed()){ isConnect = true;//置为true bt_connect.setText("连接");//按钮上显示连接 } else{ isConnect = false;//置为false bt_connect.setText("断开");//按钮上显示--断开 } } else //标志位 = false表示退出连接 { isConnect = true;//置为true bt_connect.setText("连接");//按钮上显示连接 clientThread.stop_connect(); } } // 3 发送信息 public void Sendmsg(){ String msg="1123"; if( sentText!=null) { msg=sentText.getText().toString(); } //更新文本框 tv_send_text.setText(msg); // 发送函数 try { Message msga = new Message(); msga.what = 0x345; msga.obj = sentText.getText().toString(); clientThread.revHandler.sendMessage(msga); } catch (Exception e) { e.printStackTrace(); tv_send_text.setText("发送失败!"); } } // 4 按键点击触发器 @Override public void onClick(View v) { switch (v.getId()) { //1链接函数 case R.id.bt_connect: connectIP(); break; //2 发送信息 case R.id.bt_send: Sendmsg(); break; // 停止 case R.id.bt_stop: // 停止函数 break; } } }
package com.espressif.myapplication; import java.io.BufferedReader; import java.io.DataInputStream; import java.io.OutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.net.Socket; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.text.TextUtils; import java.net.SocketTimeoutException; public class ClientThread implements Runnable { private static int Port_target; private static String IP_target; boolean isConnect=true;//连接还是断开 public Socket s; // 该线程所处理的Socket所对应的输入流 BufferedReader br =null; // 输出 OutputStream os =null; // 定义向UI线程发送消息的Handler对象 private Handler handler; // 定义接收UI线程的消息的Handler对象 public Handler revHandler; // 2.1设置通线程向主界面通信对象 public ClientThread(Handler handler) { this.handler = handler; // 定义向UI线程发送消息的Handler对象 } // 2.2设置链接地址 public void setip_port(String IP,int port){ IP_target=IP; Port_target=port; } // 2.3从服务器接收信息线程 public void connectthread(){ // 启动一条子线程来读取服务器响应的数据 new Thread() { String content =null; @Override public void run() { // 不断读取Socket输入流中的内容。 try { while ((content = br.readLine())!=null) { // 每当读到来自服务器的数据之后,发送消息通知程序界面显示该数据 Message msg =new Message(); msg.what = 0x123; msg.obj = content; handler.sendMessage(msg); } } catch (IOException e) { e.printStackTrace(); } } }.start(); } // 2.4将接收命令更新主界面显示框 public void fruh_UI(){ // 为当前线程初始化Looper Looper.prepare(); // 创建revHandler对象 revHandler =new Handler() { @Override public void handleMessage(Message msg) { // 接收到UI线程中用户输入的数据 if (msg.what == 0x345) { // 将用户在文本框内输入的内容写入网络 try { os.write((msg.obj.toString() + " ") .getBytes("utf-8")); } catch (Exception e) { e.printStackTrace(); } } } }; // 启动Looper Looper.loop(); } // 2.4终止链接 public void stop_connect(){ try { isConnect = false; s.close();//关闭连接 s = null; } catch (IOException e) { e.printStackTrace(); } } public void run() {byte[] acceptdata1=null; try { s =new Socket(IP_target, Port_target); if(s.isConnected()&& !s.isClosed()){isConnect=true;} // 接收数据 br =new BufferedReader(new InputStreamReader( s.getInputStream())); //存放数据 os =s.getOutputStream(); // 新线程 接收数据 connectthread(); // 更新主界面显示框 fruh_UI(); } catch (SocketTimeoutException e1) { isConnect=false; System.out.println("网络连接超时!!"); } catch (Exception e) { e.printStackTrace(); } } }