不要让昨天 占据你的今天 夏午晴天

夏午晴天

手机服务器微架构设计与实现 之 http server

手机服务器微架构设计与实现 之 http server

·应用

 

 

 

·传输协议和应用层协议概念

 

 

TCP

 UDP

 TCP和UDP选择

三次握手(客户端与服务器端建立连接)/四次挥手(断开连接)过程图

 

 

·java Socket 基础

 

 

 

·Get 与 Post 协议格式

 

·开发真机与模拟器网络调试工具与配置

 真机:开发机和真机处于同一网段下即可

 模拟器:

 

·关键代码

 

  1 package com.example.lifen.simplehttpserver;
  2 
  3 import android.util.Log;
  4 
  5 import java.io.IOException;
  6 import java.io.InputStream;
  7 import java.net.InetSocketAddress;
  8 import java.net.ServerSocket;
  9 import java.net.Socket;
 10 import java.util.HashSet;
 11 import java.util.concurrent.ExecutorService;
 12 import java.util.concurrent.Executors;
 13 
 14 /**
 15  * Created by LiFen on 2017/12/27.
 16  */
 17 
 18 public class SimpleHttpServer {
 19     private static final String TAG = "SimpleHttpServer";
 20     private final WebConfiguration webConfig;
 21     private final ExecutorService threadPool;//线程池
 22     private final HashSet<IRsourceUriHandler> resourceUriHandlers;
 23     private boolean isEnable;
 24     private ServerSocket socket;
 25 
 26     public SimpleHttpServer(WebConfiguration webConfig){
 27         this.webConfig = webConfig;
 28         threadPool = Executors.newCachedThreadPool();
 29         resourceUriHandlers = new HashSet<>();
 30     }
 31 
 32     public void registerResourceHandler(IRsourceUriHandler iRsourceUriHandler){
 33         resourceUriHandlers.add(iRsourceUriHandler);
 34     }
 35 
 36     /**
 37      * 启动Server(异步)
 38      */
 39     public void startAsync(){
 40         isEnable = true;
 41         new Thread(new Runnable() {
 42             @Override
 43             public void run() {
 44                 doProcSync();
 45             }
 46         }).start();
 47     }
 48 
 49     /**
 50      * 停止Server(异步)
 51      */
 52     public void stopAsync() {
 53         if(!isEnable){
 54             return;
 55         }
 56         isEnable = false;
 57         try {
 58             socket.close();
 59         } catch (IOException e) {
 60             e.printStackTrace();
 61         }
 62         socket = null;
 63     }
 64 
 65     private void doProcSync() {
 66         Log.d(TAG, "doProcSync() called");
 67         try {
 68             InetSocketAddress socketAddr = new InetSocketAddress(webConfig.getPort());
 69             socket = new ServerSocket();
 70             socket.bind(socketAddr);
 71             while(isEnable){
 72                 final Socket remotePeer = socket.accept();
 73                 threadPool.submit(new Runnable() {
 74                     @Override
 75                     public void run() {
 76                         onAcceptRemotePeer(remotePeer);
 77                     }
 78                 });
 79             }
 80         } catch (IOException e) {
 81             Log.e(TAG, "doProcSync: e ", e);
 82         }
 83     }
 84 
 85     /**
 86      * 一个HTTP请求报文由请求行(request line)、请求头部(header)、空行和请求数据4个部分组成
 87      * @param remotePeer
 88      */
 89     private void onAcceptRemotePeer(Socket remotePeer) {
 90         Log.d(TAG, "onAcceptRemotePeer() called with: remotePeer.getRemoteSocketAddress() = [" + remotePeer.getRemoteSocketAddress() + "]");
 91         HttpContext  httpContext = new HttpContext();
 92         try {
 93 //            remotePeer.getOutputStream().write("congratulations, connected success".getBytes());
 94             httpContext.setUnderlySocket(remotePeer);
 95             InputStream nis = remotePeer.getInputStream();
 96             String headerLine = null;
 97             String readLine = StreamToolkit.readLine(nis);//http请求行(request line)1
 98             Log.i(TAG, "http 1 请求行  readLine =" + readLine);
 99             //测试请求行结果 readLine =POST /get/?key=dddd&sss=123456 HTTP/1.1
100             httpContext.setType(readLine.split(" ")[0]);
101             String resourceUri = headerLine = readLine.split(" ")[1];
102             Log.i(TAG, "地址 =" + headerLine);
103             while ((headerLine = StreamToolkit.readLine(nis)) != null) {//http请求头部(header)2
104 
105                 if (headerLine.equals("\r\n")) {//http请求空行3
106                     Log.i(TAG, "http 3 请求头部 /r/n ");
107                     break;
108                 }
109                 Log.i(TAG, "http 2 请求头部 headerLine = " + headerLine);
110                 String[] pair = headerLine.split(": ");
111                 if (pair.length > 1) {
112                     httpContext.addRequestHeader(pair[0], pair[1]);
113                 }
114             }
115 
116             for (IRsourceUriHandler handler : resourceUriHandlers) {
117                 if (!handler.accept(resourceUri)) {
118                     continue;
119                 }
120                 handler.postHandle(resourceUri, httpContext);//http请求数据4
121             }
122         } catch (IOException e) {
123             Log.e("spy",e.toString());
124         }finally {//只要流或socket不关闭,浏览器就收不到信息
125             try {
126                 remotePeer.close();
127             } catch (IOException e) {
128                 e.printStackTrace();
129             }
130         }
131     }
132 }

 

 1 package com.example.lifen.simplehttpserver;
 2 
 3 import android.graphics.Bitmap;
 4 import android.graphics.BitmapFactory;
 5 import android.os.Bundle;
 6 import android.support.v7.app.AppCompatActivity;
 7 import android.widget.ImageView;
 8 
 9 public class MainActivity extends AppCompatActivity {
10 
11     private SimpleHttpServer shs;
12     private ImageView imageView;
13 
14     @Override
15     protected void onCreate(Bundle savedInstanceState) {
16         super.onCreate(savedInstanceState);
17         setContentView(R.layout.activity_main);
18         imageView = (ImageView) findViewById(R.id.imageView);
19 
20         WebConfiguration wc = new WebConfiguration();
21         wc.setPort(8088);
22         wc.setMaxParallels(50);
23 
24         shs = new SimpleHttpServer(wc);
25         /*
26         向 shs 中添加 IRsourceUriHander 实例
27          */
28         shs.registerResourceHandler(new ResourceInAssetsHandler(this));
29         shs.registerResourceHandler(new UploadImageHandler(this){
30             @Override
31             public void onImageLoaded(final String path) {//重写onImageLoaded方法 在UiTread 主线程中更新Ui
32                 runOnUiThread(new Runnable() {
33                     @Override
34                     public void run() {
35                         Bitmap bitmap = BitmapFactory.decodeFile(path);
36                         imageView.setImageBitmap(bitmap);
37                     }
38                 });
39             }
40         });
41 
42         shs.startAsync();
43     }
44 
45     @Override
46     protected void onDestroy() {
47         super.onDestroy();
48         shs.stopAsync();
49     }
50 }
 1 package com.example.lifen.simplehttpserver;
 2 
 3 import android.content.Context;
 4 import android.util.Log;
 5 
 6 import java.io.IOException;
 7 import java.io.InputStream;
 8 import java.io.OutputStream;
 9 import java.io.PrintStream;
10 
11 /**
12  * Created by LiFen on 2017/12/29.
13  */
14 
15 public class ResourceInAssetsHandler implements IRsourceUriHandler {
16     private static final String TAG = "ResourceInAssetsHandler";
17     private String acceptPrefix = "/static/";
18     private Context context;
19 
20     public ResourceInAssetsHandler(Context context){
21         this.context = context;
22     }
23 
24     @Override
25     public boolean accept(String uri) {
26         /*
27         判断uri 是否以 “/static/" 开始
28          */
29         return uri.startsWith(acceptPrefix);
30     }
31 
32     @Override
33     public void postHandle(String uri, HttpContext httpContext) throws IOException{
34         Log.d(TAG, "postHandle() called with: uri = [" + uri + "], httpContext = [" + httpContext + "]");
35         /*
36         截取assets 文件夹下静态网页相对路径
37          */
38         int startIndex = acceptPrefix.length();
39         String assetsPath = uri.substring(startIndex);
40         Log.i(TAG, "assetsPath: " + assetsPath);
41 
42         /*
43         打开静态网页 返回一个输入流 转化为 byte[]
44          */
45         InputStream inputStream = context.getAssets().open(assetsPath);//打开文件 返回一个输入流
46         byte[] raw = StreamToolkit.readRawFromStream(inputStream);
47         Log.i(TAG, "assetsPath "+ "raw =" + raw.length);
48         inputStream.close();
49 
50         OutputStream outputStream = httpContext.getUnderlySocket().getOutputStream();//获取当前Socket的输出流
51         PrintStream printWriter = new PrintStream(outputStream);//输出符合http协议的信息给浏览器
52         printWriter.println("HTTP/1.1 200 OK");
53         printWriter.println("Content-Length:" + raw.length);
54 
55         if(assetsPath.endsWith(".html")){
56             printWriter.println("Content-Type:text/html");
57         }else if(assetsPath.endsWith(".js")){
58             printWriter.println("Content-Type:text/js");
59         }else if(assetsPath.endsWith(".css")){
60             printWriter.println("Content-Type:text/css");
61         }else if(assetsPath.endsWith(".jpg")){
62             printWriter.println("Content-Type:text/jpg");
63         }else if(assetsPath.endsWith(".png")){
64             printWriter.println("Content-Type:text/png");
65         }
66         printWriter.println();
67 
68         printWriter.write(raw);
69         Log.i(TAG, "postHandle: over");
70     }
71 }
 1 package com.example.lifen.simplehttpserver;
 2 
 3 import android.app.Activity;
 4 import android.os.Environment;
 5 import android.util.Log;
 6 
 7 import java.io.File;
 8 import java.io.FileOutputStream;
 9 import java.io.IOException;
10 import java.io.InputStream;
11 import java.io.OutputStream;
12 import java.io.PrintWriter;
13 
14 
15 /**
16  * Created by LiFen on 2017/12/30.
17  */
18 
19 class UploadImageHandler implements IRsourceUriHandler {
20     private static final String TAG = "UploadImageHandler";
21     String acceptPrefix = "/image/";
22     Activity activity;
23 
24     public UploadImageHandler(Activity activity){
25         this.activity = activity;
26     }
27 
28     @Override
29     public boolean accept(String uri) {
30         return uri.startsWith(acceptPrefix);
31     }
32 
33     @Override
34     public void postHandle(String uri, HttpContext httpContext) throws IOException {
35         long totalLength = Long.parseLong(httpContext.getRequestHeaderValue("Content-Length").trim());
36         /*
37         将 接收到的 图片存储到本机 SD卡目录下
38          */
39         File file = new File(Environment.getExternalStorageDirectory(),
40                 "tmpFile.jpg");
41         Log.i(TAG, "postHandle: " +"totalLength=" + totalLength + " file    getPath=" + file.getPath() );
42         if(file.exists()){
43             file.delete();
44         }
45         byte[] buffer = new byte[10240];
46         int read;
47         long nLeftLength = totalLength;
48         FileOutputStream fileOutputStream = new FileOutputStream(file.getPath());//文件输出流
49         InputStream inputStream = httpContext.getUnderlySocket().getInputStream();//从当前 Socket 中得到文件输入流
50         while (nLeftLength > 0 && (read = inputStream.read(buffer)) > 0){//写到文件输出流中,即存储到SD 卡路径下
51             fileOutputStream.write(buffer,0,read);
52             nLeftLength -= read;
53         }
54         Log.i(TAG, "postHandle: close");
55         fileOutputStream.close();
56 
57         /*
58         从当前 Socket 中得到 文件输出流,将 符合http协议的信息 返回给浏览器
59          */
60         OutputStream outputStream = httpContext.getUnderlySocket().getOutputStream();
61         PrintWriter printWriter = new PrintWriter(outputStream);
62         printWriter.print("HTTP/1.1 200 OK");
63         printWriter.println();
64         /*
65         记录图片存储的位置
66          */
67         onImageLoaded(file.getPath());
68     }
69 
70     public void onImageLoaded(String path){
71         Log.d(TAG, "onImageLoaded() called with: path = [" + path + "]");
72     }
73 }
 1 package com.example.lifen.simplehttpserver;
 2 
 3 import java.net.Socket;
 4 import java.util.HashMap;
 5 import java.util.Map;
 6 
 7 /**
 8  * Created by LiFen on 2017/12/28.
 9  */
10 
11 public class HttpContext {
12     private Socket underlySocket;
13     Map<String,String> heard = new HashMap<>();
14     private String type;
15 
16     public void setUnderlySocket(Socket underlySocket){
17         this.underlySocket = underlySocket;
18     }
19 
20     public void addRequestHeader(String key,String value){
21         heard.put(key,value);
22     }
23 
24     public Socket getUnderlySocket(){
25         return underlySocket;
26     }
27 
28     public String getRequestHeaderValue(String key){
29         return heard.get(key);
30     }
31 
32     public String getType(){
33         return type;
34     }
35 
36     public void setType(String type){
37         this.type = type;
38     }
39 }
 1 package com.example.lifen.simplehttpserver;
 2 
 3 import java.io.ByteArrayOutputStream;
 4 import java.io.IOException;
 5 import java.io.InputStream;
 6 
 7 /**
 8  * Created by LiFen on 2017/12/27.
 9  */
10 
11 public class StreamToolkit {
12     public static final String readLine(InputStream nis) throws IOException {
13         StringBuffer sb = new StringBuffer();
14         int c1 = 0;
15         int c2 = 0;
16         while (c2 != -1 && !(c1 == '\r' && c2 == '\n')){
17             c1 = c2;
18             c2 = nis.read();
19             sb.append((char)c2);
20         }
21         if(sb.length() == 0) {
22             return null;
23         }
24         return sb.toString();
25     }
26 
27     public static byte[] readRawFromStream(InputStream inputStream) throws IOException {
28         ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
29         byte[] buffer = new byte[10240];
30         int read;
31         while ((read = inputStream.read(buffer)) > 0){
32             outputStream.write(buffer,0,read);
33         }
34         return outputStream.toByteArray();
35     }
36 
37 }
 1 package com.example.lifen.simplehttpserver;
 2 
 3 /**
 4  * Created by LiFen on 2017/12/27.
 5  */
 6 
 7 public class WebConfiguration {
 8     /**
 9      * 端口
10      */
11     private int port;
12     /**
13      * 最大监听数
14      */
15     private int maxParallels;
16 
17     public int getPort() {
18         return port;
19     }
20 
21     public void setPort(int port) {
22         this.port = port;
23     }
24 
25     public int getMaxParallels() {
26         return maxParallels;
27     }
28 
29     public void setMaxParallels(int maxParallels) {
30         this.maxParallels = maxParallels;
31     }
32 }
 1 package com.example.lifen.simplehttpserver;
 2 
 3 import java.io.IOException;
 4 
 5 /**
 6  * Created by LiFen on 2017/12/29.
 7  */
 8 
 9 public interface IRsourceUriHandler {
10     boolean accept(String uri);
11     void postHandle(String uri, HttpContext httpContext) throws IOException;
12 }

 

此项目github下载地址:

https://github.com/li-fengjie/SimpleHttpServer

win10正式版telnet不是内部或外部命令解决办法:

https://jingyan.baidu.com/article/1e5468f9033a71484961b7d7.html

 

posted on 2017-12-30 21:57  夏晴天  阅读(971)  评论(0编辑  收藏

导航

统计

Live2D