`
s929498110
  • 浏览: 106933 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

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

阅读更多
 

用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就不贴了。。。
分享到:
评论
14 楼 acf 2011-06-03  
avgguy,说不建议用available()是因为数据是动态的,后续还有数据,用available得不到正确的值。
其实这种想法是不正的,因为用户程序不会直接跟网络打交道的,如果用户级程序自己能搞到网络流,那这个写程序的是不是要把TCP/IP也实现了!
13 楼 acf 2011-06-03  
avgguy 写道
1. Socket/ServerSocket 本来就是阻塞的,java.nio 才是非阻塞的。你非要将 A 搞成 B 的话,总有点差强人意。
2. 在网络流操作的时候,是不建议使用 available() 的。

为什么不用available(),你以为网络真的是在网线上实时的动态数据流吗!
应该不是,如果大量的数据流,还是会临时的存在硬盘上,然后才开启一个流给用户级程序使用,这些应该是操作系统暗地里做的。
12 楼 s929498110 2011-04-28  
uin57 写道
真的不知道改投什么贴好呢?新手?良好?
你的分享精神不错,但是哪些错误都是没有自己研读API惹的祸。。
投什么帖,LZ自己说吧。。

 
确实不够深入、但是java.io那一块对于Socket I/O阻塞机制确实不好办的
java.nio又没深入学习过。还算是不太了解吧
11 楼 uin57 2011-04-20  
真的不知道改投什么贴好呢?新手?良好?
你的分享精神不错,但是哪些错误都是没有自己研读API惹的祸。。
投什么帖,LZ自己说吧。。
10 楼 s929498110 2011-03-29  
还有  为什么不能用 available()啊?
我见有的资料书上讲服务器用这个方法比我用的猛多了。。。
我是无可奈何了只好稍微用了一下
9 楼 s929498110 2011-03-29  
avgguy 写道
1. Socket/ServerSocket 本来就是阻塞的,java.nio 才是非阻塞的。你非要将 A 搞成 B 的话,总有点差强人意。
2. 在网络流操作的时候,是不建议使用 available() 的。


但是如果不用nio里面的ServerSocketChannel的话,仍然有阻塞现象啊、 我试了好几次。。。

如果非得用ServerSocketChannel的话    还是费点事把ServerSocket实现了吧、学习呢不能偷懒
8 楼 s929498110 2011-03-29  
sky_dream 写道
大二,你把学生证贴下?


 

真的啊、 河南大学大二的、去年暑假加入了学院的网络工程实验室学习j2ee
7 楼 jackra 2011-03-25  
fnasty 写道
现在的大学生这么勤奋啊,我大二的时候都没写过代码,只会玩游戏!

也很勤奋嘛,我大二的时候只会上网看个H图片.....那时候真好啊,没有great wall.
6 楼 li463968565 2011-03-25  
好像不能运行~~~
5 楼 sky_dream 2011-03-25  
大二,你把学生证贴下?
4 楼 tterry 2011-03-25  
avgguy 写道
1. Socket/ServerSocket 本来就是阻塞的,java.nio 才是非阻塞的。你非要将 A 搞成 B 的话,总有点差强人意。
2. 在网络流操作的时候,是不建议使用 available() 的。

为神马不建议使用available

3 楼 avgguy 2011-03-24  
1. Socket/ServerSocket 本来就是阻塞的,java.nio 才是非阻塞的。你非要将 A 搞成 B 的话,总有点差强人意。
2. 在网络流操作的时候,是不建议使用 available() 的。
2 楼 fnasty 2011-03-24  
现在的大学生这么勤奋啊,我大二的时候都没写过代码,只会玩游戏!
1 楼 s929498110 2011-03-24  
没人响应啊。 可能都老鸟,我这个帖子才菜鸟了吧

相关推荐

    多线程技术教学视频

    - 在Web服务器中,多线程可以用来处理并发请求,每个请求由单独的线程处理,提高服务器的响应速度。 #### 2. 数据分析 - 对于大规模的数据集,可以使用多线程进行并行处理,显著加快计算速度。 #### 3. 游戏开发 -...

    (牛客网C++课程)Linux 高并发Web服务器项目实战(带定时检测代码)

    3. 采用模拟 Proacto r的事件处理模式,利用线程池实现多线程机制,实现高并发通信,减少频繁创建和销毁线程带来的开销;(信号和互斥锁) 4. 主进程负责事件的读写,子线程负责业务逻辑——用有限状态机解析HTTP...

    linux下c实现单线程web服务器

    在Linux环境下,C语言实现一个简单的单线程Web服务器是一项挑战性的任务,它涉及到网络编程、多路复用I/O、HTTP协议...实际生产环境中,通常会采用多线程或多进程模型,或者使用异步I/O技术如epoll来提高服务器性能。

    .net模拟IIS服务器

    7. **多线程和异步处理**:模拟服务器可能需要处理并发请求,因此了解如何在.NET中使用线程和异步编程(如async/await)是很重要的。这将确保服务器能够有效地处理多个客户端请求而不阻塞。 8. **安全性和身份验证*...

    LINUX单线程网络服务器设计.pdf

    【Linux 单线程网络服务器设计】 Linux 单线程网络服务器是一种在Linux操作系统上实现...它特别适用于高并发、低延迟的网络服务,例如Web服务器和代理服务器,其设计思想和关键技术对于系统开发者有着重要的参考价值。

    unix多线程/多进程编程

    在Unix系统中,多线程常用于服务器编程,如Web服务器通过创建多个线程处理并发请求。多进程则常见于大型软件,如数据库系统,每个进程负责一部分工作,通过进程通信协调。 总结,Unix的多线程和多进程编程提供了...

    multithread-tcpip:多线程tcpip服务器

    多线程TCP/IP服务器是一种高效的设计模式,它允许多个客户端同时连接并处理请求,从而提高了服务的并发能力。本项目“multithread-tcpip”正是这样一个实现,用C语言编写,专门用于处理HTTP协议中的GET、POST、...

    多线程的一个例子

    本文将深入探讨多线程的概念、应用及其在实际开发中的运用,结合“2ccc.com.txt”和“Elavotor”这两个文件名,我们可以推断可能涉及的是一个具体的多线程实现示例,比如Web服务器或者电梯控制系统的模拟。...

    c# 多线程网页信息抓取

    5. **异步编程**:为了防止多线程阻塞主线程,C#引入了异步编程模型,使用`async`和`await`关键字。这使得程序可以继续执行其他任务,而无需等待IO操作(如网络请求)完成,提高了程序的响应性。 6. **线程安全**:...

    C# post模拟提交接收图片存到服务器上

    在C#编程中,模拟POST提交通常用于模拟用户在网页上的表单提交行为,例如上传文件,特别是图片。这个过程涉及到HTTP协议的理解、文件流处理以及服务器端的存储逻辑。以下将详细介绍如何实现这个功能。 首先,理解...

    Python语言实现http服务器

    2. **多线程**: 在这个实现中,服务器采用了多线程来处理并发请求。Python的`threading`模块提供了创建和管理线程的功能。当一个HTTP请求到达时,服务器会创建一个新的线程来处理该请求,这样可以避免单线程阻塞导致...

    linux下TCP聊天程序(多线程)

    ### 基于TCP协议的聊天程序(多线程)——深入解析 #### 1. 背景 ##### 1.1 开发背景 随着Linux操作系统在全球范围内的广泛应用,越来越多的人开始关注这一开源平台所带来的可能性。对于刚接触Linux的新用户而言...

    http1.0 的ftp模拟java实现(含多线程)

    本项目涉及的是在Java环境下,模拟实现HTTP 1.0版本的FTP功能,并结合多线程技术来提高效率。以下是这个项目的核心知识点: 1. **HTTP 1.0**: HTTP(超文本传输协议)是互联网上应用最广泛的一种网络协议,用于从...

    模拟tomcat的工作原理

    在模拟Tomcat的过程中,了解并实现这些步骤可以帮助我们更好地理解Web服务器的工作流程,特别是Java的多线程模型在其中的应用。同时,标签"java tomcat"提示我们需要关注的是与Java和Tomcat相关的技术,例如Servlet...

    用点菜和吃菜来做模拟(生产者和消费者的多线程关系)

    例如,一个Web服务器可能会有多个生产者线程负责接收和解析客户端请求,而消费者线程则处理这些请求并返回响应。在这种情况下,队列充当了请求缓冲区的角色,保持生产者和消费者的平衡,避免因处理速度不匹配而导致...

    实时接收发送消息(接收消息线程阻塞,发送消息线程唤醒)

    - 文件名可能暗示了一个性能测试,旨在确定Web服务器处理请求的最大阻塞时间。 - 这种测试通常涉及模拟大量并发用户,观察服务器在不同负载下对新消息的处理能力,以及是否能及时唤醒接收线程。 7. **优化策略**...

    php多线程并发实现方法

    在了解PHP多线程并发实现方法之前,首先需要明白PHP语言本身并不是设计为支持传统意义上的多线程并发模型。PHP是一种主要用于Web开发的服务器端脚本语言,它通常以单线程的方式运行。每次有请求进入,PHP都会启动一...

    WebServer:基于模拟Proactor的C ++轻量级web服务器

    "WebServer:基于模拟Proactor的C ++轻量级web服务器" 这个标题揭示了我们讨论的核心内容,即一个使用C++编程语言开发的轻量级Web服务器。它采用了Proactor模式作为其核心设计原则,这是一种异步事件处理模式,特别...

    java 远程控制客户端和服务器端代码详细

    - 使用多线程处理客户端连接,避免单线程阻塞。 - 优化图像传输,使用高效的压缩算法,并考虑使用帧率控制,避免过于频繁的屏幕更新。 - 对于大流量的数据传输,可以考虑使用TCP的滑动窗口协议,自动调整发送速率...

Global site tag (gtag.js) - Google Analytics