花了3天时间搞了这么个服务器程序,基于Socket与Thread等JAVA基本东西.
基本知识.
HTTP协议,这个是浏览器与服务器交互的"暗语".一般常见的交互方式是Get/Post
下面是请求文件信息Request:
GET /index.html?uname=11111111111111111&pwd=22222222222222 HTTP/1.1
Host: localhost:8888
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.89 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8
POST /index.html HTTP/1.1
Host: localhost:8888
Connection: keep-alive
Content-Length: 41
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Origin: null
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.89 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8
uname=11111111111111111&pwd=2222222222222
get传输信息比较少,直接跟在文件名后,post 传输信息比较多,在文件头后面.每一行都有一个分隔符'\r\n'
服务器响应格式Response:
HTTP/1.1 200 OK
Content-Length 47
Content-Type text/html;charset=GBK
Date 31 Mar 2015 06:12:22 GMT
Server MyServer Server/0.0.1
接下来是一个CRLF分隔符,再就是文本信息
二.系统框架
简单来说就是服务器接受信息,看请求的是谁,则转给那个servlet处理.
servlet处理就是根据响应类型写好头信息,+CRLF+正文,发送.
为了服务多个,则引入多线程,为了提供不同的服务则要先将接收到的头信息做处理,看是哪种类型的然后分别处理.
下面是图示:
使用表单交互:
Server:
package MyServer;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.sql.Date;
/*
* 响应
*/
public class Server {
public final static int port = 8888;
public final static String CRLF = "\r\n";
private ServerSocket serversocket;
private boolean isRunning=true;
public static void main(String[] args) {
new Server().start();
}
public void start(){
start(8888);//没有参数则调用有参的,还有另一种就是有参的this()其他
}
public void start(int port){
try {
serversocket = new ServerSocket(port);
receive();//启动ServerSocket服务,并接收
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
stop();
}
}
private void receive() {
// TODO Auto-generated method stub
try {
while(true){
Socket client = serversocket.accept();
new Thread(new Dispatcher(client)).start();// Dispatcher启动多线程处理
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
stop();
}
}
private void stop(){
isRunning = false;
CloseUtil.closeServerSocket(serversocket);
}
}
package MyServer;
import java.net.Socket;
public class Dispatcher implements Runnable{
private Request req ;
private Response rep ;
private Servlet servlet;
private Socket socket;
private int code;//Request接收请求,Response发送响应.Servlet处理请求 code 200 正常 404 没有页面 500 服务器异常
public Dispatcher(Socket client){
this.socket = client;
req = new Request(client);
rep = new Response(client);
code = 200;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
servlet = new WebApp().getServlet(req.getUrl());
} catch (Exception e) {
// TODO Auto-generated catch block
//e.printStackTrace();
code = 500;
}
try {
servlet.doGet(req, rep, code);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Request;类:
package MyServer;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class Request {
private String method;//请求的方式
private String url;//放请求的URL
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
private Map<String,List<String>> parameterMapValue;//键值对 eg复选框 可能有多个
public final static int port = 8888;
public final static String CRLF = "\r\n";
private String requestInfo;
private BufferedInputStream bis;
private Socket socket;
public Request(){
method = "";
url = "";
parameterMapValue = new HashMap<String,List<String>>();
requestInfo = "";
}
public Request(Socket socket){
this();//
this.socket = socket;
try {
bis = new BufferedInputStream(socket.getInputStream());
byte [] data = new byte[20480];
int len = bis.read(data);
requestInfo = new String(data,0,len);//读入所有请求,然后分离URL.参数等(parseRequestInfo处理)
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
parseRequestInfo();
}
/*
* 请求参数
* 请求路径(get/post/other)
*
*/
private void parseRequestInfo(){
requestInfo = requestInfo.trim();
if(null == requestInfo || requestInfo.equals(""))
return;
String paramString = "";
String fitstLine = requestInfo.substring(0, requestInfo.indexOf(CRLF));//读入第一句话
String url = fitstLine.substring(fitstLine.indexOf("/"),fitstLine.indexOf("HTTP/")).trim();//按格式划分
this.method = fitstLine.substring(0, fitstLine.indexOf("/")).trim();
if(this.method.equalsIgnoreCase("post")){
paramString = requestInfo.substring(requestInfo.indexOf(CRLF));
this.url = url;
}else if(this.method.equalsIgnoreCase("get")){
//是否存在参数
if(requestInfo.contains("?")){
String [] urlArrary = url.split("\\?");//划分参数与网址
this.url = urlArrary[0];
paramString = urlArrary[1];
}else{
this.url = url;
}
}
if(paramString.equals(""))
return;
parseParms(paramString);//接收参数
}
private void parseParms(String paramString){
String [] param = paramString.split("&");
for(String t:param){
String key = t.substring(0,t.indexOf("=")).trim();
String value = decode(t.substring(t.indexOf("=")+1).trim(),"gbk");//URL编码问题,解码放入
if(!parameterMapValue.containsKey(key))
parameterMapValue.put(key, new LinkedList<String>());
List<String> temp = parameterMapValue.get(key);
temp.add(value);//如果这里为空放入
//System.out.println(value);
}
}
public String[] getParmeterValues(String name){//Servlet应用获取参数
List<String> value = parameterMapValue.get(name);
if(value == null)
return null;
return value.toArray(new String [0]);
}
public String getParmeter(String name){
String [] value = getParmeterValues(name);
if(null == value)
return null;
return value[0];
}
private static String decode(String value,String code){
try {
return java.net.URLDecoder.decode(value, code);//
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
}
return null;
}
// public static void main(String[] args) {
// String str = " uname=11111111111111111&pwd=&bike=on&bike=on ";
// new Request().parseParms(str);
// }
}
Response类:
package MyServer;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.sql.Date;
public class Response {
public final static int port = 8888;
public final static String CRLF = "\r\n";
private StringBuilder headInfo;
private StringBuilder content;
private Socket socket;
private PrintWriter pw;
private int len;
public Response(){
headInfo = new StringBuilder();
content = new StringBuilder();
len = 0;
}
public Response(Socket socket){
this();
try {
pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public Response print(String str){
content.append(str);
len += str.getBytes().length;
return this;
}
private void createHeadInfo(int code){
headInfo = new StringBuilder();
headInfo.append("HTTP/1.1 "+code+" ");
switch(code){
case 200:headInfo.append("OK");
break;
case 404:headInfo.append("NOT FOUND");
break;
case 500:headInfo.append("SERVER ERROR");
break;
}
headInfo.append(CRLF);
headInfo.append("Server:MyServer Server/0.0.1"+CRLF);
headInfo.append("Date:"+(new Date(System.currentTimeMillis()).toGMTString())+CRLF);
headInfo.append("Content-Type:text/html;charset=GBK"+CRLF);
headInfo.append("Content-Length:"+len+CRLF);
headInfo.append(CRLF);//这个特别注意
}
public void pustToClient(int code){
createHeadInfo(code);
pw.print(headInfo.toString()+content.toString());
pw.flush();
}
public void close(){
CloseUtil.closeAll(pw);
}
}
Servlet类:
package MyServer;
public abstract class Servlet {
public void service(Request req,Response rep,int code) throws Exception{
this.doGet(req,rep,code);
}
public abstract void doGet(Request req,Response rep,int code) throws Exception;
public abstract void doPost(Request req,Response rep,int code) throws Exception;
}
LoginServlet类:
package MyServer;
public class LoginServlet extends Servlet{
@Override
public void doGet(Request req, Response rep, int code) throws Exception {
// TODO Auto-generated method stub
rep.print("<html><body>welcome ");
rep.print(req.getParmeter("uname")+" back<br>");
rep.print("</body></html>");
rep.pustToClient(code);
}
@Override
public void doPost(Request req, Response rep, int code) throws Exception {
// TODO Auto-generated method stub
}
}
CloseUtil类:
package MyServer;
import java.io.Closeable;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class CloseUtil implements Closeable{
@Override
public void close() throws IOException {
// TODO Auto-generated method stub
}
public static void closeAll(Closeable...io){
for(Closeable t:io){
if(null!=t)
try {
t.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void closeSocket(Socket socket){
if(socket == null)
return;
try {
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void closeServerSocket(ServerSocket serversocket){
if(serversocket == null)
return;
try {
serversocket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
然后就是用XML文件去配置Servlet与健壮性的问题了.
学习到的一些特性:
1.可变参,在CloseUtil中使用,要放在最后一位.继承Closeable各种流随便关.
2.反射技术在JAVA执行过程中动态生成类
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Scanner;
public class Main {
public static void main(String[] args) throws Exception {
Main x = new Child();
Child c =(Child)x;
System.out.println(x==c);
String str = "aaa";
Constructor con = str.getClass().getDeclaredConstructor(new Class[] {String.class});
String test1;
test1 = (String)con.newInstance(str);
String test2;
Class<?> clz;
clz = str.getClass();
test2 = (String)clz.newInstance();
String test3;
clz = String.class;
test3 = (String)clz.newInstance();
String test4;
clz = Class.forName("java.lang.String");
test4 = (String)clz.newInstance();
System.out.println(test1);
System.out.println(test2 instanceof String);
System.out.println(test3 instanceof String);
System.out.println(test4 instanceof String);
}
}
/*
* Result:
* true
* aaa
* true
* true
* true
*/
class Child extends Main {
}
3.画图真方便:点击打开链接
4.list.toArray()
不带参数的方法,构造并返回一个Object数组对象,这时候向下转型为String数组对象,导致类型不兼容,报错。(本身就是Object,而不是String转到Object)
而带参数的方法,构造的数组对象类型和参数的类型一致,故不存在转型.
String[] array = new String[] {"A", "B"}; List list = Arrays.asList(array);
List list = new ArrayList(); list.add("A");list.add("B");final int size = list.size();
String[] array = (String[])list.toArray(new String[size]);
5. Servlet的实现
双Map即 A->B B->C 中间这个B满足了多对一,工厂模式