您的位置:首页 > 运维架构 > 网站架构

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

2017-01-15 16:23 357 查看
功能:
使得手机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();
}
}



                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: