最近工作经常会遇到要做一个很小的微服务,对并发要求不高,尽量小,要提供
api,还要查数据库。想想使用Spingboot,tomcat,Mybatis能妥妥的解决,可是还是太
大了,业界还有很多例如Weblogic,jetty,Lighttpd等等,都是企业级的应用。思来想
去,估计需要自己做一个实现http协议的web服务器了;
思路大概如下:
1.new一个ServerSocket,监听某个端口
2.socket等待新的请求接入
3.当有新的请求接入,直接new一个任务提交给线程池处理
4.SocketHandler主要处理请求,获取请求参数,并根据对应的请求规则路由到对应的处理
1.主程序
package com.example.demo.server.serverSocket;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TCPServer {
public ExecutorService executorService= Executors.newFixedThreadPool(200);
public void start() {
try {
ServerSocket serverSocket= new ServerSocket(8090);
System.out.println("ServerSocket监听端口:"+serverSocket.getLocalPort());
while (true){
Socket socket= serverSocket.accept();
System.out.println("request客户的地址为:"+socket.getInetAddress()+":"+socket.getPort());
try {
executorService.execute(new SocketHandler(socket));
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.处理类
package com.example.demo.server.serverSocket;
import com.alibaba.fastjson.JSON;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
public class SocketHandler implements Runnable {
private Socket socket;
public SocketHandler(Socket socket){
this.socket= socket;
}
@Override
public void run() {
try {
socketHandler(socket);
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void socketHandler(Socket socket) throws IOException, InterruptedException {
//1.获取请求
InputStream inputStream = socket.getInputStream();
Thread.sleep(10);
int len= inputStream.available();
byte[] bytes= new byte[len];
inputStream.read(bytes);
String request= new String(bytes);
System.out.println(request);
//2.处理请求并回掉
callBack(request,socket.getOutputStream());
}
private Map requestParameters(String request){
Map map= new HashMap<>();
String path= null;
try {
String[] lines= request.split("\r\n");
for(int i=0;i-1){
Map body= null;
try{
body= JSON.parseObject(trim(line),HashMap.class);
}catch (Exception e){
e.printStackTrace();
}
map.put("reqBody", body);
}else {
String[] parts= line.split(":");
if(parts.length>1){
map.put(trim(parts[0]),trim(parts[1]));
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return map;
}
private String trim(String s){
if(s!=null){
return s.trim().replaceAll("\r\n","");
}
return "";
}
private void callBack(String request,OutputStream outputStream){
String firstLine= request.substring(0,request.indexOf("\r\n"));
String[] parts= firstLine.split(" ");
String path= parts[1];
//1.请求数据,规定必须以.json为结尾,也可以自定义格式,这里只是举例
if(path.indexOf(".json")>-1){
toData(path,outputStream,request);
}else{//2.请求页面
toView(path,outputStream);
}
}
//1.请求数据
private void toData(String path,OutputStream outputStream,String request){
//组装请求参数
Map params= requestParameters(request);
System.out.println(JSON.toJSON(params));
PathRouter.router(path,outputStream);
}
//2.请求页面或者静态文件
private void toView(String path,OutputStream outputStream){
InputStream in= TCPServer.class.getResourceAsStream(path);
String contentType= getContentType(path);
String responseFirstLine= "HTTP/1.1 200 OK\r\n";
String responseHeader="Content-type:"+contentType+"\r\n\r\n";
if(in==null){
responseFirstLine= "HTTP/1.1 404 OK\r\n";
}
try {
outputStream.write(responseFirstLine.getBytes());
outputStream.write(responseHeader.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
try {
int length=0;
byte[] bytes= new byte[128];
while ((length=in.read(bytes))!=-1){
outputStream.write(bytes,0,length);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private String getContentType(String uri) {
if(uri.indexOf("html")!=-1||uri.indexOf("htm")!=-1)
return ContentType.html;
else if(uri.indexOf("jpg")!=-1||uri.indexOf("jpeg")!=-1)
return ContentType.jpg;
else if(uri.indexOf("gif")!=-1)
return ContentType.gif;
else if(uri.indexOf("png")!=-1){
return ContentType.png;
}
return ContentType.octetStream;
}
}
3.路由Router
package com.example.demo.server.serverSocket;
import com.alibaba.fastjson.JSON;
import com.sun.xml.internal.bind.v2.TODO;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 类似于dispatcher功能实现路径转发
* 不过在此过于简单
*/
public class PathRouter {
//用户初始化请求接口
public static List paths= new ArrayList<>();
static {
paths.add("/test.json");
}
public static void router(String path,OutputStream os) {
if(!pathCheck(path)){
p404(os);
return;
}else {
p200(os);
}
if("/test.json".equals(path)){
Map map= new HashMap();
map.put("you",1);
map.put("are",2);
try {
os.write(JSON.toJSONString(map).getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}else if("...".equals(path)){
//todo
}else {
//todo
}
}
private static void p200(OutputStream os){
String responseFirstLine= "HTTP/1.1 200 OK\r\n";
String responseHeader="Content-type:"+ContentType.json+"\r\n\r\n";
try {
os.write(responseFirstLine.getBytes());
os.write(responseHeader.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
private static void p404(OutputStream os){
String responseFirstLine= "HTTP/1.1 404 OK\r\n";
String responseHeader="Content-type:"+ContentType.json+"\r\n\r\n";
try {
os.write(responseFirstLine.getBytes());
os.write(responseHeader.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
private static boolean pathCheck(String path){
for (String p:paths){
if(p.equals(path)){
return true;
}
}
return false;
}
}
4.常量类
package com.example.demo.server.serverSocket;
public class ContentType {
public static final String html= "text/html";
public static final String jpg= "image/jpeg";
public static final String gif= "image/gif";
public static final String png= "image/png";
public static final String octetStream= "application/octet-stream";
public static final String json= "application/json;charset=UTF-8";
}