1 需求
在同一局域网内,手机(PC端)浏览器作为客户端,然后手机app里面通过socket写服务代码,然后浏览器访问手机服务端的网页和图片和css文件和下载APK
2 代码实现
创建服务线程代码
var serverSocket: ServerSocket? = null
var run = true
inner class HttpServerThread : Runnable {
var TAG = javaClass.name
var context : Context?= null
constructor(context : Context){
this.context = context
}
override fun run() {
Log.i(TAG, "await start")
try {
//Android9.0以下开启了热点服务器的IP值是192.168.43.1,如果Android版本大于9.0 IP服务器地址会随机变
serverSocket = ServerSocket(SocketContants.HTTP_SERVER_PORT)
// serverSocket = ServerSocket(SocketContants.HTTP_SERVER_PORT, 10, InetAddress.
// ("192.168.43.1"))
} catch (e: IOException) {
Log.i(TAG, "has error")
e.printStackTrace()
}
if (serverSocket == null) {
Log.i(TAG, "serverSocket == null")
return;
}
while (run) {
Log.i(TAG, "while (true) start")
var socket: Socket? = null
var input: InputStream? = null
var output: OutputStream? = null
try { //接收了客户端发来的请求,否则一致是等待状态
Log.i(TAG, "serverSocket.accept() start")
socket = serverSocket!!.accept()
Log.i(TAG, "serverSocket.accept() after")
input = socket.getInputStream()
output = socket.getOutputStream()
// create Request object and parse
val request = Request(input)
request.parse() //从请求中读取内容
Log.i(TAG, "shutdown.req=" + request.uri)
val response = Response(output, context)
response.setRequest(request)
response.sendDataToClient()
socket.close()
} catch (e: Exception) {
e.printStackTrace()
Log.i(TAG, "Exception has error ")
}
}
}
}
开启服务代码
Thread(context?.let { HttpServerThread(it) }).start()
在onDestroy里面关闭服务代码
//关闭socket
if (serverSocket != null) {
Log.i(TAG, "serverSocket != null and close")
serverSocket!!.close()
}
run = false
Request.java代码
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
public class Request {
public static final String TAG = "Request";
private static final int SIZE = 2048;
private InputStream input;
private String uri;
public Request(InputStream input) {
this.input = input;
}
public void parse() {
StringBuffer request = new StringBuffer(SIZE);
int i;
byte[] buffer = new byte[SIZE];
try {
i = input.read(buffer); //将从输入流取2048长度的内容存到buffer字节数组中,如果内容不到2048,数组其他空间剩余空着
} catch (IOException e) {
e.printStackTrace();
i = -1;
}
for (int j = 0; j < i; j++) {
request.append((char) buffer[j]);
}
Log.i(TAG, "************Http-request*****************");
Log.i(TAG, request.toString());
uri = parseUri(request.toString().replace('/', '\\'));
Log.i(TAG, "uri is:" + uri);
}
private String parseUri(String requestString) {
int index1, index2;
index1 = requestString.indexOf(' ');
Log.i(TAG, "index1 is:" + index1);
/*
* http请求行的结构:方法 统一资源标识符(URI) 协议/版本(它们之间以空格分隔)
* 例如:POST //examples/default.jsp HTTP/1.1
*/
if (index1 != -1) {// index1 == -1表示没找到
index2 = requestString.indexOf(' ', index1 + 1);//从index+1位置开始寻找‘ ’
Log.i(TAG, "index2 is:" + index2);
if (index2 > index1)
return requestString.substring(index1 + 1, index2);
}
return null;
}
public String getUri() {
return uri;
}
}
Response.java代码
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
public class Response {
public static final String TAG = "Response";
private static final int BUFFER_SIZE = 1024;
public Context conext;
Request request;
OutputStream output;
DataOutputStream sout;
public Response(OutputStream output, Context conext) {
this.output = output;
sout = new DataOutputStream(output);
this.conext = conext;
}
public Response(OutputStream output) {
this.output = output;
sout = new DataOutputStream(output);
}
public void setRequest(Request request) {
this.request = request;
}
public void sendDataToClient() {
String url = request.getUri();
Log.i(TAG, "url is:" + url);
if (url != null && "\\".equals(url)) {
//发送apk
// sendAPK(Constants.APK_NAME);
sendHtml();
// sendImage();
} else if (url != null && url.contains("png")){
sendImage();
} else if (url != null && url.contains("css")){
String cssName = getRequestFileName(url);
Log.i(TAG, "css Name is" + cssName);
sendCss(cssName);
} else if (url != null && url.contains("apk")) {
sendAPK(Constants.APK_NAME);
}
}
/**
* 解析url url is:\192.168.43.1:9999\download.css url is:\ url is:\192.168.43.1:9999\ic_launcher_round.png
*/
public String getRequestFileName(String url) {
if (TextUtils.isEmpty(url)) {
return null;
}
String ss = "\\\\";
String[] paths = url.split(ss);
if (paths == null || paths.length == 0) {
Log.i(TAG, "paths is null");
return "";
}
String name = paths[paths.length - 1];
Log.i(TAG, "getNameByImagePath name is:" + name);
return name;
}
public void sendHtml() {
try {
String line="HTTP/1.1 200 OK \r\n";
Log.i(TAG,"line="+line);
sout.write(line.getBytes());//用字节传输,不能用字符,浏览器无法解析
byte[] buff = new byte[1024];
conext.getAssets().open("share/download.html").read(buff);
String header="Content-Type: text/html; charset=utf-8 \r\n"
+"Content-length: "+ buff.length +" \r\n\r\n";
Log.i(TAG,"header="+header);
sout.writeBytes(header);
sout.write(buff);
sout.flush();
sout.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (sout != null)
try {
sout.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void sendCss(String fileName) {
try {
String line="HTTP/1.1 200 OK \r\n";
Log.i(TAG,"line="+line);
sout.write(line.getBytes());//用字节传输,不能用字符,浏览器无法解析
byte[] buff = new byte[1024 * 100];
conext.getAssets().open("share/" + fileName).read(buff);
String header="Content-Type: text/css; charset=utf-8 \r\n"
+"Content-length: "+ buff.length +" \r\n\r\n";
Log.i(TAG,"header="+header);
sout.writeBytes(header);
sout.write(buff);
sout.flush();
sout.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (sout != null)
try {
sout.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void sendImage() {
try {
String line="HTTP/1.1 200 OK \r\n";
Log.i(TAG,"line="+line);
sout.write(line.getBytes());//用字节传输,不能用字符,浏览器无法解析
byte[] buff = new byte[1024 * 100];
conext.getAssets().open("share/ic_launcher_round.png").read(buff);
String header="Content-Type: image/jpeg; charset=utf-8 \r\n"
+"Content-length: "+ buff.length +" \r\n\r\n";
Log.i(TAG,"header="+header);
sout.writeBytes(header);
sout.write(buff);
sout.flush();
sout.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (sout != null)
try {
sout.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void sendAPK(String fileName) {
FileInputStream fis = null;
try {
Log.i(TAG, "request.getUri() is:" + request.getUri());
String apkPath = SPHelper.getInstance().getString(SpConstants.CURRENT_APK_PATH, "");
Log.i(TAG, "apkPath is:" + apkPath);
if (TextUtils.isEmpty(apkPath)) {
sendHtml();
return;
}
File file = new File(apkPath);
// File file = new File("/storage/emulated/0/my.html");
Log.i(TAG, "*************Http-response****************");
if (file != null && file.exists()) {
String line="HTTP/1.1 200 OK \r\n";
Log.i(TAG,"line="+line);
//sout.writeChars(line);
sout.write(line.getBytes());//用字节传输,不能用字符,浏览器无法解析
// String header="Content-Type: application/vnd.android.package-archive; charset=utf-8 \r\n"
String header="Content-Type: application/octet-stream; charset=utf-8 \r\n"
+ "Content-Disposition: attachment; filename=" + fileName+ "\r\n"
+ "Content-length: "+file.length()+" \r\n\r\n";
Log.i(TAG,"header="+header);
sout.writeBytes(header);
fis = new FileInputStream(file);
/**
byte[] bytes = new byte[BUFFER_SIZE];
int ch = fis.read(bytes, 0, BUFFER_SIZE);;
while (ch!=-1) { //ch==-1表示读到末尾了
sout.write(bytes, 0, ch); //写出到浏览器
Log.i(TAG, "ch is:" + ch);
ch = fis.read(bytes, 0, BUFFER_SIZE);//再读会接上一次读的位置往下读,如果读到末尾就会返回-1,终止输出
} **/
byte[] buff = new byte[1024];
int size;
while ((size = fis.read(buff)) != -1) {
Log.i(TAG, "size is:" + size);
sout.write(buff, 0, size);
}
sout.flush();
sout.close();
fis.close();
} else {
// file not found
String errorMessage = "HTTP/1.1 404 File Not Found \r\n"
+ "Content-Type: text/html \r\n"
+ "Content-Length: 23 \r\n"
+ "\r\n"
+ "<h1>File Not Found</h1>";
System.out.println(errorMessage);
output.write(errorMessage.getBytes());
}
} catch (Exception e) {
Log.i(TAG, "sendAPK apk has error");
e.printStackTrace();
} finally {
Log.i(TAG, "finally here.............");
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (sout != null) {
try {
sout.flush();
sout.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
这个APK的路径是当前APK在手机里面的路径,然后css和html和image图片放在assets目录下的share目录
******\app\src\main\assets\share\