Java中的单线程服务器

时间:2020-01-09 10:36:12  来源:igfitidea点击:

本文将展示如何在Java中实现单线程服务器。单线程服务器不是服务器的最佳设计,但是代码很好地说明了服务器的生命周期。多线程服务器上的以下文本将基于此代码模板。

这是一个简单的单线程服务器:

package servers;

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

public class SingleThreadedServer implements Runnable{

    protected int          serverPort   = 8080;
    protected ServerSocket serverSocket = null;
    protected boolean      isStopped    = false;
    protected Thread       runningThread= null;

    public SingleThreadedServer(int port){
        this.serverPort = port;
    }

    public void run(){
        synchronized(this){
            this.runningThread = Thread.currentThread();
        }
        openServerSocket();
        
        while(! isStopped()){
            Socket clientSocket = null;
            try {
                clientSocket = this.serverSocket.accept();
            } catch (IOException e) {
                if(isStopped()) {
                    System.out.println("Server Stopped.") ;
                    return;
                }
                throw new RuntimeException(
                    "Error accepting client connection", e);
            }
            try {
                processClientRequest(clientSocket);
            } catch (Exception e) {
                //log exception and go on to next request.
            }
        }
        
        System.out.println("Server Stopped.");
    }

    private void processClientRequest(Socket clientSocket)
    throws Exception {
        InputStream  input  = clientSocket.getInputStream();
        OutputStream output = clientSocket.getOutputStream();
        long time = System.currentTimeMillis();

        byte[] responseDocument = "<html><body>" +
                "Singlethreaded Server: " +
                time +
                "</body></html>".getBytes("UTF-8");

        byte[] responseHeader =
            "HTTP/1.1 200 OK\r\n" +
            "Content-Type: text/html; charset=UTF-8\r\n" +
            "Content-Length: " + responseDocument.length +
            "\r\n\r\n".getBytes("UTF-8");

        output.write(responseHeader);
        output.write(responseDocument);
        output.close();
        input.close();
        System.out.println("Request processed: " + time);
    }

    private synchronized boolean isStopped() {
        return this.isStopped;
    }

    public synchronized void stop(){
        this.isStopped = true;
        try {
            this.serverSocket.close();
        } catch (IOException e) {
            throw new RuntimeException("Error closing server", e);
        }
    }

    private void openServerSocket() {
        try {
            this.serverSocket = new ServerSocket(this.serverPort);
        } catch (IOException e) {
            throw new RuntimeException("Cannot open port 8080", e);
        }
    }
}

这是运行它的代码:

SingleThreadedServer server = new SingleThreadedServer(9000);
new Thread(server).start();

try {
    Thread.sleep(10 * 1000);
} catch (InterruptedException e) {
    e.printStackTrace();  
}
System.out.println("Stopping Server");
server.stop();

服务器运行时,我们可以使用普通的Web浏览器访问它。使用地址http:// localhost:9000 /

服务器循环

单线程服务器最有趣的部分是其主循环,在上面的代码中以粗体显示。循环在这里重复:

while(! isStopped()){
     Socket clientSocket = null;
     try {
         clientSocket = this.serverSocket.accept();
     } catch (IOException e) {
        if(isStopped()) {
            System.out.println("Server Stopped.") ;
            return;
        }
        throw new RuntimeException("Error accepting client connection", e);
     }
     try {
         processClientRequest(clientSocket);
     } catch (IOException e) {
         //log exception and go on to next request.
     }
 }

简而言之,服务器要做的是:

  • 等待客户要求
  • 处理客户要求
  • 从1开始重复。

对于大多数用Java实现的服务器,此循环几乎相同。单线程服务器与多线程服务器的区别在于,单线程服务器在接受客户端连接的同一线程中处理传入的请求。多线程服务器将连接传递给处理请求的工作线程。

在接受客户端连接的同一线程中处理传入请求不是一个好主意。服务器只能在serverSocket.accept()方法调用内时,客户端才能连接到服务器。侦听线程在" serverSocket.accept()"调用之外花费的时间越长,客户端被拒绝访问服务器的可能性就越高。这就是多线程服务器将传入的连接传递到工作线程的原因,工作线程将处理请求。这样,侦听线程在serverSocket.accept()调用之外花费的时间尽可能少。