安卓socket实例(三)实现OPN、DIR操作,优化架构,制定好后面代码编写的目标蓝图,实现安卓端命令操作
idea服务端更新
之前的内容中我们实现了idea服务端实现NetFileData返回文件列表的操作,在此基础上我们设计架构,设置一个Operator类来专门处理命令调用各种功能类,实现服务端传入的命令,并返回指定信息,按次思路对idea端做出如下操作。
创建BaseOperator.java,设立exe()方法,让各功能函数继承。
package lrz.base;
import java.util.ArrayList;
public abstract class BaseOperator {
public abstract ArrayList<String> exe(String cmdBody) throws Exception ;
}
复制代码
将NetFileData.java更名为DIR.java,继承BaseOperator类,更改如下
package lrz.data;
import lrz.base.BaseOperator;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
public class DIR extends BaseOperator {
public ArrayList<String> exe(String cmdBody) throws Exception {
// TODO Auto-generated method stub
ArrayList<String> backList=new ArrayList<String>();
File file = new File(cmdBody);
File[] listFiles = file.listFiles();
for(File mfile:listFiles){
String fileName = mfile.getName();
long lastModified = mfile.lastModified();//获取文件修改时间
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//给时间格式,例如:2018-03-16 09:50:23
String fileDate = dateFormat.format(new Date(lastModified));//取得文件最后修改时间,并按格式转为字符串
String fileSize="0";
String isDir="1";
if(!mfile.isDirectory()){//判断是否为目录
isDir="0";
fileSize=""+mfile.length();
}
backList.add(fileName+">"+fileDate+">"+fileSize+">"+isDir+">");
}
return backList;
}
}
复制代码
创建OPN.java类实现打开文件功能
package lrz.data;
import lrz.base.BaseOperator;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
public class OPN extends BaseOperator {
public ArrayList<String> exe(String cmdBody) throws Exception {
ArrayList<String> backList=new ArrayList<String>();
Desktop desk=Desktop.getDesktop();
File file=new File(cmdBody);//创建一个java文件系统
try {
desk.open(file); //调用open(File f)方法打开文件
backList.add("成功运行文件:"+cmdBody);
} catch (IOException ex) {
ex.printStackTrace();
System.out.println(ex.toString());
backList.add(ex.toString());
}
return backList;
}
}
复制代码
创建Operator.java负责处理传入的命令,调用指定函数类实现命令功能,并在获得功能类返回结果后在头部添加命令标签,为安卓端分析返回数据提供便捷。
package lrz.tool;
import lrz.data.DIR;
import lrz.data.OPN;
import java.util.ArrayList;
public class Operator {
public static ArrayList<String> exeCmd(String cmd) throws Exception {
//所有的命令操作在此静态函数中判断及调用返回
//后续新增的命令判断操作在此函数中添加
String[] splitCmd = splitCmd(cmd);//按":"分割命令
// 分割时,会把命令前缀转为小写,以保证判断不分大小写
String cmdHead = splitCmd[0];
String cmdBody = splitCmd[1];
ArrayList<String> msgBackList = new ArrayList<String>();
if (cmdHead.equals("dir")) {
msgBackList = new DIR().exe(cmdBody);
msgBackList.add(0,"dir");//增加正常执行返回的代码"ok"
return msgBackList;
}
if (cmdHead.equals("opn")) {
msgBackList = new OPN().exe(cmdBody); // 待实现的打开文件命令
msgBackList.add(0,"opn");//增加正常执行返回的代码"ok"
return msgBackList;
}
//... 继续判断其他命令
if(msgBackList.size()==0){
msgBackList.add(0,"无效命令:"+cmd);//msgBackList长度为0,说明命令没有得到解析和执行
}
throw new Exception("无效命令!");//若代码正确执行了,就return了,不会执行到这里,执行到这说明出错了
}
public static String[] splitCmd(String cmd) throws Exception {
String[] cmdout = null;
int splitIdx = cmd.indexOf(":");
System.out.println("服务端接受命令: " + cmd);
if (splitIdx < 1) {
throw new Exception("非法命令: " + cmd);// 抛出异常
} else {
cmdout = new String[2];
String cmdHead = cmd.substring(0, splitIdx);
String cmdBody = cmd.substring(splitIdx + 1);
cmdout[0] = cmdHead.toLowerCase();//按小写处理字符串
cmdout[1] = cmdBody;
}
return cmdout;
}
}
复制代码
ServerSocket01.java更改如下
package lrz.server;
import lrz.tool.Operator;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Enumeration;
public class ServerSocket01 {
int port = 8019;// 自定义一个端口,端口号尽可能挑选一些不被其他服务占用的端口,祥见http://blog.csdn.net/hsj521li/article/details/7678880
ArrayList<String> msgBackList;
public ServerSocket01() {
// TODO Auto-generated constructor stub
}
public ServerSocket01(int port) {
super();
this.port = port;
}
private void printLocalIp(ServerSocket serverSocket) {// 枚举打印服务端的IP
try {
System.out.println("服务端命令端口prot=" + serverSocket.getLocalPort());
Enumeration<NetworkInterface> interfaces = null;
interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
NetworkInterface ni = interfaces.nextElement();
Enumeration<InetAddress> addresss = ni.getInetAddresses();
while (addresss.hasMoreElements()) {
InetAddress nextElement = addresss.nextElement();
String hostAddress = nextElement.getHostAddress();
System.out.println("本机IP地址为:" + hostAddress);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void work() throws Exception {
// 注意:由于Socket的工作是阻塞式,Android端Socket的工作必须在新的线程中实现,若在UI主线程中工作会报错
ServerSocket serverSocket = new ServerSocket(port);
printLocalIp(serverSocket);
while (true) {// 无限循环
System.out.println("=================================");
System.out.println("| Waiting client to connect.....|");
System.out.println("=================================");
Socket socket = serverSocket.accept();// 阻塞式
System.out.println("连接请求来自: "+ socket.getRemoteSocketAddress().toString());
try{
getAndDealCmd(socket);
} catch (Exception e) {
cmdFail(e.toString());
}
SocketMsg.writeBackMsg(socket,msgBackList);
System.out.println("..............输出流..............");
msgBackList.forEach(s -> System.out.println(s));
System.out.println(".................................");
socket.close();
System.out.println("本次Socket服务结束");
System.out.println("---------------------------------");
System.out.println("---------------------------------");
}
}
public void getAndDealCmd(Socket socket) throws Exception {
ArrayList<String> cmdList = SocketMsg.readSocketMsg(socket);
if(cmdList.size()==0){
cmdFail("Cmd输入为空. ");//若命令长度0行,则返回错误信息
}
System.out.println("..............输入流..............");
cmdList.forEach(s -> System.out.println(s));
System.out.println(".................................");
if(cmdList.size()==1){
msgBackList=Operator.exeCmd(cmdList.get(0));// Operator类为自定义类,实现命令头部和主体的分离和调用判断
}else{
// msgBackList=MultiOperator.exeCmd(cmdList);//待实现的,支持多条命令串行执行
}
}
private void cmdFail(String e) {
msgBackList.clear();//
String nu="java.lang.NullPointerException";
if(e.equals(nu)){
e="目标不存在";
}
msgBackList.add(e);//将出错信息放入msgBackList
}
/**
* @param args
*/
public static void main(String[] args) throws Exception {
new ServerSocket01().work();
}
}
复制代码
安卓端更新
SocketClient.java添加对服务端返回命令头部命令类型的解析,设置handler返回的type,例如SERVER_MSG_DIR,借此在MainActivity.java得以让handler监听到的数据根据不同的命令返回结果进行不同的操作。
package com.example.android_app;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;
public class SocketClient {
public static int SERVER_MSG_OK=0;//用于发送给句柄的消息类型,放在消息的arg2中,表示服务端正常
public static int SERVER_MSG_ERROR=1;//表示服务端出错
public static int SERVER_MSG_DIR=2;//表示DIR操作返回
public static int SERVER_MSG_OPN=3;//表示OPN操作返回
private int msgType;
private String ip;
private int port;
private int connect_timeout=10000;
private Handler handler;
private Socket socket;
public static final String KEY_SERVER_ACK_MSG = "KEY_SERVER_ACK_MSG";
private OutputStreamWriter writer;
private BufferedReader bufferedReader;
public SocketClient(String ip, int port, Handler handler) {
this.port = port;
this.ip = ip;
this.handler = handler;
}
private void connect() throws IOException {//连接服务端函数
InetSocketAddress address = new InetSocketAddress(ip, port);
socket = new Socket();
socket.connect(address, connect_timeout);
}
private void writeCmd(String cmd) throws IOException {
BufferedOutputStream os=new BufferedOutputStream(socket.getOutputStream());
writer=new OutputStreamWriter(os,"UTF-8");
writer.write("1\n");
writer.write(cmd+"\n");
writer.flush();
}
private ArrayList<String> readSocketMsg() throws IOException {
ArrayList<String> msgList=new ArrayList<>();
InputStreamReader isr=new InputStreamReader(socket.getInputStream(),"UTF-8");
bufferedReader=new BufferedReader(isr);
String numStr = bufferedReader.readLine();
int linNum = Integer.parseInt(numStr);
msgType=SERVER_MSG_ERROR;
if(linNum<1){
msgList.add("服务端返回为空");
return msgList;
}
String status = bufferedReader.readLine();
if(status.equalsIgnoreCase("OK")){
msgType=SERVER_MSG_OK;
}else if(status.equalsIgnoreCase("DIR")){
msgType=SERVER_MSG_DIR;
}else if(status.equalsIgnoreCase("OPN")){
msgType=SERVER_MSG_OPN;
}else{
msgList.add(status);//将服务端的错误信息放入消息列表
}
for (int i = 1; i <linNum ; i++) {
String s = bufferedReader.readLine();
msgList.add(s);
}
return msgList;
}
private void close() throws IOException {
bufferedReader.close();
writer.close();
socket.close();
}
private void doCmdTask(String cmd){
ArrayList<String> msgList=new ArrayList<>();
try {
connect();
writeCmd(cmd);
msgList = readSocketMsg();
close();
} catch (IOException e) {
msgType=SERVER_MSG_ERROR;
msgList.add(e.toString());
e.printStackTrace();
}
Message message = handler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putStringArrayList(KEY_SERVER_ACK_MSG,msgList);
message.arg2=msgType;
message.setData(bundle);
handler.sendMessage(message);
}
public void work(final String cmd){
new Thread(new Runnable() {
@Override
public void run() {
doCmdTask(cmd);
}
}).start();
}
}
复制代码
Activity_main.xml添加一个textview来显示操作信息
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:id="@+id/url"
android:text="服务端ip"/>
<EditText
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:id="@+id/way"
android:text="8019"/>
</LinearLayout>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/dir"
android:text="d://"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="submit"
android:id="@+id/submit"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/show_msg"
android:text="show_msg"/>
<ListView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/listview"/>
</LinearLayout>
复制代码
MainActivity.java更改如下,添加handler对不同返回type的判定与不同操作,并且将操作结果显示在一个Textview中
package com.example.android_app;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import androidx.annotation.NonNull;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
public static final String KEY_SERVER_ACK_MSG = "KEY_SERVER_ACK_MSG";
private Handler handler = null;
EditText url,way,dir;
ListView lv;
Button submit;
SocketClient socketClient=null;
String here;
ArrayList<String> data;
int port;
TextView show_msg;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
url=findViewById(R.id.url);
way=findViewById(R.id.way);
dir=findViewById(R.id.dir);
lv=findViewById(R.id.listview);
submit=findViewById(R.id.submit);
show_msg=findViewById(R.id.show_msg);
handler=new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
int fp=msg.arg2;
Bundle data_bundle = msg.getData();
data=data_bundle.getStringArrayList(KEY_SERVER_ACK_MSG);
if(fp==SocketClient.SERVER_MSG_DIR){
data=dataMaker();
printAdapter(data);
show_msg.setText("成功执行DIR操作");
}else if(fp==SocketClient.SERVER_MSG_OPN){
show_msg.setText("成功执行OPN操作");
}
return false; }
});
submit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
port=Integer.parseInt(way.getText().toString());
here=dir.getText().toString();
getdata();
}
});
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
here=here+"/"+data.get(position);
getdata();
}
});
}
private void getdata() {
socketClient=new SocketClient(url.getText().toString(),port,handler);
socketClient.work(here);
}
private ArrayList<String> dataMaker() {
ArrayList<String> dataResult=new ArrayList<>();
int i=data.size();
for (int j = 0; j <i ; j++) {
String str=data.get(j);
str=str.substring(0,str.indexOf(">"));
dataResult.add(str);
}
return dataResult;
}
private void printAdapter(ArrayList<String> data) {
ArrayAdapter<String> arrayAdapter=new ArrayAdapter<>(this,android.R.layout.simple_list_item_1,data);
lv.setAdapter(arrayAdapter);
}
}
复制代码
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END