Android 手机微服务架构的简单实现

来源:互联网 发布:编程电极怎么编 编辑:程序博客网 时间:2024/05/31 19:47
功能:
使得手机app具有服务端功能,例如:wifi传书,可以通过手机app产生一个url地址,然后使用同一网段的电脑,打开浏览器,输入ip地址打开网页,上传文件,这样手机app就可以接受到这个文件,完成整个数据上传的过程。

知识储备:
通信协议:
TCP/IP 面向连接--需要连接之后才能传输数据(三次握手,四次挥手)数据可靠性--数据校验保证完整性,传输ack防丢包
UDP 不面向连接 不提供数据可靠性验证 速度快
容错性高,速度快,可以接受丢包使用UDP。

Socket:
网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。
ServerSocket 对socket进行简单封装
1、对指定端口监听--bind
2、等待连接--accept--对应三次握手的结束--接受到连接之后返回远程的socket
3、inputstream\outputstream 远程端口的输入输出流
4、关闭连接--close

http 请求格式:



http 相应格式:


路由规则:
客户端访问服务端以后,交给谁来处理。

代码:
网络配置类
WebConfiguration
public class WebConfiguration {    // 端口号    private int port;    // 最大并发数    private int maxParallels;    public int getPort() {        return port;    }    public void setPort(int port) {        this.port = port;    }    public int getMaxParallels() {        return maxParallels;    }    public void setMaxParallels(int maxParallels) {        this.maxParallels = maxParallels;    }}


服务类--启动和停止服务,不断监听,针对不同的请求进行不同的处理。
SimpleHttpServer
public class SimpleHttpServer {    private final WebConfiguration webConfig;    private final ExecutorService threadPool;    private boolean isEnable;    private ServerSocket socket;    private Set<IResourceHandler> resourceHandlers;    SimpleHttpServer(WebConfiguration webConfig) {        this.webConfig = webConfig;        threadPool = Executors.newCachedThreadPool();        resourceHandlers = new HashSet<IResourceHandler>();    }    /**     * 启动Server(异步)     */    public void startAsync() {        isEnable = true;        new Thread(new Runnable() {            @Override            public void run() {                doProcSync();            }        }).start();    }    /**     * 停止Server(异步)     */    public void stopAsync() throws IOException {        if (!isEnable || socket == null) {            return;        }        isEnable = false;        socket.close();        socket = null;    }    private void doProcSync() {        try {            // 创建端口地址            InetSocketAddress socketAddress = new InetSocketAddress(webConfig.getPort());            socket = new ServerSocket();            // 监听端口            socket.bind(socketAddress);            while (isEnable) {                // 当远程有设备连接当前端口时,返回远程端口                final Socket remotePeer = socket.accept();                threadPool.submit(new Runnable() {                    @Override                    public void run() {                        Log.d("spy", "a remote peer accepted..." + remotePeer.getRemoteSocketAddress().toString());                        onAcceptedRemotePeer(remotePeer);                    }                });            }        } catch (IOException e) {            Log.e("spy", e.toString());        }    }    public void registerResourceHandler(IResourceHandler handler) {        resourceHandlers.add(handler);    }    private void onAcceptedRemotePeer(Socket remotePeer) {        try {            HttpContext context = new HttpContext();            context.setUnderlySocket(remotePeer);//            remotePeer.getOutputStream().write("congratulations, connect successful".getBytes());            InputStream nis = remotePeer.getInputStream();            String headLine = null;            String resourceUri = headLine = StreamTookit.readLine(nis).split(" ")[1];            Log.d("spy", resourceUri);            while ((headLine = StreamTookit.readLine(nis)) != null) {                if (headLine.equals("\r\n")) {                    break;                }                String[] pair = headLine.split(": ");                if (pair.length > 1) {                    context.addRequestHeaders(pair[0], pair[1].replace("\r\n", ""));                }                Log.d("spy", "header line = " + headLine);            }            for (IResourceHandler handler : resourceHandlers) {                if (!handler.accept(resourceUri)) {                    continue;                }                handler.handle(resourceUri, context);            }        } catch (IOException e) {            Log.e("spy", e.toString());        } finally {            try {                remotePeer.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }}
启动服务之后,就会调用doProcSync函数,这个函数可以循环监听是否有远程连接到服务端。
首先根据网络配置监听指定的端口,然后监听,一旦有远程连接就返回远程socket,把他交给一个线程池中的某个线程进行处理,避免延迟和频繁的线程创建和销毁,影响其他连接的监听。
onAcceptedRemotePeer是真正的处理函数。可以从远程socket的inputstream获取网络请求头的具体内容,还可以根据url的内容把他交给相匹配的handler进行处理。
网络请求头的内容是有‘/r/n’来分行的,而我们需要的url具体请求地址在第一行,第二个字段中。
IResourceHandler
public interface IResourceHandler {    boolean accept(String uri);    void handle(String uri, HttpContext httpContext) throws IOException;}

处理访问静态html的handler
如果路径是ip/static/xxx,就使用这个handler
public class ResourceInAssetsHandler implements IResourceHandler {    private final Context context;    private String acceptPrefix = "/static/";    ResourceInAssetsHandler(Context context) {        this.context = context;    }    @Override    public boolean accept(String uri) {        return uri.startsWith(acceptPrefix);    }    @Override    public void handle(String uri, HttpContext httpContext) throws IOException {        int startIndex = acceptPrefix.length();        String assetsPath = uri.substring(startIndex);        InputStream fis = context.getAssets().open(assetsPath);        byte[] raw = StreamTookit.readRawFromStrem(fis);        fis.close();        OutputStream nos = httpContext.getUnderlySocket().getOutputStream();        PrintStream print = new PrintStream(nos);        print.println("HTTP/1.1 200 OK");        print.println("Content-Length:" + raw.length);        if (assetsPath.endsWith(".html")) {            print.println("Content-Type:text/html");        } else if (assetsPath.endsWith(".js")) {            print.println("Content-Type:text/js");        } else if (assetsPath.endsWith(".css")) {            print.println("Content-Type:text/css");        } else if (assetsPath.endsWith(".jpg")) {            print.println("Content-Type:text/jpg");        } else if (assetsPath.endsWith(".png")) {            print.println("Content-Type:text/png");        }        print.println();        print.write(raw);        print.close();    }}
处理上传图片的handler
如果路径是ip/upload_image/xxx,就使用该handler.
public class UploadImageHandler implements IResourceHandler {    private String acceptPrefix = "/upload_image/";    @Override    public boolean accept(String uri) {        return uri.startsWith(acceptPrefix);    }    @Override    public void handle(String uri, HttpContext httpContext) throws IOException {        String tmpPath = Environment.getExternalStorageDirectory().getPath() + "/test_upload.jpg";        long totalLength = Long.parseLong(httpContext.getRequestHeaderValue("Content-Length"));        FileOutputStream fos = new FileOutputStream(tmpPath);        InputStream nis = httpContext.getUnderlySocket().getInputStream();        byte[] buffer = new byte[10240];        int nReaded = 0;        long nLeftLength = totalLength;        while ((nLeftLength > 0) && (nReaded = nis.read(buffer)) > 0) {            fos.write(buffer, 0, nReaded);            nLeftLength -= nReaded;        }        fos.close();        OutputStream nos = httpContext.getUnderlySocket().getOutputStream();        PrintStream writer = new PrintStream(nos);        writer.println("HTTP/1.1 200 OK");        writer.println();        onImageLoaded(tmpPath);    }    protected void onImageLoaded(String path) {    }}

MainActivity
public class MainActivity extends Activity {    private SimpleHttpServer shs;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        WebConfiguration configuration = new WebConfiguration();        configuration.setPort(8088);        configuration.setMaxParallels(50);        shs = new SimpleHttpServer(configuration);        shs.registerResourceHandler(new ResourceInAssetsHandler(this));        shs.registerResourceHandler(new UploadImageHandler() {            @Override            protected void onImageLoaded(String path) {                showImage(path);            }        });        shs.startAsync();    }    private void showImage(final String path) {        runOnUiThread(new Runnable() {            @Override            public void run() {                ImageView img = (ImageView) findViewById(R.id.img);                Bitmap bitmap = BitmapFactory.decodeFile(path);                img.setImageBitmap(bitmap);                Toast.makeText(MainActivity.this, "image received and show", Toast.LENGTH_SHORT).show();            }        });    }    @Override    protected void onDestroy() {        try {            shs.stopAsync();        } catch (IOException e) {            Log.e("spy", e.toString());        }        super.onDestroy();    }}

layout_main
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical">    <ImageView        android:id="@+id/img"        android:layout_width="match_parent"        android:layout_height="match_parent" /></LinearLayout>
HttpContext
public class HttpContext {    private final HashMap<String, String> requestHeaders;    private Socket underlySocket;    HttpContext() {        requestHeaders = new HashMap<String, String>();    }    public void setUnderlySocket(Socket socket) {        this.underlySocket = socket;    }    public Socket getUnderlySocket() {        return underlySocket;    }    public void addRequestHeaders(String headerName, String headerValue) {        requestHeaders.put(headerName, headerValue);    }    public String getRequestHeaderValue(String headerName) {        return requestHeaders.get(headerName);    }}

工具类
读取请求的每一行并返回。
public class StreamTookit {    public static final String readLine(InputStream nis) throws IOException {        StringBuffer sb = new StringBuffer();        int c1 = 0;        int c2 = 0;        while (c2 != -1 && !(c1 == '\r' && c2 == '\n')) {            c1 = c2;            c2 = nis.read();            sb.append((char) c2);        }        if (sb.length() == 0) {            return null;        }        return sb.toString();    }    public static byte[] readRawFromStrem(InputStream fis) throws IOException {        ByteArrayOutputStream bos = new ByteArrayOutputStream();        byte[] buffer = new byte[10240];        int nReaded;        while ((nReaded = fis.read(buffer)) > 0) {            bos.write(buffer, 0, nReaded);        }        return bos.toByteArray();    }}





0 0