需要掌握:HTTP协议
提出问题
socket的接收请求是 阻塞 的,需要处理完一个请求才能处理下一个请求,所以在面对高并发的服务请求时,性能就会很差。
如下图,当 client 1、client 2 发来请求时,Server 先处理 client 1,处理完成后,再处理 client 2。
理解:相当于食堂只开一个打饭窗口,当学生多时,就需要排队,前一个打饭没结束,后面人都要 一直等待
分析
使用多线程,接收到一个请求,就创建一个线程处理,这样就 不会阻塞 了,可以提升性能
多线程的缺点
当 请求很多 的时候,就会 创建大量的线程,维护线程需要资源的消耗(JAVA中每个线程都需要占用内存),线程之间的切换也需要消耗性能。而且系统创建线程的数量也是有限的,所以当高并发时,会直接把系统拖垮。
实现1
使用 socket、多线程是线程
提示: 需要用多线程实现,否则使用浏览器接收不到内容。
public class WebServer {
public static int PORT=8080;
public static void main(String[] args) {
try {
ServerSocket ss=new ServerSocket(PORT);
System.out.println("启动成功,端口:"+PORT);
while(true) {
Socket s=ss.accept();
new Thread(new Handle(s)).start();
}
// ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
处理操作类
需要在当前工程根目录下创建 WebRoot 文件夹,然后在该文件夹下创建 .html 网页、图片等
package socket;
import java.io.*;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class Handle implements Runnable{
private Socket s;
private static final int BUFF_SIZE=1024*8;
private static final String WEB_ROOT="WebRoot";
private static final String INDEX_HTML="index.html";
private static final List<String> HTML_SUFFIX=new ArrayList<>();
private static final List<String> IMG_SUFFIX=new ArrayList<>();
public Handle(Socket s) {
this.s = s;
HTML_SUFFIX.add("html");
HTML_SUFFIX.add("htm");
IMG_SUFFIX.add("png");
IMG_SUFFIX.add("jpg");
IMG_SUFFIX.add("jpeg");
IMG_SUFFIX.add("gif");
}
@Override
public void run() {
System.out.println(String.format("客户端:%s:%d 连接到服务器", s.getInetAddress().getHostAddress(),s.getPort()));
List<String> heads=new ArrayList<String>();
try {
InputStream is = s.getInputStream();
OutputStream os=s.getOutputStream();
//接收数据
BufferedReader br=new BufferedReader(new InputStreamReader(is));
String line=br.readLine();
while(line!=null){
//使用BufferedReader,最后2个\r\n解析成一个空字符串
if("".equals(line)){
break;
}
heads.add(line);
line=br.readLine();
}
System.out.println("请求头:");
for(String str:heads){
System.out.println(str);
}
String fileName=heads.get(0).split(" ")[1];
if("/".equals(fileName)){
fileName=INDEX_HTML;
}
sendFile(os,fileName);
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private void sendFile(OutputStream os,String fileName) throws IOException {
byte[] buff=new byte[BUFF_SIZE];
os.write("HTTP/1.1 200 OK\r\n".getBytes());
String suffix=fileName.substring(fileName.lastIndexOf(".")+1,fileName.length()).toLowerCase();
if(HTML_SUFFIX.contains(suffix)){
os.write("Content-Type: text/html;charset=utf-8\r\n".getBytes());
}else if(IMG_SUFFIX.contains(suffix)){
os.write(String.format("content-type: image/%s\r\n",suffix).getBytes());
}else{
os.write("Content-Type: text/html;charset=utf-8\r\n".getBytes());
}
os.write("Server: houlong server/1.1\r\n".getBytes());
os.write("\r\n".getBytes());
File file = new File(WEB_ROOT, fileName);
FileInputStream fis = new FileInputStream(file);
int len = fis.read(buff);
while (len != -1) {
os.write(buff, 0, len);
len = fis.read(buff);
}
os.write("\r\n\r\n".getBytes());
os.flush();
}
}
测试
需要在当前工程根目录下创建 WebRoot 文件夹,然后在该文件夹下创建 index.html 文件,内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
hello world!
</body>
</html>
打开浏览器,访问:http://localhost:8080/ ,就能访问到网页
实现2
使用 ThreadPoolExecutor 线程池
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class WebServer2 {
public static int PORT=8080;
public static void main(String[] args) {
ThreadPoolExecutor pool = new ThreadPoolExecutor(100, 100,
60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(200));
try {
ServerSocket ss=new ServerSocket(PORT);
System.out.println("启动成功,端口:"+PORT);
while(true) {
Socket s=ss.accept();
pool.execute(new Handle(s));
}
// ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
处理操作类
同上


