WEB服务器工作机制由浅至深(1):多线程模拟服

系统 1515 0
 

用Socket和ServerSocket这两个类模拟监听80端口请求的WEB服务器
其实很简单的,我搞了一晚上才搞定。。。。。。

说说为神马吧,主要是InputStream的阻塞机制!
刚开始,我一次性读取1024字节,成功了。然后我又想到如果请求的字节数很长的话,只读取一次肯定不行, 于是就用循环的方法使用read方法读取Request输入流的数据。
然后,悲剧的事情就发生了!!! read方法竟然阻塞了,然后找资料找了好久找不到有用的资料,
只看见有人说用java.nio包里面的新类可以实现。 我就想啊,我是学习的,面对问题怎么能逃避了???  
于是继续研究其中的算法和InputStream提供的各种方法。(我想其中不合理的代码就不必贴了,认真看我这篇文章的人应该都能想到的)
最先啊,是看到java.nio.channel包里面有将InputStream转换为可读通道的方法,我就试了试,转换之后,发现、、、无论算法怎么处理、读取着还是不行啊。 仍然阻塞
然后,我想到一种方法:
     就是定义字节数组一定长度LEN, 然后循环读取, 如果某一次读取到的数据(read(buf)的返回值)小于100的话,不是意味着输入流读取到了结尾了么? 然后我就高高兴兴的实现这种想法了     。 再然后呢。。。我就发现如果InputStream里面的字节数如果是LEN的整倍数的话,最后一次读取也是 ==LEN的,还是会出现 read方法已经到了结尾但是循环仍然执行导致的阻塞现象

又一次失败了
我就继续想啊想,就想到了InputStream提供的available()方法,这个方法返回的是下一次读取的估计字节数,当初我就看到了这个方法,但是看到文档上说的这个方法不建议使用,就一直没想用。。。真是到了无可奈何啊。只好又试试这个方法的效果
于是呢,我就用了这种方法:
      在每一次循环读取之前判断InputStream剩余的字节数是不是 0 、即剩余字节是不是空的,如果是空的就不读了,这种方法果然奏效!!! 但是经过我的反复测试啊,发现又出现了一个问题,同一个浏览器的同一个页面如果刷新的话,这个页面第一次请求时候available() 正常, 下来的请求就不正常了,available()直接就 == 0 了, 第一次页面请求 之后 的每一次页面刷新后台都读取不到请求数据。 Socket我都关闭了啊!怎么会出现这种情况??? 纳闷了, 又找了好长时间的资料,仍然没有解决办法。好像是上一次处理的InputStream的available()保留下来了一样,但是如果另开一个页面请求就正常。。。。
   
然后就是我想到的终极解决办法了, 我假设的是 用户请求内容不可能是空值 ,于是,我就用了do{}while循环,先执行读取一次,在判断available()的值是不是0,这样就保证了上一个问题的影响。 至于为什么这么做,我也是说不明白的,就是当时的一种灵感吧。。。我感觉是为了把InputStream读取指针先放在流头。这样可以避免干扰。

      额语文水平很低啊、看的同学们勉强凑合吧
      小弟现在大二,经验有限,高手老鸟轻喷啊
第一次发不会贴代码、试试吧!

主服务类:
    
package server.test01;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @author sulin
 */
public class MyService {

	private final String HOST = "127.0.0.1";	//绑定的主机127.0.0.1,不设置也可、默认即为IP或localhost
	private final int BACKLOG = 1;				//最大请求并发量
	private final int PORT = 80;				//服务器绑定端口
	
	public void startService(){
		//建立指定HOST和PORT的服务器
		ServerSocket server = null;
		try {
			server = new ServerSocket(this.PORT, this.BACKLOG, InetAddress.getByName(this.HOST));
		} catch (IOException e) {
			e.printStackTrace();
		}
		//服务开始工作
		System.out.println("服务器开始工作");
		while(true){
			try{
				//侦测一次请求
				final Socket socket = server.accept();
				final InputStream in = socket.getInputStream();
				final OutputStream out = socket.getOutputStream();
				//多线程机制响应请求
				new Thread(){
					public void run() {
						try {
							//处理响应侦探到的请求
							MyRequest request = new MyRequest(in);
							request.Parse();
							MyResponse response = new MyResponse(out);
							response.setRequest(request);
							response.SendResponse();
							//本次请求处理结束
							socket.close();
						} catch (IOException e) {
							e.printStackTrace();
						}
					}
				}.start();
			}catch(Exception e){
				e.printStackTrace();
			}
		}
		
	}
	
	/**
	 * 程序入口
	 * @param args
	 */
	public static void main(String[] args) {
		MyService service = new MyService();
		service.startService();
	}
	
}


  

请求处理类
    
package server.test01;

import java.io.IOException;
import java.io.InputStream;

public class MyRequest {
	
	private InputStream in;				//请求的输入流
	private String uri;					//请求的uri
	private String request = "";		//请求处理后的字符串
	
	public MyRequest( InputStream in ) {
		this.in = in;
	}
	public String getUri() {
		return uri;
	}
	
	/**
	 * 提供对request请求的解析服务
	 */
	public void Parse(){
		try {
			byte[] buffer = new byte[377];
			int len;
			do{
				len = in.read(buffer);
				request += new String(buffer).substring(0, len);
			}while(in.available()>0);
		} catch (IOException e) {
			e.printStackTrace();
		}		
		this.uri = this.ParseURI();
		System.out.println(uri);
	}
	
	private String ParseURI(){
		String[] ss = this.request.trim().split(" ");
		if(ss.length > 2){
			return ss[1];
		}else{
			return "";
		}
	}
	
}

  

另外一个相应类MyResponse就不贴了。。。

WEB服务器工作机制由浅至深(1):多线程模拟服务器并防止阻塞


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论